1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
use super::CodeSource;
use crate::{exec_trace::OperationRef, Error};
use eth_types::{
evm_types::{Memory, OpcodeId},
Address, Hash, Word,
};
/// Type of a *CALL*/CREATE* Function.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CallKind {
/// CALL
Call,
/// CALLCODE
CallCode,
/// DELEGATECALL
DelegateCall,
/// STATICCALL
StaticCall,
/// CREATE
Create,
/// CREATE2
Create2,
}
impl CallKind {
fn is_create(&self) -> bool {
matches!(self, Self::Create | Self::Create2)
}
}
impl Default for CallKind {
fn default() -> Self {
Self::Call
}
}
impl TryFrom<OpcodeId> for CallKind {
type Error = Error;
fn try_from(op: OpcodeId) -> Result<Self, Self::Error> {
Ok(match op {
OpcodeId::CALL => CallKind::Call,
OpcodeId::CALLCODE => CallKind::CallCode,
OpcodeId::DELEGATECALL => CallKind::DelegateCall,
OpcodeId::STATICCALL => CallKind::StaticCall,
OpcodeId::CREATE => CallKind::Create,
OpcodeId::CREATE2 => CallKind::Create2,
_ => return Err(Error::OpcodeIdNotCallType),
})
}
}
/// Circuit Input related to an Ethereum Call
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Call {
/// Unique call identifier within the Block.
pub call_id: usize,
/// Caller's id.
pub caller_id: usize,
/// Last Callee's id.
pub last_callee_id: usize,
/// Type of call
pub kind: CallKind,
/// This call is being executed without write access (STATIC)
pub is_static: bool,
/// This call generated implicitly by a Transaction.
pub is_root: bool,
/// This call is persistent or call stack reverts at some point
pub is_persistent: bool,
/// This call ends successfully or not
pub is_success: bool,
/// This rw_counter at the end of reversion
pub rw_counter_end_of_reversion: usize,
/// Address of caller
pub caller_address: Address,
/// Address where this call is being executed
pub address: Address,
/// Code Source
pub code_source: CodeSource,
/// Code Hash
pub code_hash: Hash,
/// Depth
pub depth: usize,
/// Value
pub value: Word,
/// Call data offset
pub call_data_offset: u64,
/// Call data length
pub call_data_length: u64,
/// Return data offset
pub return_data_offset: u64,
/// Return data length
pub return_data_length: u64,
/// last callee's return data offset
pub last_callee_return_data_offset: u64,
/// last callee's return data length
pub last_callee_return_data_length: u64,
}
impl Call {
/// This call is root call with tx.to == null, or op == CREATE or op ==
/// CREATE2
pub fn is_create(&self) -> bool {
self.kind.is_create()
}
/// This call is call with op DELEGATECALL
pub fn is_delegatecall(&self) -> bool {
matches!(self.kind, CallKind::DelegateCall)
}
/// Get the code address if possible
pub fn code_address(&self) -> Option<Address> {
match self.kind {
CallKind::Call | CallKind::StaticCall => Some(self.address),
CallKind::CallCode | CallKind::DelegateCall => match self.code_source {
CodeSource::Address(address) => Some(address),
_ => None,
},
CallKind::Create | CallKind::Create2 => None,
}
}
}
/// Context of a [`Call`].
#[derive(Debug, Default, Clone)]
pub struct CallContext {
/// Index of call
pub index: usize,
/// Reversible Write Counter tracks the number of write operations in the
/// call. It is incremented when a subcall in this call succeeds by the
/// number of successful writes in the subcall.
pub reversible_write_counter: usize,
/// Call data (copy of tx input or caller's
/// memory[call_data_offset..call_data_offset + call_data_length])
pub call_data: Vec<u8>,
/// memory context of current call
pub memory: Memory,
/// return data buffer
pub return_data: Vec<u8>,
}
impl CallContext {
/// Memory size in words, rounded up
pub fn memory_word_size(&self) -> u64 {
u64::try_from(self.memory.len()).expect("failed to convert usize to u64") / 32
}
}
/// A reversion group is the collection of calls and the operations which are
/// [`Operation::reversible`](crate::operation::Operation::reversible) that
/// happened in them, that will be reverted at once when the call that initiated
/// this reversion group eventually ends with failure (and thus reverts).
#[derive(Debug, Default, Clone)]
pub struct ReversionGroup {
/// List of `index` and `reversible_write_counter_offset` of calls belong to
/// this group. `reversible_write_counter_offset` is the number of
/// reversible operations that have happened before the call within the
/// same reversion group.
pub(crate) calls: Vec<(usize, usize)>,
/// List of `step_index` and [`OperationRef`] that have been done in this
/// group.
pub(crate) op_refs: Vec<(usize, OperationRef)>,
}
impl ReversionGroup {
/// Creates a new `ReversionGroup` instance from the calls and operation
/// references lists.
pub fn new(calls: Vec<(usize, usize)>, op_refs: Vec<(usize, OperationRef)>) -> Self {
Self { calls, op_refs }
}
}