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 context::{CallScheme, Context, CreateScheme};
36pub use handler::{Handler, Transfer};
37pub use 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, 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}
283
284impl Config {
285 #[must_use]
287 pub const fn frontier() -> Self {
288 Self {
289 gas_ext_code: 20,
290 gas_ext_code_hash: 20,
291 gas_balance: 20,
292 gas_sload: 50,
293 gas_sload_cold: 0,
294 gas_sstore_set: 20000,
295 gas_sstore_reset: 5000,
296 refund_sstore_clears: 15000,
297 max_refund_quotient: 2,
298 gas_suicide: 0,
299 gas_suicide_new_account: 0,
300 gas_call: 40,
301 gas_expbyte: 10,
302 gas_transaction_create: 21000,
303 gas_transaction_call: 21000,
304 gas_transaction_zero_data: 4,
305 gas_transaction_non_zero_data: 68,
306 gas_access_list_address: 0,
307 gas_access_list_storage_key: 0,
308 gas_account_access_cold: 0,
309 gas_storage_read_warm: 0,
310 sstore_gas_metering: false,
311 sstore_revert_under_stipend: false,
312 increase_state_access_gas: false,
313 decrease_clears_refund: false,
314 disallow_executable_format: false,
315 warm_coinbase_address: false,
316 err_on_call_with_more_gas: true,
317 empty_considered_exists: true,
318 create_increase_nonce: false,
319 call_l64_after_gas: false,
320 stack_limit: 1024,
321 memory_limit: usize::MAX,
322 call_stack_limit: 1024,
323 create_contract_limit: None,
324 max_initcode_size: None,
325 call_stipend: 2300,
326 has_delegate_call: false,
327 has_create2: false,
328 has_revert: false,
329 has_return_data: false,
330 has_bitwise_shifting: false,
331 has_chain_id: false,
332 has_self_balance: false,
333 has_ext_code_hash: false,
334 has_base_fee: false,
335 has_push0: false,
336 estimate: false,
337 has_blob_base_fee: false,
338 has_shard_blob_transactions: false,
339 has_transient_storage: false,
340 has_mcopy: false,
341 has_restricted_selfdestruct: false,
342 }
343 }
344
345 #[must_use]
347 pub const fn istanbul() -> Self {
348 Self {
349 gas_ext_code: 700,
350 gas_ext_code_hash: 700,
351 gas_balance: 700,
352 gas_sload: 800,
353 gas_sload_cold: 0,
354 gas_sstore_set: 20000,
355 gas_sstore_reset: 5000,
356 refund_sstore_clears: 15000,
357 max_refund_quotient: 2,
358 gas_suicide: 5000,
359 gas_suicide_new_account: 25000,
360 gas_call: 700,
361 gas_expbyte: 50,
362 gas_transaction_create: 53000,
363 gas_transaction_call: 21000,
364 gas_transaction_zero_data: 4,
365 gas_transaction_non_zero_data: 16,
366 gas_access_list_address: 0,
367 gas_access_list_storage_key: 0,
368 gas_account_access_cold: 0,
369 gas_storage_read_warm: 0,
370 sstore_gas_metering: true,
371 sstore_revert_under_stipend: true,
372 increase_state_access_gas: false,
373 decrease_clears_refund: false,
374 disallow_executable_format: false,
375 warm_coinbase_address: false,
376 err_on_call_with_more_gas: false,
377 empty_considered_exists: false,
378 create_increase_nonce: true,
379 call_l64_after_gas: true,
380 stack_limit: 1024,
381 memory_limit: usize::MAX,
382 call_stack_limit: 1024,
383 create_contract_limit: Some(0x6000),
384 max_initcode_size: None,
385 call_stipend: 2300,
386 has_delegate_call: true,
387 has_create2: true,
388 has_revert: true,
389 has_return_data: true,
390 has_bitwise_shifting: true,
391 has_chain_id: true,
392 has_self_balance: true,
393 has_ext_code_hash: true,
394 has_base_fee: false,
395 has_push0: false,
396 estimate: false,
397 has_blob_base_fee: false,
398 has_shard_blob_transactions: false,
399 has_transient_storage: false,
400 has_mcopy: false,
401 has_restricted_selfdestruct: false,
402 }
403 }
404
405 #[must_use]
407 pub const fn berlin() -> Self {
408 Self::config_with_derived_values(DerivedConfigInputs::berlin())
409 }
410
411 #[must_use]
413 pub const fn london() -> Self {
414 Self::config_with_derived_values(DerivedConfigInputs::london())
415 }
416
417 #[must_use]
419 pub const fn merge() -> Self {
420 Self::config_with_derived_values(DerivedConfigInputs::merge())
421 }
422
423 #[must_use]
425 pub const fn shanghai() -> Self {
426 Self::config_with_derived_values(DerivedConfigInputs::shanghai())
427 }
428
429 #[must_use]
431 pub const fn cancun() -> Self {
432 Self::config_with_derived_values(DerivedConfigInputs::cancun())
433 }
434
435 const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self {
436 let DerivedConfigInputs {
437 gas_storage_read_warm,
438 gas_sload_cold,
439 gas_access_list_storage_key,
440 decrease_clears_refund,
441 has_base_fee,
442 has_push0,
443 disallow_executable_format,
444 warm_coinbase_address,
445 max_initcode_size,
446 has_blob_base_fee,
447 has_shard_blob_transactions,
448 has_transient_storage,
449 has_mcopy,
450 has_restricted_selfdestruct,
451 } = inputs;
452
453 let gas_sload = gas_storage_read_warm;
455 let gas_sstore_reset = 5000 - gas_sload_cold;
456
457 #[allow(clippy::as_conversions, clippy::cast_possible_wrap)]
459 let refund_sstore_clears = if decrease_clears_refund {
461 (gas_sstore_reset + gas_access_list_storage_key) as i64
462 } else {
463 15000
464 };
465 let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
466
467 Self {
468 gas_ext_code: 0,
469 gas_ext_code_hash: 0,
470 gas_balance: 0,
471 gas_sload,
472 gas_sload_cold,
473 gas_sstore_set: 20000,
474 gas_sstore_reset,
475 refund_sstore_clears,
476 max_refund_quotient,
477 gas_suicide: 5000,
478 gas_suicide_new_account: 25000,
479 gas_call: 0,
480 gas_expbyte: 50,
481 gas_transaction_create: 53000,
482 gas_transaction_call: 21000,
483 gas_transaction_zero_data: 4,
484 gas_transaction_non_zero_data: 16,
485 gas_access_list_address: 2400,
486 gas_access_list_storage_key,
487 gas_account_access_cold: 2600,
488 gas_storage_read_warm,
489 sstore_gas_metering: true,
490 sstore_revert_under_stipend: true,
491 increase_state_access_gas: true,
492 decrease_clears_refund,
493 disallow_executable_format,
494 warm_coinbase_address,
495 err_on_call_with_more_gas: false,
496 empty_considered_exists: false,
497 create_increase_nonce: true,
498 call_l64_after_gas: true,
499 stack_limit: 1024,
500 memory_limit: usize::MAX,
501 call_stack_limit: 1024,
502 create_contract_limit: Some(0x6000),
503 max_initcode_size,
504 call_stipend: 2300,
505 has_delegate_call: true,
506 has_create2: true,
507 has_revert: true,
508 has_return_data: true,
509 has_bitwise_shifting: true,
510 has_chain_id: true,
511 has_self_balance: true,
512 has_ext_code_hash: true,
513 has_base_fee,
514 has_push0,
515 estimate: false,
516 has_blob_base_fee,
517 has_shard_blob_transactions,
518 has_transient_storage,
519 has_mcopy,
520 has_restricted_selfdestruct,
521 }
522 }
523}
524
525#[allow(clippy::struct_excessive_bools)]
528#[derive(Debug, Copy, Clone, Eq, PartialEq)]
529struct DerivedConfigInputs {
530 gas_storage_read_warm: u64,
531 gas_sload_cold: u64,
532 gas_access_list_storage_key: u64,
533 decrease_clears_refund: bool,
534 has_base_fee: bool,
535 has_push0: bool,
536 disallow_executable_format: bool,
537 warm_coinbase_address: bool,
538 max_initcode_size: Option<usize>,
539 has_blob_base_fee: bool,
540 has_shard_blob_transactions: bool,
541 has_transient_storage: bool,
542 has_mcopy: bool,
543 has_restricted_selfdestruct: bool,
544}
545
546impl DerivedConfigInputs {
547 const fn berlin() -> Self {
548 Self {
549 gas_storage_read_warm: 100,
550 gas_sload_cold: 2100,
551 gas_access_list_storage_key: 1900,
552 decrease_clears_refund: false,
553 has_base_fee: false,
554 has_push0: false,
555 disallow_executable_format: false,
556 warm_coinbase_address: false,
557 max_initcode_size: None,
558 has_blob_base_fee: false,
559 has_shard_blob_transactions: false,
560 has_transient_storage: false,
561 has_mcopy: false,
562 has_restricted_selfdestruct: false,
563 }
564 }
565
566 const fn london() -> Self {
567 Self {
568 gas_storage_read_warm: 100,
569 gas_sload_cold: 2100,
570 gas_access_list_storage_key: 1900,
571 decrease_clears_refund: true,
572 has_base_fee: true,
573 has_push0: false,
574 disallow_executable_format: true,
575 warm_coinbase_address: false,
576 max_initcode_size: None,
577 has_blob_base_fee: false,
578 has_shard_blob_transactions: false,
579 has_transient_storage: false,
580 has_mcopy: false,
581 has_restricted_selfdestruct: false,
582 }
583 }
584
585 const fn merge() -> Self {
586 Self {
587 gas_storage_read_warm: 100,
588 gas_sload_cold: 2100,
589 gas_access_list_storage_key: 1900,
590 decrease_clears_refund: true,
591 has_base_fee: true,
592 has_push0: false,
593 disallow_executable_format: true,
594 warm_coinbase_address: false,
595 max_initcode_size: None,
596 has_blob_base_fee: false,
597 has_shard_blob_transactions: false,
598 has_transient_storage: false,
599 has_mcopy: false,
600 has_restricted_selfdestruct: false,
601 }
602 }
603
604 const fn shanghai() -> Self {
605 Self {
606 gas_storage_read_warm: 100,
607 gas_sload_cold: 2100,
608 gas_access_list_storage_key: 1900,
609 decrease_clears_refund: true,
610 has_base_fee: true,
611 has_push0: true,
612 disallow_executable_format: true,
613 warm_coinbase_address: true,
614 max_initcode_size: Some(0xC000),
616 has_blob_base_fee: false,
617 has_shard_blob_transactions: false,
618 has_transient_storage: false,
619 has_mcopy: false,
620 has_restricted_selfdestruct: false,
621 }
622 }
623
624 const fn cancun() -> Self {
625 Self {
626 gas_storage_read_warm: 100,
627 gas_sload_cold: 2100,
628 gas_access_list_storage_key: 1900,
629 decrease_clears_refund: true,
630 has_base_fee: true,
631 has_push0: true,
632 disallow_executable_format: true,
633 warm_coinbase_address: true,
634 max_initcode_size: Some(0xC000),
636 has_blob_base_fee: true,
637 has_shard_blob_transactions: true,
638 has_transient_storage: true,
639 has_mcopy: true,
640 has_restricted_selfdestruct: true,
641 }
642 }
643}