1use revm_interpreter::CallValue;
2use revm_precompile::PrecompileErrors;
3
4use super::inner_evm_context::InnerEvmContext;
5use crate::{
6 db::Database,
7 interpreter::{
8 analysis::validate_eof, CallInputs, Contract, CreateInputs, EOFCreateInputs, EOFCreateKind,
9 Gas, InstructionResult, Interpreter, InterpreterResult,
10 },
11 primitives::{
12 keccak256, Address, Bytecode, Bytes, CreateScheme, EVMError, Env, Eof,
13 SpecId::{self, *},
14 B256, EOF_MAGIC_BYTES,
15 },
16 ContextPrecompiles, FrameOrResult, CALL_STACK_LIMIT,
17};
18use core::{
19 fmt,
20 ops::{Deref, DerefMut},
21};
22use std::{boxed::Box, sync::Arc};
23
24pub struct EvmContext<DB: Database> {
26 pub inner: InnerEvmContext<DB>,
28 pub precompiles: ContextPrecompiles<DB>,
30}
31
32impl<DB: Database + Clone> Clone for EvmContext<DB>
33where
34 DB::Error: Clone,
35{
36 fn clone(&self) -> Self {
37 Self {
38 inner: self.inner.clone(),
39 precompiles: ContextPrecompiles::default(),
40 }
41 }
42}
43
44impl<DB> fmt::Debug for EvmContext<DB>
45where
46 DB: Database + fmt::Debug,
47 DB::Error: fmt::Debug,
48{
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("EvmContext")
51 .field("inner", &self.inner)
52 .field("precompiles", &self.inner)
53 .finish_non_exhaustive()
54 }
55}
56
57impl<DB: Database> Deref for EvmContext<DB> {
58 type Target = InnerEvmContext<DB>;
59
60 fn deref(&self) -> &Self::Target {
61 &self.inner
62 }
63}
64
65impl<DB: Database> DerefMut for EvmContext<DB> {
66 fn deref_mut(&mut self) -> &mut Self::Target {
67 &mut self.inner
68 }
69}
70
71impl<DB: Database> EvmContext<DB> {
72 pub fn new(db: DB) -> Self {
74 Self {
75 inner: InnerEvmContext::new(db),
76 precompiles: ContextPrecompiles::default(),
77 }
78 }
79
80 #[inline]
82 pub fn new_with_env(db: DB, env: Box<Env>) -> Self {
83 Self {
84 inner: InnerEvmContext::new_with_env(db, env),
85 precompiles: ContextPrecompiles::default(),
86 }
87 }
88
89 #[inline]
93 pub fn with_db<ODB: Database>(self, db: ODB) -> EvmContext<ODB> {
94 EvmContext {
95 inner: self.inner.with_db(db),
96 precompiles: ContextPrecompiles::default(),
97 }
98 }
99
100 #[inline]
102 pub fn set_precompiles(&mut self, precompiles: ContextPrecompiles<DB>) {
103 self.journaled_state
105 .warm_preloaded_addresses
106 .extend(precompiles.addresses_set());
107 self.precompiles = precompiles;
108 }
109
110 #[inline]
112 fn call_precompile(
113 &mut self,
114 address: &Address,
115 input_data: &Bytes,
116 gas: Gas,
117 ) -> Result<Option<InterpreterResult>, EVMError<DB::Error>> {
118 let Some(outcome) =
119 self.precompiles
120 .call(address, input_data, gas.limit(), &mut self.inner)
121 else {
122 return Ok(None);
123 };
124
125 let mut result = InterpreterResult {
126 result: InstructionResult::Return,
127 gas,
128 output: Bytes::new(),
129 };
130
131 match outcome {
132 Ok(output) => {
133 if result.gas.record_cost(output.gas_used) {
134 result.result = InstructionResult::Return;
135 result.output = output.bytes;
136 } else {
137 result.result = InstructionResult::PrecompileOOG;
138 }
139 }
140 Err(PrecompileErrors::Error(e)) => {
141 result.result = if e.is_oog() {
142 InstructionResult::PrecompileOOG
143 } else {
144 InstructionResult::PrecompileError
145 };
146 }
147 Err(PrecompileErrors::Fatal { msg }) => return Err(EVMError::Precompile(msg)),
148 }
149 Ok(Some(result))
150 }
151
152 #[inline]
154 pub fn make_call_frame(
155 &mut self,
156 inputs: &CallInputs,
157 ) -> Result<FrameOrResult, EVMError<DB::Error>> {
158 let gas = Gas::new(inputs.gas_limit);
159
160 let return_result = |instruction_result: InstructionResult| {
161 Ok(FrameOrResult::new_call_result(
162 InterpreterResult {
163 result: instruction_result,
164 gas,
165 output: Bytes::new(),
166 },
167 inputs.return_memory_offset.clone(),
168 ))
169 };
170
171 if self.journaled_state.depth() > CALL_STACK_LIMIT {
173 return return_result(InstructionResult::CallTooDeep);
174 }
175
176 let _ = self
178 .inner
179 .journaled_state
180 .load_account_delegated(inputs.bytecode_address, &mut self.inner.db)?;
181
182 let checkpoint = self.journaled_state.checkpoint();
184
185 match inputs.value {
187 CallValue::Transfer(value) if value.is_zero() => {
189 self.load_account(inputs.target_address)?;
190 self.journaled_state.touch(&inputs.target_address);
191 }
192 CallValue::Transfer(value) => {
193 if let Some(result) = self.inner.journaled_state.transfer(
196 &inputs.caller,
197 &inputs.target_address,
198 value,
199 &mut self.inner.db,
200 )? {
201 self.journaled_state.checkpoint_revert(checkpoint);
202 return return_result(result);
203 }
204 }
205 _ => {}
206 };
207
208 let is_ext_delegate = inputs.scheme.is_ext_delegate_call();
209
210 if !is_ext_delegate {
211 if let Some(result) =
212 self.call_precompile(&inputs.bytecode_address, &inputs.input, gas)?
213 {
214 if result.result.is_ok() {
215 self.journaled_state.checkpoint_commit();
216 } else {
217 self.journaled_state.checkpoint_revert(checkpoint);
218 }
219 return Ok(FrameOrResult::new_call_result(
220 result,
221 inputs.return_memory_offset.clone(),
222 ));
223 }
224 }
225 let account = self
227 .inner
228 .journaled_state
229 .load_code(inputs.bytecode_address, &mut self.inner.db)?;
230
231 let code_hash = account.info.code_hash();
232 let mut bytecode = account.info.code.clone().unwrap_or_default();
233
234 if is_ext_delegate && !bytecode.bytes_slice().starts_with(&EOF_MAGIC_BYTES) {
236 return return_result(InstructionResult::InvalidExtDelegateCallTarget);
237 }
238
239 if bytecode.is_empty() {
240 self.journaled_state.checkpoint_commit();
241 return return_result(InstructionResult::Stop);
242 }
243
244 if let Bytecode::Eip7702(eip7702_bytecode) = bytecode {
245 bytecode = self
246 .inner
247 .journaled_state
248 .load_code(eip7702_bytecode.delegated_address, &mut self.inner.db)?
249 .info
250 .code
251 .clone()
252 .unwrap_or_default();
253 }
254
255 let contract =
256 Contract::new_with_context(inputs.input.clone(), bytecode, Some(code_hash), inputs);
257 Ok(FrameOrResult::new_call_frame(
259 inputs.return_memory_offset.clone(),
260 checkpoint,
261 Interpreter::new(contract, gas.limit(), inputs.is_static),
262 ))
263 }
264
265 #[inline]
267 pub fn make_create_frame(
268 &mut self,
269 spec_id: SpecId,
270 inputs: &CreateInputs,
271 ) -> Result<FrameOrResult, EVMError<DB::Error>> {
272 let return_error = |e| {
273 Ok(FrameOrResult::new_create_result(
274 InterpreterResult {
275 result: e,
276 gas: Gas::new(inputs.gas_limit),
277 output: Bytes::new(),
278 },
279 None,
280 ))
281 };
282
283 if self.journaled_state.depth() > CALL_STACK_LIMIT {
285 return return_error(InstructionResult::CallTooDeep);
286 }
287
288 if spec_id.is_enabled_in(OSAKA) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) {
290 return return_error(InstructionResult::CreateInitCodeStartingEF00);
291 }
292
293 let caller_balance = self.balance(inputs.caller)?;
295
296 if caller_balance.data < inputs.value {
298 return return_error(InstructionResult::OutOfFunds);
299 }
300
301 let old_nonce;
303 if let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) {
304 old_nonce = nonce - 1;
305 } else {
306 return return_error(InstructionResult::Return);
307 }
308
309 let mut init_code_hash = B256::ZERO;
311 let created_address = match inputs.scheme {
312 CreateScheme::Create => inputs.caller.create(old_nonce),
313 CreateScheme::Create2 { salt } => {
314 init_code_hash = keccak256(&inputs.init_code);
315 inputs.caller.create2(salt.to_be_bytes(), init_code_hash)
316 }
317 };
318
319 if self.precompiles.contains(&created_address) {
321 return return_error(InstructionResult::CreateCollision);
322 }
323
324 self.load_account(created_address)?;
326
327 let checkpoint = match self.journaled_state.create_account_checkpoint(
329 inputs.caller,
330 created_address,
331 inputs.value,
332 spec_id,
333 ) {
334 Ok(checkpoint) => checkpoint,
335 Err(e) => {
336 return return_error(e);
337 }
338 };
339
340 let bytecode = Bytecode::new_legacy(inputs.init_code.clone());
341
342 let contract = Contract::new(
343 Bytes::new(),
344 bytecode,
345 Some(init_code_hash),
346 created_address,
347 None,
348 inputs.caller,
349 inputs.value,
350 );
351
352 Ok(FrameOrResult::new_create_frame(
353 created_address,
354 checkpoint,
355 Interpreter::new(contract, inputs.gas_limit, false),
356 ))
357 }
358
359 #[inline]
361 pub fn make_eofcreate_frame(
362 &mut self,
363 spec_id: SpecId,
364 inputs: &EOFCreateInputs,
365 ) -> Result<FrameOrResult, EVMError<DB::Error>> {
366 let return_error = |e| {
367 Ok(FrameOrResult::new_eofcreate_result(
368 InterpreterResult {
369 result: e,
370 gas: Gas::new(inputs.gas_limit),
371 output: Bytes::new(),
372 },
373 None,
374 ))
375 };
376
377 let (input, initcode, created_address) = match &inputs.kind {
378 EOFCreateKind::Opcode {
379 initcode,
380 input,
381 created_address,
382 } => (input.clone(), initcode.clone(), Some(*created_address)),
383 EOFCreateKind::Tx { initdata } => {
384 let Ok((eof, input)) = Eof::decode_dangling(initdata.clone()) else {
387 self.journaled_state.inc_nonce(inputs.caller);
388 return return_error(InstructionResult::InvalidEOFInitCode);
389 };
390
391 if validate_eof(&eof).is_err() {
392 self.journaled_state.inc_nonce(inputs.caller);
394 return return_error(InstructionResult::InvalidEOFInitCode);
395 }
396
397 let nonce = self
400 .env
401 .tx
402 .nonce
403 .map(|nonce| self.env.tx.caller.create(nonce));
404
405 (input, eof, nonce)
406 }
407 };
408
409 if self.journaled_state.depth() > CALL_STACK_LIMIT {
411 return return_error(InstructionResult::CallTooDeep);
412 }
413
414 let caller_balance = self.balance(inputs.caller)?;
416
417 if caller_balance.data < inputs.value {
419 return return_error(InstructionResult::OutOfFunds);
420 }
421
422 let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) else {
424 return return_error(InstructionResult::Return);
426 };
427 let old_nonce = nonce - 1;
428
429 let created_address = created_address.unwrap_or_else(|| inputs.caller.create(old_nonce));
430
431 if self.precompiles.contains(&created_address) {
433 return return_error(InstructionResult::CreateCollision);
434 }
435
436 self.load_account(created_address)?;
438
439 let checkpoint = match self.journaled_state.create_account_checkpoint(
441 inputs.caller,
442 created_address,
443 inputs.value,
444 spec_id,
445 ) {
446 Ok(checkpoint) => checkpoint,
447 Err(e) => {
448 return return_error(e);
449 }
450 };
451
452 let contract = Contract::new(
453 input.clone(),
454 Bytecode::Eof(Arc::new(initcode.clone())),
456 None,
457 created_address,
458 None,
459 inputs.caller,
460 inputs.value,
461 );
462
463 let mut interpreter = Interpreter::new(contract, inputs.gas_limit, false);
464 interpreter.set_is_eof_init();
466
467 Ok(FrameOrResult::new_eofcreate_frame(
468 created_address,
469 checkpoint,
470 interpreter,
471 ))
472 }
473}
474
475#[cfg(any(test, feature = "test-utils"))]
477pub(crate) mod test_utils {
478 use super::*;
479 use crate::primitives::U256;
480 use crate::{
481 db::{CacheDB, EmptyDB},
482 journaled_state::JournaledState,
483 primitives::{address, HashSet, SpecId, B256},
484 };
485
486 pub const MOCK_CALLER: Address = address!("0000000000000000000000000000000000000000");
488
489 pub fn create_mock_call_inputs(to: Address) -> CallInputs {
491 CallInputs {
492 input: Bytes::new(),
493 gas_limit: 0,
494 bytecode_address: to,
495 target_address: to,
496 caller: MOCK_CALLER,
497 value: CallValue::Transfer(U256::ZERO),
498 scheme: revm_interpreter::CallScheme::Call,
499 is_eof: false,
500 is_static: false,
501 return_memory_offset: 0..0,
502 }
503 }
504
505 pub fn create_cache_db_evm_context_with_balance(
509 env: Box<Env>,
510 mut db: CacheDB<EmptyDB>,
511 balance: U256,
512 ) -> EvmContext<CacheDB<EmptyDB>> {
513 db.insert_account_info(
514 test_utils::MOCK_CALLER,
515 crate::primitives::AccountInfo {
516 nonce: 0,
517 balance,
518 code_hash: B256::default(),
519 code: None,
520 },
521 );
522 create_cache_db_evm_context(env, db)
523 }
524
525 pub fn create_cache_db_evm_context(
527 env: Box<Env>,
528 db: CacheDB<EmptyDB>,
529 ) -> EvmContext<CacheDB<EmptyDB>> {
530 EvmContext {
531 inner: InnerEvmContext {
532 env,
533 journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()),
534 db,
535 error: Ok(()),
536 #[cfg(feature = "optimism")]
537 l1_block_info: None,
538 },
539 precompiles: ContextPrecompiles::default(),
540 }
541 }
542
543 pub fn create_empty_evm_context(env: Box<Env>, db: EmptyDB) -> EvmContext<EmptyDB> {
545 EvmContext {
546 inner: InnerEvmContext {
547 env,
548 journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()),
549 db,
550 error: Ok(()),
551 #[cfg(feature = "optimism")]
552 l1_block_info: None,
553 },
554 precompiles: ContextPrecompiles::default(),
555 }
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562 use crate::primitives::U256;
563 use crate::{
564 db::{CacheDB, EmptyDB},
565 primitives::{address, Bytecode},
566 Frame, JournalEntry,
567 };
568 use std::boxed::Box;
569 use test_utils::*;
570
571 #[test]
574 fn test_make_call_frame_stack_too_deep() {
575 let env = Env::default();
576 let db = EmptyDB::default();
577 let mut context = test_utils::create_empty_evm_context(Box::new(env), db);
578 context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1;
579 let contract = address!("dead10000000000000000000000000000001dead");
580 let call_inputs = test_utils::create_mock_call_inputs(contract);
581 let res = context.make_call_frame(&call_inputs);
582 let Ok(FrameOrResult::Result(err)) = res else {
583 panic!("Expected FrameOrResult::Result");
584 };
585 assert_eq!(
586 err.interpreter_result().result,
587 InstructionResult::CallTooDeep
588 );
589 }
590
591 #[test]
595 fn test_make_call_frame_transfer_revert() {
596 let env = Env::default();
597 let db = EmptyDB::default();
598 let mut evm_context = test_utils::create_empty_evm_context(Box::new(env), db);
599 let contract = address!("dead10000000000000000000000000000001dead");
600 let mut call_inputs = test_utils::create_mock_call_inputs(contract);
601 call_inputs.value = CallValue::Transfer(U256::from(1));
602 let res = evm_context.make_call_frame(&call_inputs);
603 let Ok(FrameOrResult::Result(result)) = res else {
604 panic!("Expected FrameOrResult::Result");
605 };
606 assert_eq!(
607 result.interpreter_result().result,
608 InstructionResult::OutOfFunds
609 );
610 let checkpointed = vec![vec![JournalEntry::AccountWarmed { address: contract }]];
611 assert_eq!(evm_context.journaled_state.journal, checkpointed);
612 assert_eq!(evm_context.journaled_state.depth, 0);
613 }
614
615 #[test]
616 fn test_make_call_frame_missing_code_context() {
617 let env = Env::default();
618 let cdb = CacheDB::new(EmptyDB::default());
619 let bal = U256::from(3_000_000_000_u128);
620 let mut context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal);
621 let contract = address!("dead10000000000000000000000000000001dead");
622 let call_inputs = test_utils::create_mock_call_inputs(contract);
623 let res = context.make_call_frame(&call_inputs);
624 let Ok(FrameOrResult::Result(result)) = res else {
625 panic!("Expected FrameOrResult::Result");
626 };
627 assert_eq!(result.interpreter_result().result, InstructionResult::Stop);
628 }
629
630 #[test]
631 fn test_make_call_frame_succeeds() {
632 let env = Env::default();
633 let mut cdb = CacheDB::new(EmptyDB::default());
634 let bal = U256::from(3_000_000_000_u128);
635 let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00]));
636 let contract = address!("dead10000000000000000000000000000001dead");
637 cdb.insert_account_info(
638 contract,
639 crate::primitives::AccountInfo {
640 nonce: 0,
641 balance: bal,
642 code_hash: by.clone().hash_slow(),
643 code: Some(by),
644 },
645 );
646 let mut evm_context = create_cache_db_evm_context_with_balance(Box::new(env), cdb, bal);
647 let call_inputs = test_utils::create_mock_call_inputs(contract);
648 let res = evm_context.make_call_frame(&call_inputs);
649 let Ok(FrameOrResult::Frame(Frame::Call(call_frame))) = res else {
650 panic!("Expected FrameOrResult::Frame(Frame::Call(..))");
651 };
652 assert_eq!(call_frame.return_memory_range, 0..0,);
653 }
654}