1#![deny(warnings)]
4#![forbid(unsafe_code, unused_variables)]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7extern crate alloc;
8
9#[cfg(feature = "tracing")]
10pub mod tracing;
11
12#[cfg(feature = "tracing")]
13macro_rules! event {
14 ($x:expr) => {
15 use crate::tracing::Event::*;
16 crate::tracing::with(|listener| listener.event($x));
17 };
18}
19
20#[cfg(not(feature = "tracing"))]
21macro_rules! event {
22 ($x:expr) => {};
23}
24
25mod context;
26mod eval;
27mod handler;
28mod interrupt;
29
30pub use evm_core::*;
31
32pub use crate::context::{CallScheme, Context, CreateScheme};
33pub use crate::handler::{Handler, Transfer};
34pub use crate::interrupt::{Resolve, ResolveCall, ResolveCreate};
35
36use alloc::rc::Rc;
37use alloc::vec::Vec;
38use primitive_types::{H160, U256};
39
40macro_rules! step {
41 ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({
42 if let Some((opcode, stack)) = $self.machine.inspect() {
43 event!(Step {
44 context: &$self.context,
45 opcode,
46 position: $self.machine.position(),
47 stack,
48 memory: $self.machine.memory()
49 });
50
51 match $handler.pre_validate(&$self.context, opcode, stack) {
52 Ok(()) => (),
53 Err(e) => {
54 $self.machine.exit(e.clone().into());
55 $self.status = Err(e.into());
56 },
57 }
58 }
59
60 match &$self.status {
61 Ok(()) => (),
62 Err(e) => {
63 #[allow(unused_parens)]
64 $return $($err)*(Capture::Exit(e.clone()))
65 },
66 }
67
68 let result = $self.machine.step();
69
70 event!(StepResult {
71 result: &result,
72 return_value: &$self.machine.return_value(),
73 });
74
75 match result {
76 Ok(()) => $($ok)?(()),
77 Err(Capture::Exit(e)) => {
78 $self.status = Err(e.clone());
79 #[allow(unused_parens)]
80 $return $($err)*(Capture::Exit(e))
81 },
82 Err(Capture::Trap(opcode)) => {
83 match eval::eval($self, opcode, $handler) {
84 eval::Control::Continue => $($ok)?(()),
85 eval::Control::CallInterrupt(interrupt) => {
86 let resolve = ResolveCall::new($self);
87 #[allow(unused_parens)]
88 $return $($err)*(Capture::Trap(Resolve::Call(interrupt, resolve)))
89 },
90 eval::Control::CreateInterrupt(interrupt) => {
91 let resolve = ResolveCreate::new($self);
92 #[allow(unused_parens)]
93 $return $($err)*(Capture::Trap(Resolve::Create(interrupt, resolve)))
94 },
95 eval::Control::Exit(exit) => {
96 $self.machine.exit(exit.clone().into());
97 $self.status = Err(exit.clone());
98 #[allow(unused_parens)]
99 $return $($err)*(Capture::Exit(exit))
100 },
101 }
102 },
103 }
104 });
105}
106
107pub struct Runtime {
111 machine: Machine,
112 status: Result<(), ExitReason>,
113 return_data_buffer: Vec<u8>,
114 return_data_len: U256,
115 return_data_offset: U256,
116 context: Context,
117}
118
119impl Runtime {
120 pub fn new(
122 code: Rc<Vec<u8>>,
123 data: Rc<Vec<u8>>,
124 context: Context,
125 stack_limit: usize,
126 memory_limit: usize,
127 ) -> Self {
128 Self {
129 machine: Machine::new(code, data, stack_limit, memory_limit),
130 status: Ok(()),
131 return_data_buffer: Vec::new(),
132 return_data_len: U256::zero(),
133 return_data_offset: U256::zero(),
134 context,
135 }
136 }
137
138 pub fn machine(&self) -> &Machine {
140 &self.machine
141 }
142
143 pub fn context(&self) -> &Context {
145 &self.context
146 }
147
148 pub fn step<'a, H: Handler>(
150 &'a mut self,
151 handler: &mut H,
152 ) -> Result<(), Capture<ExitReason, Resolve<'a, H>>> {
153 step!(self, handler, return Err; Ok)
154 }
155
156 pub fn run<'a, H: Handler>(
158 &'a mut self,
159 handler: &mut H,
160 ) -> Capture<ExitReason, Resolve<'a, H>> {
161 loop {
162 step!(self, handler, return;)
163 }
164 }
165
166 pub fn finish_create(
167 &mut self,
168 reason: ExitReason,
169 address: Option<H160>,
170 return_data: Vec<u8>,
171 ) -> Result<(), ExitReason> {
172 eval::finish_create(self, reason, address, return_data)
173 }
174
175 pub fn finish_call(
176 &mut self,
177 reason: ExitReason,
178 return_data: Vec<u8>,
179 ) -> Result<(), ExitReason> {
180 eval::finish_call(
181 self,
182 self.return_data_len,
183 self.return_data_offset,
184 reason,
185 return_data,
186 )
187 }
188}
189
190#[derive(Clone, Debug)]
192pub struct Config {
193 pub gas_ext_code: u64,
195 pub gas_ext_code_hash: u64,
197 pub gas_sstore_set: u64,
199 pub gas_sstore_reset: u64,
201 pub refund_sstore_clears: i64,
203 pub max_refund_quotient: u64,
205 pub gas_balance: u64,
207 pub gas_sload: u64,
209 pub gas_sload_cold: u64,
211 pub gas_suicide: u64,
213 pub gas_suicide_new_account: u64,
215 pub gas_call: u64,
217 pub gas_expbyte: u64,
219 pub gas_transaction_create: u64,
221 pub gas_transaction_call: u64,
223 pub gas_transaction_zero_data: u64,
225 pub gas_transaction_non_zero_data: u64,
227 pub gas_access_list_address: u64,
229 pub gas_access_list_storage_key: u64,
231 pub gas_account_access_cold: u64,
233 pub gas_storage_read_warm: u64,
235 pub sstore_gas_metering: bool,
237 pub sstore_revert_under_stipend: bool,
239 pub increase_state_access_gas: bool,
241 pub decrease_clears_refund: bool,
243 pub disallow_executable_format: bool,
245 pub warm_coinbase_address: bool,
247 pub gas_auth_base_cost: u64,
249 pub gas_per_empty_account_cost: u64,
251 pub err_on_call_with_more_gas: bool,
255 pub call_l64_after_gas: bool,
257 pub empty_considered_exists: bool,
259 pub create_increase_nonce: bool,
261 pub stack_limit: usize,
263 pub memory_limit: usize,
265 pub call_stack_limit: usize,
267 pub create_contract_limit: Option<usize>,
269 pub max_initcode_size: Option<usize>,
271 pub call_stipend: u64,
273 pub has_delegate_call: bool,
275 pub has_create2: bool,
277 pub has_revert: bool,
279 pub has_return_data: bool,
281 pub has_bitwise_shifting: bool,
283 pub has_chain_id: bool,
285 pub has_self_balance: bool,
287 pub has_ext_code_hash: bool,
289 pub has_base_fee: bool,
291 pub has_push0: bool,
293 pub has_tloadstore: bool,
295 pub has_mcopy: bool,
297 pub estimate: bool,
299 pub has_eip_6780: bool,
301 pub has_eip_7702: bool,
303}
304
305impl Config {
306 pub const fn frontier() -> Config {
308 Config {
309 gas_ext_code: 20,
310 gas_ext_code_hash: 20,
311 gas_balance: 20,
312 gas_sload: 50,
313 gas_sload_cold: 0,
314 gas_sstore_set: 20000,
315 gas_sstore_reset: 5000,
316 refund_sstore_clears: 15000,
317 max_refund_quotient: 2,
318 gas_suicide: 0,
319 gas_suicide_new_account: 0,
320 gas_call: 40,
321 gas_expbyte: 10,
322 gas_transaction_create: 21000,
323 gas_transaction_call: 21000,
324 gas_transaction_zero_data: 4,
325 gas_transaction_non_zero_data: 68,
326 gas_access_list_address: 0,
327 gas_access_list_storage_key: 0,
328 gas_account_access_cold: 0,
329 gas_storage_read_warm: 0,
330 sstore_gas_metering: false,
331 sstore_revert_under_stipend: false,
332 increase_state_access_gas: false,
333 decrease_clears_refund: false,
334 disallow_executable_format: false,
335 warm_coinbase_address: false,
336 err_on_call_with_more_gas: true,
337 empty_considered_exists: true,
338 create_increase_nonce: false,
339 call_l64_after_gas: false,
340 stack_limit: 1024,
341 memory_limit: usize::MAX,
342 call_stack_limit: 1024,
343 create_contract_limit: None,
344 max_initcode_size: None,
345 call_stipend: 2300,
346 has_delegate_call: false,
347 has_create2: false,
348 has_revert: false,
349 has_return_data: false,
350 has_bitwise_shifting: false,
351 has_chain_id: false,
352 has_self_balance: false,
353 has_ext_code_hash: false,
354 has_base_fee: false,
355 has_push0: false,
356 has_tloadstore: false,
357 has_mcopy: false,
358 estimate: false,
359 has_eip_6780: false,
360 has_eip_7702: false,
361 gas_auth_base_cost: 0,
362 gas_per_empty_account_cost: 0,
363 }
364 }
365
366 pub const fn istanbul() -> Config {
368 Config {
369 gas_ext_code: 700,
370 gas_ext_code_hash: 700,
371 gas_balance: 700,
372 gas_sload: 800,
373 gas_sload_cold: 0,
374 gas_sstore_set: 20000,
375 gas_sstore_reset: 5000,
376 refund_sstore_clears: 15000,
377 max_refund_quotient: 2,
378 gas_suicide: 5000,
379 gas_suicide_new_account: 25000,
380 gas_call: 700,
381 gas_expbyte: 50,
382 gas_transaction_create: 53000,
383 gas_transaction_call: 21000,
384 gas_transaction_zero_data: 4,
385 gas_transaction_non_zero_data: 16,
386 gas_access_list_address: 0,
387 gas_access_list_storage_key: 0,
388 gas_account_access_cold: 0,
389 gas_storage_read_warm: 0,
390 sstore_gas_metering: true,
391 sstore_revert_under_stipend: true,
392 increase_state_access_gas: false,
393 decrease_clears_refund: false,
394 disallow_executable_format: false,
395 warm_coinbase_address: false,
396 err_on_call_with_more_gas: false,
397 empty_considered_exists: false,
398 create_increase_nonce: true,
399 call_l64_after_gas: true,
400 stack_limit: 1024,
401 memory_limit: usize::MAX,
402 call_stack_limit: 1024,
403 create_contract_limit: Some(0x6000),
404 max_initcode_size: None,
405 call_stipend: 2300,
406 has_delegate_call: true,
407 has_create2: true,
408 has_revert: true,
409 has_return_data: true,
410 has_bitwise_shifting: true,
411 has_chain_id: true,
412 has_self_balance: true,
413 has_ext_code_hash: true,
414 has_base_fee: false,
415 has_push0: false,
416 has_tloadstore: false,
417 has_mcopy: false,
418 estimate: false,
419 has_eip_6780: false,
420 has_eip_7702: false,
421 gas_auth_base_cost: 0,
422 gas_per_empty_account_cost: 0,
423 }
424 }
425
426 pub const fn berlin() -> Config {
428 Self::config_with_derived_values(DerivedConfigInputs::berlin())
429 }
430
431 pub const fn london() -> Config {
433 Self::config_with_derived_values(DerivedConfigInputs::london())
434 }
435
436 pub const fn merge() -> Config {
438 Self::config_with_derived_values(DerivedConfigInputs::merge())
439 }
440
441 pub const fn shanghai() -> Config {
443 Self::config_with_derived_values(DerivedConfigInputs::shanghai())
444 }
445
446 pub const fn cancun() -> Config {
448 Self::config_with_derived_values(DerivedConfigInputs::cancun())
449 }
450
451 pub const fn pectra() -> Config {
453 Self::config_with_derived_values(DerivedConfigInputs::pectra())
454 }
455
456 const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Config {
457 let DerivedConfigInputs {
458 gas_storage_read_warm,
459 gas_sload_cold,
460 gas_access_list_storage_key,
461 decrease_clears_refund,
462 has_base_fee,
463 has_push0,
464 disallow_executable_format,
465 warm_coinbase_address,
466 max_initcode_size,
467 has_eip_6780,
468 has_tloadstore,
469 has_mcopy,
470 has_eip_7702,
471 gas_auth_base_cost,
472 gas_per_empty_account_cost,
473 } = inputs;
474
475 let gas_sload = gas_storage_read_warm;
477 let gas_sstore_reset = 5000 - gas_sload_cold;
478
479 let refund_sstore_clears = if decrease_clears_refund {
481 (gas_sstore_reset + gas_access_list_storage_key) as i64
482 } else {
483 15000
484 };
485 let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
486
487 Config {
488 gas_ext_code: 0,
489 gas_ext_code_hash: 0,
490 gas_balance: 0,
491 gas_sload,
492 gas_sload_cold,
493 gas_sstore_set: 20000,
494 gas_sstore_reset,
495 refund_sstore_clears,
496 max_refund_quotient,
497 gas_suicide: 5000,
498 gas_suicide_new_account: 25000,
499 gas_call: 0,
500 gas_expbyte: 50,
501 gas_transaction_create: 53000,
502 gas_transaction_call: 21000,
503 gas_transaction_zero_data: 4,
504 gas_transaction_non_zero_data: 16,
505 gas_access_list_address: 2400,
506 gas_access_list_storage_key,
507 gas_account_access_cold: 2600,
508 gas_storage_read_warm,
509 sstore_gas_metering: true,
510 sstore_revert_under_stipend: true,
511 increase_state_access_gas: true,
512 decrease_clears_refund,
513 disallow_executable_format,
514 warm_coinbase_address,
515 err_on_call_with_more_gas: false,
516 empty_considered_exists: false,
517 create_increase_nonce: true,
518 call_l64_after_gas: true,
519 stack_limit: 1024,
520 memory_limit: usize::MAX,
521 call_stack_limit: 1024,
522 create_contract_limit: Some(0x6000),
523 max_initcode_size,
524 call_stipend: 2300,
525 has_delegate_call: true,
526 has_create2: true,
527 has_revert: true,
528 has_return_data: true,
529 has_bitwise_shifting: true,
530 has_chain_id: true,
531 has_self_balance: true,
532 has_ext_code_hash: true,
533 has_base_fee,
534 has_push0,
535 estimate: false,
536 has_eip_6780,
537 has_tloadstore,
538 has_mcopy,
539 has_eip_7702,
540 gas_auth_base_cost,
541 gas_per_empty_account_cost,
542 }
543 }
544}
545
546struct DerivedConfigInputs {
549 gas_storage_read_warm: u64,
550 gas_sload_cold: u64,
551 gas_access_list_storage_key: u64,
552 decrease_clears_refund: bool,
553 has_base_fee: bool,
554 has_push0: bool,
555 disallow_executable_format: bool,
556 warm_coinbase_address: bool,
557 max_initcode_size: Option<usize>,
558 has_eip_6780: bool,
559 has_tloadstore: bool,
560 has_mcopy: bool,
561 has_eip_7702: bool,
562 gas_auth_base_cost: u64,
563 gas_per_empty_account_cost: u64,
564}
565
566impl DerivedConfigInputs {
567 const fn berlin() -> Self {
568 Self {
569 gas_storage_read_warm: 100,
570 gas_sload_cold: 2100,
571 gas_access_list_storage_key: 1900,
572 decrease_clears_refund: false,
573 has_base_fee: false,
574 has_push0: false,
575 disallow_executable_format: false,
576 warm_coinbase_address: false,
577 max_initcode_size: None,
578 has_eip_6780: false,
579 has_tloadstore: false,
580 has_mcopy: false,
581 has_eip_7702: false,
582 gas_auth_base_cost: 0,
583 gas_per_empty_account_cost: 0,
584 }
585 }
586
587 const fn london() -> Self {
588 Self {
589 gas_storage_read_warm: 100,
590 gas_sload_cold: 2100,
591 gas_access_list_storage_key: 1900,
592 decrease_clears_refund: true,
593 has_base_fee: true,
594 has_push0: false,
595 disallow_executable_format: true,
596 warm_coinbase_address: false,
597 max_initcode_size: None,
598 has_eip_6780: false,
599 has_tloadstore: false,
600 has_mcopy: false,
601 has_eip_7702: false,
602 gas_auth_base_cost: 0,
603 gas_per_empty_account_cost: 0,
604 }
605 }
606
607 const fn merge() -> Self {
608 Self {
609 gas_storage_read_warm: 100,
610 gas_sload_cold: 2100,
611 gas_access_list_storage_key: 1900,
612 decrease_clears_refund: true,
613 has_base_fee: true,
614 has_push0: false,
615 disallow_executable_format: true,
616 warm_coinbase_address: false,
617 max_initcode_size: None,
618 has_eip_6780: false,
619 has_tloadstore: false,
620 has_mcopy: false,
621 has_eip_7702: false,
622 gas_auth_base_cost: 0,
623 gas_per_empty_account_cost: 0,
624 }
625 }
626
627 const fn shanghai() -> Self {
628 Self {
629 gas_storage_read_warm: 100,
630 gas_sload_cold: 2100,
631 gas_access_list_storage_key: 1900,
632 decrease_clears_refund: true,
633 has_base_fee: true,
634 has_push0: true,
635 disallow_executable_format: true,
636 warm_coinbase_address: true,
637 max_initcode_size: Some(0xC000),
639 has_eip_6780: false,
640 has_tloadstore: false,
641 has_mcopy: false,
642 has_eip_7702: false,
643 gas_auth_base_cost: 0,
644 gas_per_empty_account_cost: 0,
645 }
646 }
647
648 const fn cancun() -> Self {
649 Self {
650 gas_storage_read_warm: 100,
651 gas_sload_cold: 2100,
652 gas_access_list_storage_key: 1900,
653 decrease_clears_refund: true,
654 has_base_fee: true,
655 has_push0: true,
656 disallow_executable_format: true,
657 warm_coinbase_address: true,
658 max_initcode_size: Some(0xC000),
660 has_eip_6780: true,
661 has_tloadstore: true,
662 has_mcopy: true,
663 has_eip_7702: false,
664 gas_auth_base_cost: 0,
665 gas_per_empty_account_cost: 0,
666 }
667 }
668
669 const fn pectra() -> Self {
671 Self {
672 gas_storage_read_warm: 100,
673 gas_sload_cold: 2100,
674 gas_access_list_storage_key: 1900,
675 decrease_clears_refund: true,
676 has_base_fee: true,
677 has_push0: true,
678 disallow_executable_format: true,
679 warm_coinbase_address: true,
680 max_initcode_size: Some(0xC000),
682 has_eip_6780: true,
683 has_tloadstore: true,
684 has_mcopy: true,
685 has_eip_7702: true,
686 gas_auth_base_cost: 12500,
688 gas_per_empty_account_cost: 25000,
690 }
691 }
692}