1#![doc = include_str!("../README.md")]
2#![allow(missing_docs, clippy::missing_safety_doc)]
3#![cfg_attr(not(test), warn(unused_extern_crates))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7extern crate alloc;
8
9#[macro_use]
10#[cfg(feature = "ir")]
11extern crate tracing;
12
13use alloc::{boxed::Box, vec::Vec};
14use revm_interpreter::{
15 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, InstructionResult,
16 InterpreterAction, InterpreterResult, as_u64_saturated, as_usize_saturated,
17 host::LoadError,
18 interpreter_types::{InputsTr, MemoryTr},
19};
20use revm_primitives::{Bytes, KECCAK_EMPTY, Log, LogData, U256, hardfork::SpecId};
21use revmc_context::{EvmContext, EvmWord};
22
23pub mod gas;
24
25#[cfg(feature = "ir")]
26mod ir;
27#[cfg(feature = "ir")]
28pub use ir::*;
29
30#[macro_use]
31mod macros;
32
33mod utils;
34use utils::*;
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38#[repr(u8)]
39pub enum CallKind {
40 Call,
42 CallCode,
44 DelegateCall,
46 StaticCall,
48}
49
50impl From<CallKind> for CallScheme {
51 fn from(kind: CallKind) -> Self {
52 match kind {
53 CallKind::Call => Self::Call,
54 CallKind::CallCode => Self::CallCode,
55 CallKind::DelegateCall => Self::DelegateCall,
56 CallKind::StaticCall => Self::StaticCall,
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63#[repr(u8)]
64pub enum CreateKind {
65 Create,
67 Create2,
69}
70
71#[unsafe(no_mangle)]
78pub unsafe extern "C-unwind" fn __revmc_builtin_panic(data: *const u8, len: usize) -> ! {
79 let msg = core::str::from_utf8_unchecked(core::slice::from_raw_parts(data, len));
80 panic!("{msg}");
81}
82
83#[unsafe(no_mangle)]
84pub unsafe extern "C" fn __revmc_builtin_udiv(rev![a, b]: &mut [EvmWord; 2]) {
85 let divisor = b.to_u256();
86 *b = if divisor.is_zero() { U256::ZERO } else { a.to_u256().wrapping_div(divisor) }.into();
87}
88
89#[unsafe(no_mangle)]
90pub unsafe extern "C" fn __revmc_builtin_urem(rev![a, b]: &mut [EvmWord; 2]) {
91 let divisor = b.to_u256();
92 *b = if divisor.is_zero() { U256::ZERO } else { a.to_u256().wrapping_rem(divisor) }.into();
93}
94
95#[unsafe(no_mangle)]
96pub unsafe extern "C" fn __revmc_builtin_sdiv(rev![a, b]: &mut [EvmWord; 2]) {
97 *b = revm_interpreter::instructions::i256::i256_div(a.to_u256(), b.to_u256()).into();
98}
99
100#[unsafe(no_mangle)]
101pub unsafe extern "C" fn __revmc_builtin_srem(rev![a, b]: &mut [EvmWord; 2]) {
102 *b = revm_interpreter::instructions::i256::i256_mod(a.to_u256(), b.to_u256()).into();
103}
104
105#[unsafe(no_mangle)]
106pub unsafe extern "C" fn __revmc_builtin_addmod(rev![a, b, c]: &mut [EvmWord; 3]) {
107 *c = a.to_u256().add_mod(b.to_u256(), c.to_u256()).into();
108}
109
110#[unsafe(no_mangle)]
111pub unsafe extern "C" fn __revmc_builtin_mulmod(rev![a, b, c]: &mut [EvmWord; 3]) {
112 *c = a.to_u256().mul_mod(b.to_u256(), c.to_u256()).into();
113}
114
115#[unsafe(no_mangle)]
116pub unsafe extern "C" fn __revmc_builtin_exp(
117 ecx: &mut EvmContext<'_>,
118 rev![base, exponent_ptr]: &mut [EvmWord; 2],
119) -> InstructionResult {
120 let exponent = exponent_ptr.to_u256();
121 gas!(ecx, ecx.host.gas_params().exp_cost(exponent));
122 *exponent_ptr = base.to_u256().pow(exponent).into();
123 InstructionResult::Stop
124}
125
126#[unsafe(no_mangle)]
127pub unsafe extern "C" fn __revmc_builtin_keccak256(
128 ecx: &mut EvmContext<'_>,
129 rev![offset, len_ptr]: &mut [EvmWord; 2],
130) -> InstructionResult {
131 let len = try_into_usize!(len_ptr);
132 *len_ptr = EvmWord::from_be_bytes(if len == 0 {
133 KECCAK_EMPTY.0
134 } else {
135 gas!(ecx, ecx.host.gas_params().keccak256_cost(len));
136 let offset = try_into_usize!(offset);
137 ensure_memory!(ecx, offset, len);
138 let data = ecx.memory.slice(offset..offset + len);
139 revm_primitives::keccak256(&*data).0
140 });
141 InstructionResult::Stop
142}
143
144#[unsafe(no_mangle)]
145pub unsafe extern "C" fn __revmc_builtin_balance(
146 ecx: &mut EvmContext<'_>,
147 address: &mut EvmWord,
148 spec_id: SpecId,
149) -> InstructionResult {
150 let addr = address.to_address();
151 if spec_id.is_enabled_in(SpecId::BERLIN) {
152 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
154 let account = berlin_load_account!(ecx, addr, false);
155 *address = account.balance.into();
156 } else {
157 let account = try_host!(ecx.host.load_account_info_skip_cold_load(addr, false, false).ok());
158 *address = account.balance.into();
159 }
160 InstructionResult::Stop
161}
162
163#[unsafe(no_mangle)]
164pub unsafe extern "C" fn __revmc_builtin_origin(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
165 let addr = ecx.host.caller();
167 let mut word = [0u8; 32];
168 word[12..32].copy_from_slice(addr.as_slice());
169 *slot = EvmWord::from_be_bytes(word);
170}
171
172#[unsafe(no_mangle)]
173pub unsafe extern "C" fn __revmc_builtin_calldataload(
174 ecx: &mut EvmContext<'_>,
175 offset: &mut EvmWord,
176) {
177 let offset_usize = as_usize_saturated!(offset.to_u256());
178 let mut word = [0u8; 32];
179
180 match ecx.input.input() {
181 CallInput::Bytes(bytes) => {
182 let len = bytes.len().saturating_sub(offset_usize).min(32);
183 if len > 0 && offset_usize < bytes.len() {
184 word[..len].copy_from_slice(&bytes[offset_usize..offset_usize + len]);
185 }
186 }
187 CallInput::SharedBuffer(range) => {
188 let input_slice = ecx.memory.global_slice(range.clone());
189 let input_len = input_slice.len();
190 if offset_usize < input_len {
191 let count = 32.min(input_len - offset_usize);
192 word[..count].copy_from_slice(&input_slice[offset_usize..offset_usize + count]);
193 }
194 }
195 }
196
197 *offset = EvmWord::from_be_bytes(word);
198}
199
200#[unsafe(no_mangle)]
201pub unsafe extern "C" fn __revmc_builtin_calldatasize(ecx: &mut EvmContext<'_>) -> usize {
202 match ecx.input.input() {
203 CallInput::Bytes(bytes) => bytes.len(),
204 CallInput::SharedBuffer(range) => range.len(),
205 }
206}
207
208#[unsafe(no_mangle)]
209pub unsafe extern "C" fn __revmc_builtin_calldatacopy(
210 ecx: &mut EvmContext<'_>,
211 rev![memory_offset, data_offset, len]: &mut [EvmWord; 3],
212) -> InstructionResult {
213 let len = try_into_usize!(len);
214 if len != 0 {
215 gas!(ecx, ecx.host.gas_params().copy_cost(len));
216 let memory_offset = try_into_usize!(memory_offset);
217 ensure_memory!(ecx, memory_offset, len);
218 let data_offset = as_usize_saturated!(data_offset.to_u256());
219
220 match ecx.input.input() {
221 CallInput::Bytes(bytes) => {
222 ecx.memory.set_data(memory_offset, data_offset, len, bytes.as_ref());
223 }
224 CallInput::SharedBuffer(range) => {
225 ecx.memory.set_data_from_global(memory_offset, data_offset, len, range.clone());
226 }
227 }
228 }
229 InstructionResult::Stop
230}
231
232#[unsafe(no_mangle)]
233pub unsafe extern "C" fn __revmc_builtin_codecopy(
234 ecx: &mut EvmContext<'_>,
235 sp: &mut [EvmWord; 3],
236) -> InstructionResult {
237 let bytecode = unsafe { &*ecx.bytecode };
238 copy_operation(ecx, sp, bytecode)
239}
240
241#[unsafe(no_mangle)]
242pub unsafe extern "C" fn __revmc_builtin_gas_price(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
243 *slot = ecx.host.effective_gas_price().into();
244}
245
246#[unsafe(no_mangle)]
247pub unsafe extern "C" fn __revmc_builtin_extcodesize(
248 ecx: &mut EvmContext<'_>,
249 address: &mut EvmWord,
250 spec_id: SpecId,
251) -> InstructionResult {
252 let addr = address.to_address();
253 if spec_id.is_enabled_in(SpecId::BERLIN) {
254 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
255 let account = berlin_load_account!(ecx, addr, true);
256 *address = U256::from(account.code.as_ref().unwrap().len()).into();
257 } else {
258 let account = try_host!(ecx.host.load_account_info_skip_cold_load(addr, true, false).ok());
259 *address = U256::from(account.code.as_ref().unwrap().len()).into();
260 }
261 InstructionResult::Stop
262}
263
264#[unsafe(no_mangle)]
265pub unsafe extern "C" fn __revmc_builtin_extcodecopy(
266 ecx: &mut EvmContext<'_>,
267 rev![address, memory_offset, code_offset, len]: &mut [EvmWord; 4],
268 spec_id: SpecId,
269) -> InstructionResult {
270 let addr = address.to_address();
271 let len = try_into_usize!(len);
272 gas!(ecx, ecx.host.gas_params().extcodecopy(len));
273
274 let mut memory_offset_usize = 0;
275 if len != 0 {
276 memory_offset_usize = try_into_usize!(memory_offset);
277 ensure_memory!(ecx, memory_offset_usize, len);
278 }
279
280 let code = if spec_id.is_enabled_in(SpecId::BERLIN) {
281 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
282 let account = berlin_load_account!(ecx, addr, true);
283 account.code.as_ref().unwrap().original_bytes()
284 } else {
285 let code = try_host!(ecx.host.load_account_code(addr));
286 code.data
287 };
288
289 let code_offset_usize = core::cmp::min(as_usize_saturated!(code_offset.to_u256()), code.len());
290 ecx.memory.set_data(memory_offset_usize, code_offset_usize, len, &code);
291 InstructionResult::Stop
292}
293
294#[unsafe(no_mangle)]
295pub unsafe extern "C" fn __revmc_builtin_returndatacopy(
296 ecx: &mut EvmContext<'_>,
297 rev![memory_offset, offset, len]: &mut [EvmWord; 3],
298) -> InstructionResult {
299 let len = try_into_usize!(len);
300 let data_offset = as_usize_saturated!(offset.to_u256());
301
302 let data_end = data_offset.saturating_add(len);
304 if data_end > ecx.return_data.len() {
305 return InstructionResult::OutOfOffset;
306 }
307
308 gas!(ecx, ecx.host.gas_params().copy_cost(len));
309 if len != 0 {
310 let memory_offset = try_into_usize!(memory_offset);
311 ensure_memory!(ecx, memory_offset, len);
312 ecx.memory.set(memory_offset, &ecx.return_data[data_offset..data_end]);
313 }
314 InstructionResult::Stop
315}
316
317#[unsafe(no_mangle)]
318pub unsafe extern "C" fn __revmc_builtin_extcodehash(
319 ecx: &mut EvmContext<'_>,
320 address: &mut EvmWord,
321 spec_id: SpecId,
322) -> InstructionResult {
323 let addr = address.to_address();
324 let account = if spec_id.is_enabled_in(SpecId::BERLIN) {
325 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
326 berlin_load_account!(ecx, addr, false)
327 } else {
328 try_host!(ecx.host.load_account_info_skip_cold_load(addr, false, false).ok())
329 };
330 let code_hash = if account.is_empty { revm_primitives::B256::ZERO } else { account.code_hash };
331 *address = EvmWord::from_be_bytes(code_hash.0);
332 InstructionResult::Stop
333}
334
335#[unsafe(no_mangle)]
336pub unsafe extern "C" fn __revmc_builtin_blockhash(
337 ecx: &mut EvmContext<'_>,
338 number_ptr: &mut EvmWord,
339) -> InstructionResult {
340 let requested_number = number_ptr.to_u256();
341 let block_number = ecx.host.block_number();
342
343 let Some(diff) = block_number.checked_sub(requested_number) else {
345 *number_ptr = EvmWord::ZERO;
346 return InstructionResult::Stop;
347 };
348
349 let diff = as_u64_saturated!(diff);
350
351 if diff == 0 {
353 *number_ptr = EvmWord::ZERO;
354 return InstructionResult::Stop;
355 }
356
357 const BLOCK_HASH_HISTORY: u64 = 256;
359
360 if diff <= BLOCK_HASH_HISTORY {
361 let hash = try_host!(ecx.host.block_hash(as_u64_saturated!(requested_number)));
362 *number_ptr = EvmWord::from_be_bytes(hash.0);
363 } else {
364 *number_ptr = EvmWord::ZERO;
366 }
367
368 InstructionResult::Stop
369}
370
371#[unsafe(no_mangle)]
372pub unsafe extern "C" fn __revmc_builtin_coinbase(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
373 let addr = ecx.host.beneficiary();
375 let mut word = [0u8; 32];
376 word[12..32].copy_from_slice(addr.as_slice());
377 *slot = EvmWord::from_be_bytes(word);
378}
379
380#[unsafe(no_mangle)]
381pub unsafe extern "C" fn __revmc_builtin_timestamp(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
382 *slot = ecx.host.timestamp().into();
383}
384
385#[unsafe(no_mangle)]
386pub unsafe extern "C" fn __revmc_builtin_number(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
387 *slot = ecx.host.block_number().into();
388}
389
390#[unsafe(no_mangle)]
391pub unsafe extern "C" fn __revmc_builtin_gaslimit(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
392 *slot = ecx.host.gas_limit().into();
393}
394
395#[unsafe(no_mangle)]
396pub unsafe extern "C" fn __revmc_builtin_chainid(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
397 *slot = ecx.host.chain_id().into();
398}
399
400#[unsafe(no_mangle)]
401pub unsafe extern "C" fn __revmc_builtin_basefee(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
402 *slot = ecx.host.basefee().into();
403}
404
405#[unsafe(no_mangle)]
406pub unsafe extern "C" fn __revmc_builtin_difficulty(
407 ecx: &mut EvmContext<'_>,
408 slot: &mut EvmWord,
409 spec_id: SpecId,
410) {
411 *slot = if spec_id.is_enabled_in(SpecId::MERGE) {
412 ecx.host.prevrandao().unwrap_or_default().into()
413 } else {
414 ecx.host.difficulty().into()
415 };
416}
417
418#[unsafe(no_mangle)]
419pub unsafe extern "C" fn __revmc_builtin_self_balance(
420 ecx: &mut EvmContext<'_>,
421 slot: &mut EvmWord,
422) -> InstructionResult {
423 let state = try_host!(ecx.host.balance(ecx.input.target_address));
424 *slot = state.data.into();
425 InstructionResult::Stop
426}
427
428#[unsafe(no_mangle)]
429pub unsafe extern "C" fn __revmc_builtin_blob_hash(
430 ecx: &mut EvmContext<'_>,
431 index_ptr: &mut EvmWord,
432) {
433 let index = index_ptr.to_u256();
434 let index_usize = as_usize_saturated!(index);
435 *index_ptr = ecx.host.blob_hash(index_usize).unwrap_or_default().into();
436}
437
438#[unsafe(no_mangle)]
439pub unsafe extern "C" fn __revmc_builtin_blob_base_fee(
440 ecx: &mut EvmContext<'_>,
441 slot: &mut EvmWord,
442) {
443 *slot = ecx.host.blob_gasprice().into();
444}
445
446#[unsafe(no_mangle)]
447pub unsafe extern "C" fn __revmc_builtin_sload(
448 ecx: &mut EvmContext<'_>,
449 index: &mut EvmWord,
450 spec_id: SpecId,
451) -> InstructionResult {
452 let address = ecx.input.target_address;
453 let key = index.to_u256();
454 if spec_id.is_enabled_in(SpecId::BERLIN) {
455 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
456 let additional_cold_cost = ecx.host.gas_params().cold_storage_additional_cost();
457 let skip_cold = ecx.gas.remaining() < additional_cold_cost;
458 match ecx.host.sload_skip_cold_load(address, key, skip_cold) {
459 Ok(storage) => {
460 if storage.is_cold {
461 gas!(ecx, additional_cold_cost);
462 }
463 *index = storage.data.into();
464 }
465 Err(LoadError::ColdLoadSkipped) => return InstructionResult::OutOfGas,
466 Err(LoadError::DBError) => return InstructionResult::FatalExternalError,
467 }
468 } else {
469 let storage = try_host!(ecx.host.sload(address, key));
470 gas!(ecx, gas::sload_cost(spec_id, storage.is_cold));
471 *index = storage.data.into();
472 }
473
474 InstructionResult::Stop
475}
476
477#[unsafe(no_mangle)]
478pub unsafe extern "C" fn __revmc_builtin_sstore(
479 ecx: &mut EvmContext<'_>,
480 rev![index, value]: &mut [EvmWord; 2],
481 spec_id: SpecId,
482) -> InstructionResult {
483 ensure_non_staticcall!(ecx);
484
485 let target = ecx.input.target_address;
486 let is_istanbul = spec_id.is_enabled_in(SpecId::ISTANBUL);
487
488 if is_istanbul && ecx.gas.remaining() <= ecx.host.gas_params().call_stipend() {
490 return InstructionResult::ReentrancySentryOOG;
491 }
492
493 gas!(ecx, ecx.host.gas_params().sstore_static_gas());
494
495 let state_load = if spec_id.is_enabled_in(SpecId::BERLIN) {
496 let additional_cold_cost = ecx.host.gas_params().cold_storage_additional_cost();
497 let skip_cold = ecx.gas.remaining() < additional_cold_cost;
498 match ecx.host.sstore_skip_cold_load(target, index.to_u256(), value.to_u256(), skip_cold) {
499 Ok(load) => load,
500 Err(LoadError::ColdLoadSkipped) => return InstructionResult::OutOfGas,
501 Err(LoadError::DBError) => return InstructionResult::FatalExternalError,
502 }
503 } else {
504 try_host!(ecx.host.sstore(target, index.to_u256(), value.to_u256()))
505 };
506
507 let gp = ecx.host.gas_params();
508 gas!(ecx, gp.sstore_dynamic_gas(is_istanbul, &state_load.data, state_load.is_cold));
509 ecx.gas.record_refund(gp.sstore_refund(is_istanbul, &state_load.data));
510 InstructionResult::Stop
511}
512
513#[unsafe(no_mangle)]
514pub unsafe extern "C" fn __revmc_builtin_msize(ecx: &mut EvmContext<'_>) -> usize {
515 ecx.memory.len()
516}
517
518#[unsafe(no_mangle)]
519pub unsafe extern "C" fn __revmc_builtin_tstore(
520 ecx: &mut EvmContext<'_>,
521 rev![key, value]: &mut [EvmWord; 2],
522) -> InstructionResult {
523 ensure_non_staticcall!(ecx);
524 ecx.host.tstore(ecx.input.target_address, key.to_u256(), value.to_u256());
525 InstructionResult::Stop
526}
527
528#[unsafe(no_mangle)]
529pub unsafe extern "C" fn __revmc_builtin_tload(ecx: &mut EvmContext<'_>, key: &mut EvmWord) {
530 *key = ecx.host.tload(ecx.input.target_address, key.to_u256()).into();
531}
532
533#[unsafe(no_mangle)]
534pub unsafe extern "C" fn __revmc_builtin_mcopy(
535 ecx: &mut EvmContext<'_>,
536 rev![dst, src, len]: &mut [EvmWord; 3],
537) -> InstructionResult {
538 let len = try_into_usize!(len);
539 gas!(ecx, ecx.host.gas_params().mcopy_cost(len));
540 if len != 0 {
541 let dst = try_into_usize!(dst);
542 let src = try_into_usize!(src);
543 ensure_memory!(ecx, dst.max(src), len);
544 ecx.memory.copy(dst, src, len);
545 }
546 InstructionResult::Stop
547}
548
549#[unsafe(no_mangle)]
550pub unsafe extern "C" fn __revmc_builtin_log(
551 ecx: &mut EvmContext<'_>,
552 sp: *mut EvmWord,
553 n: u8,
554) -> InstructionResult {
555 ensure_non_staticcall!(ecx);
556 assume!(n <= 4, "invalid log topic count: {n}");
557 let sp = sp.add(n as usize);
558 read_words!(sp, offset, len);
559 let len = try_into_usize!(len);
560 gas_opt!(ecx, gas::dyn_log_cost(len as u64));
561 let data = if len != 0 {
562 let offset = try_into_usize!(offset);
563 ensure_memory!(ecx, offset, len);
564 Bytes::copy_from_slice(&ecx.memory.slice(offset..offset + len))
565 } else {
566 Bytes::new()
567 };
568
569 let mut topics = Vec::with_capacity(n as usize);
570 for i in 1..=n {
571 topics.push(sp.sub(i as usize).read().to_be_bytes().into());
572 }
573
574 ecx.host.log(Log {
575 address: ecx.input.target_address,
576 data: LogData::new(topics, data).expect("too many topics"),
577 });
578 InstructionResult::Stop
579}
580
581#[unsafe(no_mangle)]
585pub unsafe extern "C" fn __revmc_builtin_create(
586 ecx: &mut EvmContext<'_>,
587 sp: *mut EvmWord,
588 spec_id: SpecId,
589 create_kind: CreateKind,
590) -> InstructionResult {
591 ensure_non_staticcall!(ecx);
592
593 let len = match create_kind {
594 CreateKind::Create => 3,
595 CreateKind::Create2 => 4,
596 };
597 let mut sp = sp.add(len);
598 pop!(sp; value, code_offset, len);
599
600 let len = try_into_usize!(len);
601 let code = if len != 0 {
602 if spec_id.is_enabled_in(SpecId::SHANGHAI) {
603 let max_initcode_size = ecx.host.max_initcode_size();
605 if len > max_initcode_size {
606 return InstructionResult::CreateInitCodeSizeLimit;
607 }
608 gas!(ecx, ecx.host.gas_params().initcode_cost(len));
609 }
610
611 let code_offset = try_into_usize!(code_offset);
612 ensure_memory!(ecx, code_offset, len);
613 Bytes::copy_from_slice(&ecx.memory.slice(code_offset..code_offset + len))
614 } else {
615 Bytes::new()
616 };
617
618 let is_create2 = create_kind == CreateKind::Create2;
619 let gp = ecx.host.gas_params();
620 gas!(ecx, if is_create2 { gp.create2_cost(len) } else { gp.create_cost() });
621
622 let scheme = if is_create2 {
623 pop!(sp; salt);
624 CreateScheme::Create2 { salt: salt.to_u256() }
625 } else {
626 CreateScheme::Create
627 };
628
629 let mut gas_limit = ecx.gas.remaining();
630 if spec_id.is_enabled_in(SpecId::TANGERINE) {
631 gas_limit = ecx.host.gas_params().call_stipend_reduction(gas_limit);
632 }
633 gas!(ecx, gas_limit);
634
635 *ecx.next_action =
636 Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Create(Box::new(
637 CreateInputs::new(ecx.input.target_address, scheme, value.to_u256(), code, gas_limit),
638 ))));
639
640 InstructionResult::Stop
641}
642
643#[unsafe(no_mangle)]
644pub unsafe extern "C" fn __revmc_builtin_call(
645 ecx: &mut EvmContext<'_>,
646 sp: *mut EvmWord,
647 spec_id: SpecId,
648 call_kind: CallKind,
649) -> InstructionResult {
650 let len = match call_kind {
651 CallKind::Call | CallKind::CallCode => 7,
652 CallKind::DelegateCall | CallKind::StaticCall => 6,
653 };
654 let mut sp = sp.add(len);
655
656 pop!(sp; local_gas_limit, to);
657 let local_gas_limit = local_gas_limit.to_u256();
658 let to = to.to_address();
659
660 let local_gas_limit = as_u64_saturated!(local_gas_limit);
664
665 let value = match call_kind {
666 CallKind::Call | CallKind::CallCode => {
667 pop!(sp; value);
668 let value = value.to_u256();
669 if call_kind == CallKind::Call && ecx.is_static && value != U256::ZERO {
670 return InstructionResult::CallNotAllowedInsideStatic;
671 }
672 value
673 }
674 CallKind::DelegateCall | CallKind::StaticCall => U256::ZERO,
675 };
676 let transfers_value = value != U256::ZERO;
677
678 pop!(sp; in_offset, in_len, out_offset, out_len);
679
680 let in_len = try_into_usize!(in_len);
681 let input = if in_len != 0 {
682 let in_offset = try_into_usize!(in_offset);
683 ensure_memory!(ecx, in_offset, in_len);
684 Bytes::copy_from_slice(&ecx.memory.slice(in_offset..in_offset + in_len))
685 } else {
686 Bytes::new()
687 };
688
689 let out_len = try_into_usize!(out_len);
690 let out_offset = if out_len != 0 {
691 let out_offset = try_into_usize!(out_offset);
692 ensure_memory!(ecx, out_offset, out_len);
693 out_offset
694 } else {
695 usize::MAX };
697
698 gas!(ecx, ecx.host.gas_params().warm_storage_read_cost());
702
703 if transfers_value {
704 gas!(ecx, ecx.host.gas_params().transfer_value_cost());
705 }
706
707 let (dynamic_gas, bytecode, code_hash) =
710 match revm_interpreter::instructions::contract::load_account_delegated(
711 ecx.host,
712 spec_id,
713 ecx.gas.remaining(),
714 to,
715 transfers_value,
716 call_kind == CallKind::Call,
717 ) {
718 Ok(out) => out,
719 Err(LoadError::ColdLoadSkipped) => return InstructionResult::OutOfGas,
720 Err(LoadError::DBError) => return InstructionResult::FatalExternalError,
721 };
722
723 gas!(ecx, dynamic_gas);
724
725 let mut gas_limit = if spec_id.is_enabled_in(SpecId::TANGERINE) {
727 let gas = ecx.gas.remaining();
728 ecx.host.gas_params().call_stipend_reduction(gas).min(local_gas_limit)
729 } else {
730 local_gas_limit
731 };
732
733 gas!(ecx, gas_limit);
734
735 if matches!(call_kind, CallKind::Call | CallKind::CallCode) && transfers_value {
737 gas_limit = gas_limit.saturating_add(ecx.host.gas_params().call_stipend());
738 }
739
740 *ecx.next_action = Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Call(
741 Box::new(CallInputs {
742 input: CallInput::Bytes(input),
743 return_memory_offset: out_offset..out_offset + out_len,
744 gas_limit,
745 bytecode_address: to,
746 known_bytecode: Some((code_hash, bytecode)),
747 target_address: if matches!(call_kind, CallKind::DelegateCall | CallKind::CallCode) {
748 ecx.input.target_address
749 } else {
750 to
751 },
752 caller: if call_kind == CallKind::DelegateCall {
753 ecx.input.caller_address
754 } else {
755 ecx.input.target_address
756 },
757 value: if call_kind == CallKind::DelegateCall {
758 CallValue::Apparent(ecx.input.call_value)
759 } else {
760 CallValue::Transfer(value)
761 },
762 scheme: call_kind.into(),
763 is_static: ecx.is_static || call_kind == CallKind::StaticCall,
764 }),
765 )));
766
767 InstructionResult::Stop
768}
769
770#[unsafe(no_mangle)]
771pub unsafe extern "C" fn __revmc_builtin_do_return(
772 ecx: &mut EvmContext<'_>,
773 rev![offset, len]: &mut [EvmWord; 2],
774 result: InstructionResult,
775) -> InstructionResult {
776 let len = try_into_usize!(len);
777 let output = if len != 0 {
778 let offset = try_into_usize!(offset);
779 ensure_memory!(ecx, offset, len);
780 ecx.memory.slice(offset..offset + len).to_vec().into()
781 } else {
782 Bytes::new()
783 };
784 *ecx.next_action =
785 Some(InterpreterAction::Return(InterpreterResult { output, gas: *ecx.gas, result }));
786 InstructionResult::Stop
787}
788
789#[unsafe(no_mangle)]
790pub unsafe extern "C" fn __revmc_builtin_selfdestruct(
791 ecx: &mut EvmContext<'_>,
792 target: &mut EvmWord,
793 spec_id: SpecId,
794) -> InstructionResult {
795 ensure_non_staticcall!(ecx);
796
797 if spec_id.is_enabled_in(SpecId::TANGERINE) {
800 gas!(ecx, 5000);
801 }
802
803 let cold_load_gas = ecx.host.gas_params().selfdestruct_cold_cost();
804 let skip_cold_load = ecx.gas.remaining() < cold_load_gas;
805 let res = match ecx.host.selfdestruct(
806 ecx.input.target_address,
807 target.to_address(),
808 skip_cold_load,
809 ) {
810 Ok(r) => r,
811 Err(LoadError::ColdLoadSkipped) => return InstructionResult::OutOfGas,
812 Err(LoadError::DBError) => return InstructionResult::FatalExternalError,
813 };
814
815 let should_charge_topup = if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
817 res.had_value && !res.target_exists
818 } else {
819 !res.target_exists
820 };
821
822 gas!(ecx, ecx.host.gas_params().selfdestruct_cost(should_charge_topup, res.is_cold));
823
824 if !res.previously_destroyed {
825 ecx.gas.record_refund(ecx.host.gas_params().selfdestruct_refund());
826 }
827
828 InstructionResult::SelfDestruct
829}
830
831#[unsafe(no_mangle)]
832pub unsafe extern "C" fn __revmc_builtin_resize_memory(
833 ecx: &mut EvmContext<'_>,
834 new_size: usize,
835) -> InstructionResult {
836 resize_memory(ecx, new_size)
837}
838
839#[unsafe(no_mangle)]
840pub unsafe extern "C" fn __revmc_builtin_mload(
841 ecx: &mut EvmContext<'_>,
842 rev![offset_ptr]: &mut [EvmWord; 1],
843) -> InstructionResult {
844 let offset = try_into_usize!(offset_ptr);
845 ensure_memory!(ecx, offset, 32);
846 let slice = ecx.memory.slice(offset..offset + 32);
847 let mut word = [0u8; 32];
848 word.copy_from_slice(&slice);
849 *offset_ptr = EvmWord::from_be_bytes(word);
850 InstructionResult::Stop
851}
852
853#[unsafe(no_mangle)]
854pub unsafe extern "C" fn __revmc_builtin_mstore(
855 ecx: &mut EvmContext<'_>,
856 rev![offset, value]: &mut [EvmWord; 2],
857) -> InstructionResult {
858 let offset = try_into_usize!(offset);
859 ensure_memory!(ecx, offset, 32);
860 ecx.memory.set(offset, &value.to_be_bytes());
861 InstructionResult::Stop
862}
863
864#[unsafe(no_mangle)]
865pub unsafe extern "C" fn __revmc_builtin_mstore8(
866 ecx: &mut EvmContext<'_>,
867 rev![offset, value]: &mut [EvmWord; 2],
868) -> InstructionResult {
869 let offset = try_into_usize!(offset);
870 ensure_memory!(ecx, offset, 1);
871 let byte = value.to_be_bytes()[31];
872 ecx.memory.set(offset, &[byte]);
873 InstructionResult::Stop
874}