1#[cfg(not(feature = "std"))]
4pub mod prelude {
5 pub use alloc::{rc::Rc, vec::Vec};
6}
7#[cfg(feature = "std")]
8pub mod prelude {
9 pub use std::{rc::Rc, vec::Vec};
10}
11
12#[cfg(feature = "tracing")]
13pub mod tracing;
14
15#[cfg(feature = "tracing")]
16macro_rules! event {
17 ($x:expr) => {
18 use crate::runtime::tracing::Event::*;
19 crate::runtime::tracing::with(|listener| listener.event($x));
20 };
21}
22
23#[cfg(not(feature = "tracing"))]
24macro_rules! event {
25 ($x:expr) => {};
26}
27
28mod context;
29mod eval;
30mod handler;
31mod interrupt;
32
33pub use crate::core::*;
34
35pub use self::context::{CallScheme, Context, CreateScheme};
36pub use self::handler::{Handler, Transfer};
37pub use self::interrupt::{Resolve, ResolveCall, ResolveCreate};
38
39use prelude::*;
40use primitive_types::H160;
41
42macro_rules! step {
43 ( $self:expr, $handler:expr, $return:tt $($err:path)?; $($ok:path)? ) => ({
44 let result = $self.machine.step($handler, &$self.context.address);
45 match result {
46 Ok(()) => $($ok)?(()),
47 Err(Capture::Exit(e)) => {
48 $self.status = Err(e.clone());
49 #[allow(unused_parens)]
50 $return $($err)*(Capture::Exit(e))
51 },
52 Err(Capture::Trap(opcode)) => {
53 match eval::eval($self, opcode, $handler) {
54 eval::Control::Continue => $($ok)?(()),
55 eval::Control::CallInterrupt(interrupt) => {
56 let resolve = ResolveCall::new($self);
57 #[allow(unused_parens)]
58 $return $($err)*(Capture::Trap(Resolve::Call(interrupt, resolve)))
59 },
60 eval::Control::CreateInterrupt(interrupt) => {
61 let resolve = ResolveCreate::new($self);
62 #[allow(unused_parens)]
63 $return $($err)*(Capture::Trap(Resolve::Create(interrupt, resolve)))
64 },
65 eval::Control::Exit(exit) => {
66 $self.machine.exit(exit.clone().into());
67 $self.status = Err(exit.clone());
68 #[allow(unused_parens)]
69 $return $($err)*(Capture::Exit(exit))
70 },
71 }
72 },
73 }
74 });
75}
76
77pub struct Runtime {
81 machine: Machine,
82 status: Result<(), ExitReason>,
83 return_data_buffer: Vec<u8>,
84 return_data_len: usize,
85 return_data_offset: usize,
86 context: Context,
87}
88
89impl Runtime {
90 #[must_use]
92 pub fn new(
93 code: Rc<Vec<u8>>,
94 data: Rc<Vec<u8>>,
95 context: Context,
96 stack_limit: usize,
97 memory_limit: usize,
98 ) -> Self {
99 Self {
100 machine: Machine::new(code, data, stack_limit, memory_limit),
101 status: Ok(()),
102 return_data_buffer: Vec::new(),
103 return_data_len: 0,
104 return_data_offset: 0,
105 context,
106 }
107 }
108
109 #[must_use]
111 pub const fn machine(&self) -> &Machine {
112 &self.machine
113 }
114
115 #[must_use]
117 pub const fn context(&self) -> &Context {
118 &self.context
119 }
120
121 pub fn step<H: Handler + InterpreterHandler>(
126 &mut self,
127 handler: &mut H,
128 ) -> Result<(), Capture<ExitReason, Resolve<H>>> {
129 step!(self, handler, return Err; Ok)
130 }
131
132 pub fn run<H: Handler + InterpreterHandler>(
134 &mut self,
135 handler: &mut H,
136 ) -> Capture<ExitReason, Resolve<H>> {
137 loop {
138 step!(self, handler, return;);
139 }
140 }
141
142 pub fn finish_create(
145 &mut self,
146 reason: ExitReason,
147 address: Option<H160>,
148 return_data: Vec<u8>,
149 ) -> Result<(), ExitReason> {
150 eval::finish_create(self, reason, address, return_data)
151 }
152
153 pub fn finish_call(
156 &mut self,
157 reason: ExitReason,
158 return_data: Vec<u8>,
159 ) -> Result<(), ExitReason> {
160 eval::finish_call(
161 self,
162 self.return_data_len,
163 self.return_data_offset,
164 reason,
165 return_data,
166 )
167 }
168}
169
170#[allow(clippy::struct_excessive_bools)]
172#[derive(Clone, Debug)]
173pub struct Config {
174 pub gas_ext_code: u64,
176 pub gas_ext_code_hash: u64,
178 pub gas_sstore_set: u64,
180 pub gas_sstore_reset: u64,
182 pub refund_sstore_clears: i64,
184 pub max_refund_quotient: u64,
186 pub gas_balance: u64,
188 pub gas_sload: u64,
190 pub gas_sload_cold: u64,
192 pub gas_suicide: u64,
194 pub gas_suicide_new_account: u64,
196 pub gas_call: u64,
198 pub gas_expbyte: u64,
200 pub gas_transaction_create: u64,
202 pub gas_transaction_call: u64,
204 pub gas_transaction_zero_data: u64,
206 pub gas_transaction_non_zero_data: u64,
208 pub gas_access_list_address: u64,
210 pub gas_access_list_storage_key: u64,
212 pub gas_account_access_cold: u64,
214 pub gas_storage_read_warm: u64,
216 pub sstore_gas_metering: bool,
218 pub sstore_revert_under_stipend: bool,
220 pub increase_state_access_gas: bool,
222 pub decrease_clears_refund: bool,
224 pub disallow_executable_format: bool,
226 pub warm_coinbase_address: bool,
228 pub err_on_call_with_more_gas: bool,
232 pub call_l64_after_gas: bool,
234 pub empty_considered_exists: bool,
236 pub create_increase_nonce: bool,
238 pub stack_limit: usize,
240 pub memory_limit: usize,
242 pub call_stack_limit: usize,
244 pub create_contract_limit: Option<usize>,
246 pub max_initcode_size: Option<usize>,
248 pub call_stipend: u64,
250 pub has_delegate_call: bool,
252 pub has_create2: bool,
254 pub has_revert: bool,
256 pub has_return_data: bool,
258 pub has_bitwise_shifting: bool,
260 pub has_chain_id: bool,
262 pub has_self_balance: bool,
264 pub has_ext_code_hash: bool,
266 pub has_base_fee: bool,
268 pub has_push0: bool,
270 pub estimate: bool,
272 pub has_blob_base_fee: bool,
274 pub has_shard_blob_transactions: bool,
276 pub has_transient_storage: bool,
278 pub has_mcopy: bool,
280 pub has_restricted_selfdestruct: bool,
282 pub has_authorization_list: bool,
284 pub gas_per_empty_account_cost: u64,
286 pub gas_per_auth_base_cost: u64,
288 pub has_floor_gas: bool,
290 pub total_cost_floor_per_token: u64,
292}
293
294impl Config {
295 #[must_use]
297 pub const fn frontier() -> Self {
298 Self {
299 gas_ext_code: 20,
300 gas_ext_code_hash: 20,
301 gas_balance: 20,
302 gas_sload: 50,
303 gas_sload_cold: 0,
304 gas_sstore_set: 20000,
305 gas_sstore_reset: 5000,
306 refund_sstore_clears: 15000,
307 max_refund_quotient: 2,
308 gas_suicide: 0,
309 gas_suicide_new_account: 0,
310 gas_call: 40,
311 gas_expbyte: 10,
312 gas_transaction_create: 21000,
313 gas_transaction_call: 21000,
314 gas_transaction_zero_data: 4,
315 gas_transaction_non_zero_data: 68,
316 gas_access_list_address: 0,
317 gas_access_list_storage_key: 0,
318 gas_account_access_cold: 0,
319 gas_storage_read_warm: 0,
320 sstore_gas_metering: false,
321 sstore_revert_under_stipend: false,
322 increase_state_access_gas: false,
323 decrease_clears_refund: false,
324 disallow_executable_format: false,
325 warm_coinbase_address: false,
326 err_on_call_with_more_gas: true,
327 empty_considered_exists: true,
328 create_increase_nonce: false,
329 call_l64_after_gas: false,
330 stack_limit: 1024,
331 memory_limit: usize::MAX,
332 call_stack_limit: 1024,
333 create_contract_limit: None,
334 max_initcode_size: None,
335 call_stipend: 2300,
336 has_delegate_call: false,
337 has_create2: false,
338 has_revert: false,
339 has_return_data: false,
340 has_bitwise_shifting: false,
341 has_chain_id: false,
342 has_self_balance: false,
343 has_ext_code_hash: false,
344 has_base_fee: false,
345 has_push0: false,
346 estimate: false,
347 has_blob_base_fee: false,
348 has_shard_blob_transactions: false,
349 has_transient_storage: false,
350 has_mcopy: false,
351 has_restricted_selfdestruct: false,
352 has_authorization_list: false,
353 gas_per_empty_account_cost: 0,
354 gas_per_auth_base_cost: 0,
355 has_floor_gas: false,
356 total_cost_floor_per_token: 0,
357 }
358 }
359
360 #[must_use]
362 pub const fn istanbul() -> Self {
363 Self {
364 gas_ext_code: 700,
365 gas_ext_code_hash: 700,
366 gas_balance: 700,
367 gas_sload: 800,
368 gas_sload_cold: 0,
369 gas_sstore_set: 20000,
370 gas_sstore_reset: 5000,
371 refund_sstore_clears: 15000,
372 max_refund_quotient: 2,
373 gas_suicide: 5000,
374 gas_suicide_new_account: 25000,
375 gas_call: 700,
376 gas_expbyte: 50,
377 gas_transaction_create: 53000,
378 gas_transaction_call: 21000,
379 gas_transaction_zero_data: 4,
380 gas_transaction_non_zero_data: 16,
381 gas_access_list_address: 0,
382 gas_access_list_storage_key: 0,
383 gas_account_access_cold: 0,
384 gas_storage_read_warm: 0,
385 sstore_gas_metering: true,
386 sstore_revert_under_stipend: true,
387 increase_state_access_gas: false,
388 decrease_clears_refund: false,
389 disallow_executable_format: false,
390 warm_coinbase_address: false,
391 err_on_call_with_more_gas: false,
392 empty_considered_exists: false,
393 create_increase_nonce: true,
394 call_l64_after_gas: true,
395 stack_limit: 1024,
396 memory_limit: usize::MAX,
397 call_stack_limit: 1024,
398 create_contract_limit: Some(0x6000),
399 max_initcode_size: None,
400 call_stipend: 2300,
401 has_delegate_call: true,
402 has_create2: true,
403 has_revert: true,
404 has_return_data: true,
405 has_bitwise_shifting: true,
406 has_chain_id: true,
407 has_self_balance: true,
408 has_ext_code_hash: true,
409 has_base_fee: false,
410 has_push0: false,
411 estimate: false,
412 has_blob_base_fee: false,
413 has_shard_blob_transactions: false,
414 has_transient_storage: false,
415 has_mcopy: false,
416 has_restricted_selfdestruct: false,
417 has_authorization_list: false,
418 gas_per_auth_base_cost: 0,
419 gas_per_empty_account_cost: 0,
420 has_floor_gas: false,
421 total_cost_floor_per_token: 0,
422 }
423 }
424
425 #[must_use]
427 pub const fn berlin() -> Self {
428 Self::config_with_derived_values(DerivedConfigInputs::berlin())
429 }
430
431 #[must_use]
433 pub const fn london() -> Self {
434 Self::config_with_derived_values(DerivedConfigInputs::london())
435 }
436
437 #[must_use]
439 pub const fn merge() -> Self {
440 Self::config_with_derived_values(DerivedConfigInputs::merge())
441 }
442
443 #[must_use]
445 pub const fn shanghai() -> Self {
446 Self::config_with_derived_values(DerivedConfigInputs::shanghai())
447 }
448
449 #[must_use]
451 pub const fn cancun() -> Self {
452 Self::config_with_derived_values(DerivedConfigInputs::cancun())
453 }
454
455 #[must_use]
457 pub const fn prague() -> Self {
458 Self::config_with_derived_values(DerivedConfigInputs::prague())
459 }
460
461 const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self {
462 let DerivedConfigInputs {
463 gas_storage_read_warm,
464 gas_sload_cold,
465 gas_access_list_storage_key,
466 decrease_clears_refund,
467 has_base_fee,
468 has_push0,
469 disallow_executable_format,
470 warm_coinbase_address,
471 max_initcode_size,
472 has_blob_base_fee,
473 has_shard_blob_transactions,
474 has_transient_storage,
475 has_mcopy,
476 has_restricted_selfdestruct,
477 has_authorization_list,
478 gas_per_empty_account_cost,
479 gas_per_auth_base_cost,
480 has_floor_gas,
481 total_cost_floor_per_token,
482 } = inputs;
483
484 let gas_sload = gas_storage_read_warm;
486 let gas_sstore_reset = 5000 - gas_sload_cold;
487
488 #[allow(clippy::as_conversions, clippy::cast_possible_wrap)]
490 let refund_sstore_clears = if decrease_clears_refund {
492 (gas_sstore_reset + gas_access_list_storage_key) as i64
493 } else {
494 15000
495 };
496 let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
497
498 Self {
499 gas_ext_code: 0,
500 gas_ext_code_hash: 0,
501 gas_balance: 0,
502 gas_sload,
503 gas_sload_cold,
504 gas_sstore_set: 20000,
505 gas_sstore_reset,
506 refund_sstore_clears,
507 max_refund_quotient,
508 gas_suicide: 5000,
509 gas_suicide_new_account: 25000,
510 gas_call: 0,
511 gas_expbyte: 50,
512 gas_transaction_create: 53000,
513 gas_transaction_call: 21000,
514 gas_transaction_zero_data: 4,
515 gas_transaction_non_zero_data: 16,
516 gas_access_list_address: 2400,
517 gas_access_list_storage_key,
518 gas_account_access_cold: 2600,
519 gas_storage_read_warm,
520 sstore_gas_metering: true,
521 sstore_revert_under_stipend: true,
522 increase_state_access_gas: true,
523 decrease_clears_refund,
524 disallow_executable_format,
525 warm_coinbase_address,
526 err_on_call_with_more_gas: false,
527 empty_considered_exists: false,
528 create_increase_nonce: true,
529 call_l64_after_gas: true,
530 stack_limit: 1024,
531 memory_limit: usize::MAX,
532 call_stack_limit: 1024,
533 create_contract_limit: Some(0x6000),
534 max_initcode_size,
535 call_stipend: 2300,
536 has_delegate_call: true,
537 has_create2: true,
538 has_revert: true,
539 has_return_data: true,
540 has_bitwise_shifting: true,
541 has_chain_id: true,
542 has_self_balance: true,
543 has_ext_code_hash: true,
544 has_base_fee,
545 has_push0,
546 estimate: false,
547 has_blob_base_fee,
548 has_shard_blob_transactions,
549 has_transient_storage,
550 has_mcopy,
551 has_restricted_selfdestruct,
552 has_authorization_list,
553 gas_per_empty_account_cost,
554 gas_per_auth_base_cost,
555 has_floor_gas,
556 total_cost_floor_per_token,
557 }
558 }
559}
560
561#[allow(clippy::struct_excessive_bools)]
564#[derive(Debug, Copy, Clone, Eq, PartialEq)]
565struct DerivedConfigInputs {
566 gas_storage_read_warm: u64,
567 gas_sload_cold: u64,
568 gas_access_list_storage_key: u64,
569 decrease_clears_refund: bool,
570 has_base_fee: bool,
571 has_push0: bool,
572 disallow_executable_format: bool,
573 warm_coinbase_address: bool,
574 max_initcode_size: Option<usize>,
575 has_blob_base_fee: bool,
576 has_shard_blob_transactions: bool,
577 has_transient_storage: bool,
578 has_mcopy: bool,
579 has_restricted_selfdestruct: bool,
580 has_authorization_list: bool,
581 gas_per_empty_account_cost: u64,
582 gas_per_auth_base_cost: u64,
583 has_floor_gas: bool,
584 total_cost_floor_per_token: u64,
585}
586
587impl DerivedConfigInputs {
588 const fn berlin() -> Self {
589 Self {
590 gas_storage_read_warm: 100,
591 gas_sload_cold: 2100,
592 gas_access_list_storage_key: 1900,
593 decrease_clears_refund: false,
594 has_base_fee: false,
595 has_push0: false,
596 disallow_executable_format: false,
597 warm_coinbase_address: false,
598 max_initcode_size: None,
599 has_blob_base_fee: false,
600 has_shard_blob_transactions: false,
601 has_transient_storage: false,
602 has_mcopy: false,
603 has_restricted_selfdestruct: false,
604 has_authorization_list: false,
605 gas_per_auth_base_cost: 0,
606 gas_per_empty_account_cost: 0,
607 has_floor_gas: false,
608 total_cost_floor_per_token: 0,
609 }
610 }
611
612 const fn london() -> Self {
613 Self {
614 gas_storage_read_warm: 100,
615 gas_sload_cold: 2100,
616 gas_access_list_storage_key: 1900,
617 decrease_clears_refund: true,
618 has_base_fee: true,
619 has_push0: false,
620 disallow_executable_format: true,
621 warm_coinbase_address: false,
622 max_initcode_size: None,
623 has_blob_base_fee: false,
624 has_shard_blob_transactions: false,
625 has_transient_storage: false,
626 has_mcopy: false,
627 has_restricted_selfdestruct: false,
628 has_authorization_list: false,
629 gas_per_auth_base_cost: 0,
630 gas_per_empty_account_cost: 0,
631 has_floor_gas: false,
632 total_cost_floor_per_token: 0,
633 }
634 }
635
636 const fn merge() -> Self {
637 Self {
638 gas_storage_read_warm: 100,
639 gas_sload_cold: 2100,
640 gas_access_list_storage_key: 1900,
641 decrease_clears_refund: true,
642 has_base_fee: true,
643 has_push0: false,
644 disallow_executable_format: true,
645 warm_coinbase_address: false,
646 max_initcode_size: None,
647 has_blob_base_fee: false,
648 has_shard_blob_transactions: false,
649 has_transient_storage: false,
650 has_mcopy: false,
651 has_restricted_selfdestruct: false,
652 has_authorization_list: false,
653 gas_per_auth_base_cost: 0,
654 gas_per_empty_account_cost: 0,
655 has_floor_gas: false,
656 total_cost_floor_per_token: 0,
657 }
658 }
659
660 const fn shanghai() -> Self {
661 Self {
662 gas_storage_read_warm: 100,
663 gas_sload_cold: 2100,
664 gas_access_list_storage_key: 1900,
665 decrease_clears_refund: true,
666 has_base_fee: true,
667 has_push0: true,
668 disallow_executable_format: true,
669 warm_coinbase_address: true,
670 max_initcode_size: Some(0xC000),
672 has_blob_base_fee: false,
673 has_shard_blob_transactions: false,
674 has_transient_storage: false,
675 has_mcopy: false,
676 has_restricted_selfdestruct: false,
677 has_authorization_list: false,
678 gas_per_auth_base_cost: 0,
679 gas_per_empty_account_cost: 0,
680 has_floor_gas: false,
681 total_cost_floor_per_token: 0,
682 }
683 }
684
685 const fn cancun() -> Self {
686 let mut config = Self::shanghai();
687 config.has_blob_base_fee = true;
688 config.has_shard_blob_transactions = true;
689 config.has_transient_storage = true;
690 config.has_mcopy = true;
691 config.has_restricted_selfdestruct = true;
692 config
693 }
694
695 const fn prague() -> Self {
696 let mut config = Self::cancun();
697 config.has_authorization_list = true;
698 config.gas_per_empty_account_cost = 25000;
699 config.gas_per_auth_base_cost = 12500;
700 config.has_floor_gas = true;
701 config.total_cost_floor_per_token = 10;
702 config
703 }
704}