Skip to content

Commit

Permalink
Add more docs (#239)
Browse files Browse the repository at this point in the history
* Add more docs

* Fix cargo fmt
  • Loading branch information
sorpaas authored Nov 25, 2023
1 parent edbf2f8 commit 96bde6e
Show file tree
Hide file tree
Showing 19 changed files with 247 additions and 29 deletions.
5 changes: 5 additions & 0 deletions interpreter/src/call_create.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Call and create trap handler.
use crate::utils::{h256_to_u256, u256_to_usize};
use crate::{
Context, ExitError, ExitException, ExitResult, Machine, Memory, Opcode, RuntimeBackend,
Expand Down Expand Up @@ -72,8 +74,11 @@ pub enum CallScheme {
StaticCall,
}

/// Combined call create trap data.
pub enum CallCreateTrapData {
/// A call trap data.
Call(CallTrapData),
/// A create trap data.
Create(CreateTrapData),
}

Expand Down
1 change: 1 addition & 0 deletions interpreter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ impl<E, T> Capture<E, T> {
}
}

/// Exit result.
pub type ExitResult = Result<ExitSucceed, ExitError>;

/// Exit reason.
Expand Down
5 changes: 5 additions & 0 deletions interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl<S> Machine<S> {
self.position
}

/// Machine code.
pub fn code(&self) -> &[u8] {
&self.code
}
Expand All @@ -84,6 +85,8 @@ impl<S> Machine<S> {
}
}

/// Perform any operation. If the operation fails, then set the machine
/// status to already exited.
pub fn perform<R, F: FnOnce(&mut Self) -> Result<R, ExitError>>(
&mut self,
f: F,
Expand Down Expand Up @@ -198,10 +201,12 @@ impl<S> Machine<S> {
self.code.get(self.position).map(|opcode| Opcode(*opcode))
}

/// Whether the machine has empty code.
pub fn is_empty(&self) -> bool {
self.code.is_empty()
}

/// Advance the PC to the next opcode.
pub fn advance(&mut self) {
if self.position == self.code.len() {
return;
Expand Down
5 changes: 5 additions & 0 deletions interpreter/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ macro_rules! impl_perform_popn_pushn {
($($peek_push:expr),*),
$pop_pushn_f:ident
) => {
/// Pop $pop_len values from the stack, and then push $push_len values
/// into the stack.
///
/// If `f` returns error, then the stack will not be changed.
#[allow(unused_parens)]
pub fn $name<R, F>(&mut self, f: F) -> Result<R, ExitError> where
F: FnOnce(
Expand Down Expand Up @@ -98,6 +102,7 @@ impl Stack {
Ok(())
}

/// Check whether it's possible to pop and push enough items in the stack.
pub fn check_pop_push(&self, pop: usize, push: usize) -> Result<(), ExitException> {
if self.data.len() < pop {
return Err(ExitException::StackUnderflow);
Expand Down
7 changes: 7 additions & 0 deletions interpreter/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
//! Small utilities.
use crate::{ExitError, ExitFatal};
use core::cmp::Ordering;
use core::ops::{Div, Rem};
use primitive_types::{H256, U256};

/// Convert [U256] into [H256].
pub fn u256_to_h256(v: U256) -> H256 {
let mut r = H256::default();
v.to_big_endian(&mut r[..]);
r
}

/// Convert [H256] to [U256].
pub fn h256_to_u256(v: H256) -> U256 {
U256::from_big_endian(&v[..])
}

/// Convert [U256] to [usize].
pub fn u256_to_usize(v: U256) -> Result<usize, ExitError> {
if v > U256::from(usize::MAX) {
return Err(ExitFatal::NotSupported.into());
}
Ok(v.as_usize())
}

/// Sign of [I256].
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Sign {
Plus,
Expand All @@ -34,6 +40,7 @@ const SIGN_BIT_MASK: U256 = U256([
0x7fffffffffffffff,
]);

/// Signed 256-bit integer.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct I256(pub Sign, pub U256);

Expand Down
2 changes: 1 addition & 1 deletion jsontests/src/hash.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use evm::backend::in_memory::InMemoryBackend;
use crate::in_memory::InMemoryBackend;
use evm::utils::h256_to_u256;
use primitive_types::{H256, U256};
use sha3::{Digest, Keccak256};
Expand Down
8 changes: 5 additions & 3 deletions src/backend/in_memory.rs → jsontests/src/in_memory.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::{
use evm::{
ExitError, ExitException, Log, MergeStrategy, RuntimeBackend, RuntimeBaseBackend,
RuntimeEnvironment, TransactionalBackend,
};
use alloc::collections::{BTreeMap, BTreeSet};
use primitive_types::{H160, H256, U256};
use std::mem;
use std::{
collections::{BTreeMap, BTreeSet},
mem,
};

#[derive(Clone, Debug)]
pub struct InMemoryEnvironment {
Expand Down
1 change: 1 addition & 0 deletions jsontests/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod error;
mod hash;
mod in_memory;
mod run;
mod types;

Expand Down
4 changes: 1 addition & 3 deletions jsontests/src/run.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::error::{Error, TestError};
use crate::in_memory::{InMemoryAccount, InMemoryBackend, InMemoryEnvironment, InMemoryLayer};
use crate::types::*;
use evm::backend::in_memory::{
InMemoryAccount, InMemoryBackend, InMemoryEnvironment, InMemoryLayer,
};
use evm::standard::{Config, Etable, EtableResolver, Gasometer, Invoker, TransactArgs};
use evm::utils::u256_to_h256;
use evm::Capture;
Expand Down
22 changes: 21 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
pub mod in_memory;
//! # Backend-related traits and implementations
//!
//! A backend exposes external information that is available to an EVM
//! interpreter. This includes block information such as the current coinbase,
//! block gas limit, etc, as well as the state such as account balance, storage
//! and code.
//!
//! Backends have layers, representing information that may be committed or
//! discard after the current call stack finishes. Due to the vast differences of
//! how different backends behave (for example, in some backends like wasm,
//! pushing/poping layers are dealt by extern functions), layers are handled
//! internally inside a backend.

pub use evm_interpreter::{RuntimeBackend, RuntimeBaseBackend, RuntimeEnvironment};

/// Backend with layers that can transactionally be committed or discarded.
pub trait TransactionalBackend {
/// Push a new substate layer into the backend.
fn push_substate(&mut self);
/// Pop the last substate layer from the backend, either committing or
/// discarding it.
///
/// The caller is expected to maintain balance of push/pop, and the backend
/// are free to panic if it does not.
fn pop_substate(&mut self, strategy: crate::MergeStrategy);
}
21 changes: 21 additions & 0 deletions src/call_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ enum HeapTransactState<'backend, 'invoker, H, Tr, I: Invoker<H, Tr>> {
},
}

/// Heap-based call stack for a transaction. This is suitable for single
/// stepping or debugging. The hybrid version [transact] uses a heap-based call
/// stack internally after certain depth.
pub struct HeapTransact<'backend, 'invoker, H, Tr, I: Invoker<H, Tr>>(
Option<HeapTransactState<'backend, 'invoker, H, Tr, I>>,
);
Expand All @@ -301,6 +304,7 @@ impl<'backend, 'invoker, H, Tr, I> HeapTransact<'backend, 'invoker, H, Tr, I>
where
I: Invoker<H, Tr>,
{
/// Create a new heap-based call stack.
pub fn new(
args: I::TransactArgs,
invoker: &'invoker I,
Expand Down Expand Up @@ -385,18 +389,21 @@ where
ret
}

/// Step the call stack, but run the interpreter inside.
pub fn step_run(
&mut self,
) -> Result<(), Capture<Result<I::TransactValue, ExitError>, I::Interrupt>> {
self.step_with(|call_stack| call_stack.step_run())
}

/// Step the call stack, and step the interpreter inside.
pub fn step(
&mut self,
) -> Result<(), Capture<Result<I::TransactValue, ExitError>, I::Interrupt>> {
self.step_with(|call_stack| call_stack.step())
}

/// Run the call stack until it exits or receives interrupts.
pub fn run(&mut self) -> Capture<Result<I::TransactValue, ExitError>, I::Interrupt> {
loop {
let step_ret = self.step_run();
Expand All @@ -407,6 +414,8 @@ where
}
}

/// The machine of the last item on the call stack. This will be `None` if
/// the heap stack is just created.
pub fn last_machine(&self) -> Option<&I::Machine> {
match &self.0 {
Some(HeapTransactState::Running { call_stack, .. }) => match &call_stack.last {
Expand Down Expand Up @@ -467,6 +476,18 @@ where
}
}

/// Initiate a transaction, using a hybrid call stack.
///
/// Up until `heap_depth`, a stack-based call stack is used first. A stack-based
/// call stack is faster, but for really deep calls, it can reach the default
/// stack size limit of the platform and thus overflow.
///
/// After `heap_depth`, a heap-based call stack is then used.
///
/// If `heap_depth` is `None`, then always use a stack-based call stack.
///
/// Because a stack-based call stack cannot handle interrupts, the [Invoker]
/// type must have its `Interrupt` type set to [Infallible].
pub fn transact<H, Tr, I>(
args: I::TransactArgs,
heap_depth: Option<usize>,
Expand Down
24 changes: 24 additions & 0 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@ use crate::{
Capture, Control, Etable, ExitResult, Gasometer, InvokerMachine, Machine, Opcode, RuntimeState,
};

/// # Colored machine.
///
/// A colored machine combines the machine interpreter, the gasometer, as well
/// as a [Color]. It's the machine type that is pushed into a standard
/// [crate::standard::Invoker] call stack.
///
/// ## About the color field
///
/// A color is anything that implements the [Color] trait, defining how the
/// combined machine should be stepped or ran.
///
/// The standard color for a machine is an [Etable] (resolved by the standard
/// [crate::standard::EtableResolver]). The machine will use the opcode handler
/// defined in the etable for the machine invocation.
///
/// A customized color can allow you to implement account versioning or a
/// complex precompile that invoke subcalls.
pub struct ColoredMachine<S, G, C> {
/// The interpreter machine.
pub machine: Machine<S>,
/// The gasometer.
pub gasometer: G,
/// Whether the current call stack is static.
pub is_static: bool,
/// The color of the machine.
pub color: C,
}

Expand Down Expand Up @@ -38,7 +59,9 @@ where
}
}

/// A color of an machine.
pub trait Color<S, G, H, Tr> {
/// Step the machine.
fn step(
&self,
machine: &mut Machine<S>,
Expand All @@ -47,6 +70,7 @@ pub trait Color<S, G, H, Tr> {
handler: &mut H,
) -> Result<(), Capture<ExitResult, Tr>>;

/// Run the machine.
fn run(
&self,
machine: &mut Machine<S>,
Expand Down
20 changes: 6 additions & 14 deletions src/gasometer.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
//! EVM gasometer.

use crate::{ExitError, Machine};
use core::ops::{Add, AddAssign, Sub, SubAssign};
use primitive_types::U256;

pub trait Gas:
Copy
+ Into<U256>
+ Add<Self, Output = Self>
+ AddAssign<Self>
+ Sub<Self, Output = Self>
+ SubAssign<Self>
{
}

impl Gas for u64 {}
impl Gas for U256 {}

/// A static gasometer, exposing functions for precompile cost recording or for
/// transactions.
pub trait StaticGasometer: Sized {
fn record_cost(&mut self, cost: U256) -> Result<(), ExitError>;
fn record_codedeposit(&mut self, len: usize) -> Result<(), ExitError>;
fn gas(&self) -> U256;
}

/// A gasometer that is suitable for an interpreter machine.
pub trait Gasometer<S, H>: StaticGasometer {
/// Record gas cost for a single opcode step.
fn record_step(
&mut self,
machine: &Machine<S>,
is_static: bool,
backend: &H,
) -> Result<(), ExitError>;
/// Record gas cost, advancing as much as possible (possibly into the next
/// branch). Returns the number of advances.
fn record_stepn(
&mut self,
machine: &Machine<S>,
Expand Down
Loading

0 comments on commit 96bde6e

Please sign in to comment.