-
Notifications
You must be signed in to change notification settings - Fork 983
Expand file tree
/
Copy pathinterpreter.rs
More file actions
192 lines (173 loc) · 5.82 KB
/
interpreter.rs
File metadata and controls
192 lines (173 loc) · 5.82 KB
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
pub mod bytecode;
mod contract;
pub(crate) mod memory;
mod stack;
pub use bytecode::{Bytecode, BytecodeLocked, BytecodeState};
pub use contract::Contract;
pub use memory::Memory;
pub use stack::Stack;
use crate::{
instructions::{eval, Return},
Gas, Host, Spec, USE_GAS,
};
use bytes::Bytes;
use core::ops::Range;
pub const STACK_LIMIT: u64 = 1024;
pub const CALL_STACK_LIMIT: u64 = 1024;
pub struct Interpreter {
/// Instruction pointer.
pub instruction_pointer: *const u8,
/// Return is main control flag, it tell us if we should continue interpreter or break from it
pub instruction_result: Return,
/// left gas. Memory gas can be found in Memory field.
pub gas: Gas,
/// Memory.
pub memory: Memory,
/// Stack.
pub stack: Stack,
/// After call returns, its return data is saved here.
pub return_data_buffer: Bytes,
/// Return value.
pub return_range: Range<usize>,
/// Is interpreter call static.
pub is_static: bool,
/// Contract information and invoking data
pub contract: Contract,
/// Memory limit. See [`crate::CfgEnv`].
#[cfg(feature = "memory_limit")]
pub memory_limit: u64,
}
impl Interpreter {
/// Current opcode
pub fn current_opcode(&self) -> u8 {
unsafe { *self.instruction_pointer }
}
/// Create new interpreter
pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self {
#[cfg(not(feature = "memory_limit"))]
{
Self {
instruction_pointer: contract.bytecode.as_ptr(),
return_range: Range::default(),
memory: Memory::new(),
stack: Stack::new(),
return_data_buffer: Bytes::new(),
contract,
instruction_result: Return::Continue,
is_static,
gas: Gas::new(gas_limit),
}
}
#[cfg(feature = "memory_limit")]
{
Self::new_with_memory_limit(contract, gas_limit, is_static, u64::MAX)
}
}
#[cfg(feature = "memory_limit")]
pub fn new_with_memory_limit(
contract: Contract,
gas_limit: u64,
is_static: bool,
memory_limit: u64,
) -> Self {
Self {
instruction_pointer: contract.bytecode.as_ptr(),
return_range: Range::default(),
memory: Memory::new(),
stack: Stack::new(),
return_data_buffer: Bytes::new(),
contract,
instruction_result: Return::Continue,
is_static,
gas: Gas::new(gas_limit),
memory_limit,
}
}
pub fn contract(&self) -> &Contract {
&self.contract
}
pub fn gas(&self) -> &Gas {
&self.gas
}
/// Reference of interpreter memory.
pub fn memory(&self) -> &Memory {
&self.memory
}
/// Reference of interpreter stack.
pub fn stack(&self) -> &Stack {
&self.stack
}
#[inline(always)]
pub fn add_next_gas_block(&mut self, pc: usize) -> Option<Return> {
if USE_GAS {
let gas_block = self.contract.gas_block(pc);
if !self.gas.record_cost(gas_block) {
return Some(Return::OutOfGas);
}
}
None
}
/// Return a reference of the program counter.
pub fn program_counter(&self) -> usize {
// Safety: this is just subtraction of pointers, it is safe to do.
unsafe {
self.instruction_pointer
.offset_from(self.contract.bytecode.as_ptr()) as usize
}
}
/// Execute next instruction
#[inline(always)]
pub fn step<H: Host, SPEC: Spec>(&mut self, host: &mut H) {
// step.
let opcode = unsafe { *self.instruction_pointer };
// Safety: In analysis we are doing padding of bytecode so that we are sure that last
// byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
// it will do noop and just stop execution of this contract
self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) };
eval::<H, SPEC>(opcode, self, host);
}
/// loop steps until we are finished with execution
pub fn run<H: Host, SPEC: Spec>(&mut self, host: &mut H) -> Return {
// add first gas_block
if USE_GAS && !self.gas.record_cost(self.contract.first_gas_block()) {
return Return::OutOfGas;
}
while self.instruction_result == Return::Continue {
self.step::<H, SPEC>(host)
}
self.instruction_result
}
/// loop steps until we are finished with execution
pub fn run_inspect<H: Host, SPEC: Spec>(&mut self, host: &mut H) -> Return {
// add first gas_block
if USE_GAS && !self.gas.record_cost(self.contract.first_gas_block()) {
return Return::OutOfGas;
}
while self.instruction_result == Return::Continue {
// step
let ret = host.step(self, self.is_static);
if ret != Return::Continue {
return ret;
}
self.step::<H, SPEC>(host);
// step ends
let ret = host.step_end(self, self.is_static, self.instruction_result);
if ret != Return::Continue {
return ret;
}
}
self.instruction_result
}
/// Copy and get the return value of the interpreter, if any.
pub fn return_value(&self) -> Bytes {
// if start is usize max it means that our return len is zero and we need to return empty
if self.return_range.start == usize::MAX {
Bytes::new()
} else {
Bytes::copy_from_slice(self.memory.get_slice(
self.return_range.start,
self.return_range.end - self.return_range.start,
))
}
}
}