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 }
    }
}