pallet_revive/vm/evm/instructions/
host.rs1use super::{
19 utility::{IntoAddress, IntoU256},
20 Context,
21};
22use crate::vm::Ext;
23use core::cmp::min;
24use revm::{
25 interpreter::{
26 gas::{self, warm_cold_cost, CALL_STIPEND},
27 host::Host,
28 interpreter_types::{InputsTr, RuntimeFlag, StackTr},
29 InstructionResult,
30 },
31 primitives::{hardfork::SpecId::*, Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256},
32};
33
34pub fn balance<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
38 popn_top!([], top, context.interpreter);
39 let address = top.into_address();
40 let Some(balance) = context.host.balance(address) else {
41 context.interpreter.halt(InstructionResult::FatalExternalError);
42 return;
43 };
44 let spec_id = context.interpreter.runtime_flag.spec_id();
45 gas_legacy!(
46 context.interpreter,
47 if spec_id.is_enabled_in(BERLIN) {
48 warm_cold_cost(balance.is_cold)
49 } else if spec_id.is_enabled_in(ISTANBUL) {
50 700
52 } else if spec_id.is_enabled_in(TANGERINE) {
53 400
54 } else {
55 20
56 }
57 );
58 *top = balance.data;
59}
60
61pub fn selfbalance<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
63 check!(context.interpreter, ISTANBUL);
64 gas_legacy!(context.interpreter, gas::LOW);
65
66 let Some(balance) = context.host.balance(context.interpreter.input.target_address()) else {
67 context.interpreter.halt(InstructionResult::FatalExternalError);
68 return;
69 };
70 push!(context.interpreter, balance.data);
71}
72
73pub fn extcodesize<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
77 popn_top!([], top, context.interpreter);
78 let address = top.into_address();
79 let Some(code) = context.host.load_account_code(address) else {
80 context.interpreter.halt(InstructionResult::FatalExternalError);
81 return;
82 };
83 let spec_id = context.interpreter.runtime_flag.spec_id();
84 if spec_id.is_enabled_in(BERLIN) {
85 gas_legacy!(context.interpreter, warm_cold_cost(code.is_cold));
86 } else if spec_id.is_enabled_in(TANGERINE) {
87 gas_legacy!(context.interpreter, 700);
88 } else {
89 gas_legacy!(context.interpreter, 20);
90 }
91
92 *top = U256::from(code.len());
93}
94
95pub fn extcodehash<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
97 check!(context.interpreter, CONSTANTINOPLE);
98 popn_top!([], top, context.interpreter);
99 let address = top.into_address();
100 let Some(code_hash) = context.host.load_account_code_hash(address) else {
101 context.interpreter.halt(InstructionResult::FatalExternalError);
102 return;
103 };
104 let spec_id = context.interpreter.runtime_flag.spec_id();
105 if spec_id.is_enabled_in(BERLIN) {
106 gas_legacy!(context.interpreter, warm_cold_cost(code_hash.is_cold));
107 } else if spec_id.is_enabled_in(ISTANBUL) {
108 gas_legacy!(context.interpreter, 700);
109 } else {
110 gas_legacy!(context.interpreter, 400);
111 }
112 *top = code_hash.into_u256();
113}
114
115pub fn extcodecopy<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
119 popn!([address, memory_offset, code_offset, len_u256], context.interpreter);
120 let address = address.into_address();
121 let Some(code) = context.host.load_account_code(address) else {
122 context.interpreter.halt(InstructionResult::FatalExternalError);
123 return;
124 };
125
126 let len = as_usize_or_fail!(context.interpreter, len_u256);
127 gas_or_fail_legacy!(
128 context.interpreter,
129 gas::extcodecopy_cost(context.interpreter.runtime_flag.spec_id(), len, code.is_cold)
130 );
131 if len == 0 {
132 return;
133 }
134 let memory_offset = as_usize_or_fail!(context.interpreter, memory_offset);
135 let code_offset = min(as_usize_saturated!(code_offset), code.len());
136 resize_memory!(context.interpreter, memory_offset, len);
137
138 context.interpreter.memory.set_data(memory_offset, code_offset, len, &code);
140}
141
142pub fn blockhash<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
146 gas_legacy!(context.interpreter, gas::BLOCKHASH);
147 popn_top!([], number, context.interpreter);
148
149 let requested_number = *number;
150 let block_number = context.host.block_number();
151
152 let Some(diff) = block_number.checked_sub(requested_number) else {
153 *number = U256::ZERO;
154 return;
155 };
156
157 let diff = as_u64_saturated!(diff);
158
159 if diff == 0 {
161 *number = U256::ZERO;
162 return;
163 }
164
165 *number = if diff <= BLOCK_HASH_HISTORY {
166 let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else {
167 context.interpreter.halt(InstructionResult::FatalExternalError);
168 return;
169 };
170 U256::from_be_bytes(hash.0)
171 } else {
172 U256::ZERO
173 }
174}
175
176pub fn sload<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
180 popn_top!([], index, context.interpreter);
181
182 let Some(value) = context.host.sload(context.interpreter.input.target_address(), *index) else {
183 context.interpreter.halt(InstructionResult::FatalExternalError);
184 return;
185 };
186
187 gas_legacy!(
188 context.interpreter,
189 gas::sload_cost(context.interpreter.runtime_flag.spec_id(), value.is_cold)
190 );
191 *index = value.data;
192}
193
194pub fn sstore<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
198 require_non_staticcall!(context.interpreter);
199
200 popn!([index, value], context.interpreter);
201
202 let Some(state_load) =
203 context.host.sstore(context.interpreter.input.target_address(), index, value)
204 else {
205 context.interpreter.halt(InstructionResult::FatalExternalError);
206 return;
207 };
208
209 if context.interpreter.runtime_flag.spec_id().is_enabled_in(ISTANBUL) &&
211 context.interpreter.gas.remaining() <= CALL_STIPEND
212 {
213 context.interpreter.halt(InstructionResult::ReentrancySentryOOG);
214 return;
215 }
216 gas_legacy!(
217 context.interpreter,
218 gas::sstore_cost(
219 context.interpreter.runtime_flag.spec_id(),
220 &state_load.data,
221 state_load.is_cold
222 )
223 );
224
225 context.interpreter.gas.record_refund(gas::sstore_refund(
226 context.interpreter.runtime_flag.spec_id(),
227 &state_load.data,
228 ));
229}
230
231pub fn tstore<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
234 check!(context.interpreter, CANCUN);
235 require_non_staticcall!(context.interpreter);
236 gas_legacy!(context.interpreter, gas::WARM_STORAGE_READ_COST);
237
238 popn!([index, value], context.interpreter);
239
240 context.host.tstore(context.interpreter.input.target_address(), index, value);
241}
242
243pub fn tload<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
246 check!(context.interpreter, CANCUN);
247 gas_legacy!(context.interpreter, gas::WARM_STORAGE_READ_COST);
248
249 popn_top!([], index, context.interpreter);
250
251 *index = context.host.tload(context.interpreter.input.target_address(), *index);
252}
253
254pub fn log<'ext, const N: usize, E: Ext>(context: Context<'_, 'ext, E>) {
258 require_non_staticcall!(context.interpreter);
259
260 popn!([offset, len], context.interpreter);
261 let len = as_usize_or_fail!(context.interpreter, len);
262 gas_or_fail_legacy!(context.interpreter, gas::log_cost(N as u8, len as u64));
263 let data = if len == 0 {
264 Bytes::new()
265 } else {
266 let offset = as_usize_or_fail!(context.interpreter, offset);
267 resize_memory!(context.interpreter, offset, len);
268 Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref())
269 };
270 if context.interpreter.stack.len() < N {
271 context.interpreter.halt(InstructionResult::StackUnderflow);
272 return;
273 }
274 let Some(topics) = <_ as StackTr>::popn::<N>(&mut context.interpreter.stack) else {
275 context.interpreter.halt(InstructionResult::StackUnderflow);
276 return;
277 };
278
279 let log = Log {
280 address: context.interpreter.input.target_address(),
281 data: LogData::new(topics.into_iter().map(B256::from).collect(), data)
282 .expect("LogData should have <=4 topics"),
283 };
284
285 context.host.log(log);
286}
287
288pub fn selfdestruct<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
292 require_non_staticcall!(context.interpreter);
293 popn!([target], context.interpreter);
294 let target = target.into_address();
295
296 let Some(res) = context.host.selfdestruct(context.interpreter.input.target_address(), target)
297 else {
298 context.interpreter.halt(InstructionResult::FatalExternalError);
299 return;
300 };
301
302 if !context.interpreter.runtime_flag.spec_id().is_enabled_in(LONDON) &&
304 !res.previously_destroyed
305 {
306 context.interpreter.gas.record_refund(gas::SELFDESTRUCT)
307 }
308
309 gas_legacy!(
310 context.interpreter,
311 gas::selfdestruct_cost(context.interpreter.runtime_flag.spec_id(), res)
312 );
313
314 context.interpreter.halt(InstructionResult::SelfDestruct);
315}