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
42pub struct Runtime {
46 machine: Machine,
47 return_data_buffer: Vec<u8>,
48 return_data_len: usize,
49 return_data_offset: usize,
50 context: Context,
51}
52
53impl Runtime {
54 #[must_use]
56 pub fn new(
57 code: Rc<Vec<u8>>,
58 data: Rc<Vec<u8>>,
59 context: Context,
60 stack_limit: usize,
61 memory_limit: usize,
62 ) -> Self {
63 Self {
64 machine: Machine::new(code, data, stack_limit, memory_limit),
65 return_data_buffer: Vec::new(),
66 return_data_len: 0,
67 return_data_offset: 0,
68 context,
69 }
70 }
71
72 #[must_use]
74 pub const fn machine(&self) -> &Machine {
75 &self.machine
76 }
77
78 #[must_use]
80 pub const fn context(&self) -> &Context {
81 &self.context
82 }
83
84 pub fn run<H: Handler + InterpreterHandler>(
86 &mut self,
87 handler: &mut H,
88 ) -> Capture<ExitReason, Resolve<H>> {
89 loop {
90 let result = self.machine.step(handler, &self.context.address);
91 match result {
92 Ok(()) => (),
93 Err(Capture::Exit(e)) => {
94 return Capture::Exit(e);
95 }
96 Err(Capture::Trap(opcode)) => match eval::eval(self, opcode, handler) {
97 eval::Control::Continue => (),
98 eval::Control::CallInterrupt(interrupt) => {
99 let resolve = ResolveCall::new(self);
100 return Capture::Trap(Resolve::Call(interrupt, resolve));
101 }
102 eval::Control::CreateInterrupt(interrupt) => {
103 let resolve = ResolveCreate::new(self);
104 return Capture::Trap(Resolve::Create(interrupt, resolve));
105 }
106 eval::Control::Exit(exit) => {
107 self.machine.exit(exit.clone());
108 return Capture::Exit(exit);
109 }
110 },
111 }
112 }
113 }
114
115 pub fn finish_create(
118 &mut self,
119 reason: ExitReason,
120 address: Option<H160>,
121 return_data: Vec<u8>,
122 ) -> Result<(), ExitReason> {
123 eval::finish_create(self, reason, address, return_data)
124 }
125
126 pub fn finish_call(
129 &mut self,
130 reason: ExitReason,
131 return_data: Vec<u8>,
132 ) -> Result<(), ExitReason> {
133 eval::finish_call(
134 self,
135 self.return_data_len,
136 self.return_data_offset,
137 reason,
138 return_data,
139 )
140 }
141}
142
143#[allow(clippy::struct_excessive_bools)]
145#[derive(Clone, Debug)]
146pub struct Config {
147 pub gas_ext_code: u64,
149 pub gas_ext_code_hash: u64,
151 pub gas_sstore_set: u64,
153 pub gas_sstore_reset: u64,
155 pub refund_sstore_clears: i64,
157 pub max_refund_quotient: u64,
159 pub gas_balance: u64,
161 pub gas_sload: u64,
163 pub gas_sload_cold: u64,
165 pub gas_suicide: u64,
167 pub gas_suicide_new_account: u64,
169 pub gas_call: u64,
171 pub gas_expbyte: u64,
173 pub gas_transaction_create: u64,
175 pub gas_transaction_call: u64,
177 pub gas_transaction_zero_data: u64,
179 pub gas_transaction_non_zero_data: u64,
181 pub gas_access_list_address: u64,
183 pub gas_access_list_storage_key: u64,
185 pub gas_account_access_cold: u64,
187 pub gas_storage_read_warm: u64,
189 pub sstore_gas_metering: bool,
191 pub sstore_revert_under_stipend: bool,
193 pub increase_state_access_gas: bool,
195 pub decrease_clears_refund: bool,
197 pub disallow_executable_format: bool,
199 pub warm_coinbase_address: bool,
201 pub err_on_call_with_more_gas: bool,
205 pub call_l64_after_gas: bool,
207 pub empty_considered_exists: bool,
209 pub create_increase_nonce: bool,
211 pub stack_limit: usize,
213 pub memory_limit: usize,
215 pub call_stack_limit: usize,
217 pub create_contract_limit: Option<usize>,
219 pub max_initcode_size: Option<usize>,
221 pub call_stipend: u64,
223 pub has_delegate_call: bool,
225 pub has_create2: bool,
227 pub has_revert: bool,
229 pub has_return_data: bool,
231 pub has_bitwise_shifting: bool,
233 pub has_chain_id: bool,
235 pub has_self_balance: bool,
237 pub has_ext_code_hash: bool,
239 pub has_base_fee: bool,
241 pub has_push0: bool,
243 pub estimate: bool,
245 pub has_blob_base_fee: bool,
247 pub has_shard_blob_transactions: bool,
249 pub has_transient_storage: bool,
251 pub has_mcopy: bool,
253 pub has_restricted_selfdestruct: bool,
255 pub has_authorization_list: bool,
257 pub gas_per_empty_account_cost: u64,
259 pub gas_per_auth_base_cost: u64,
261 pub has_floor_gas: bool,
263 pub total_cost_floor_per_token: u64,
265}
266
267impl Config {
268 #[must_use]
270 pub const fn frontier() -> Self {
271 Self {
272 gas_ext_code: 20,
273 gas_ext_code_hash: 20,
274 gas_balance: 20,
275 gas_sload: 50,
276 gas_sload_cold: 0,
277 gas_sstore_set: 20000,
278 gas_sstore_reset: 5000,
279 refund_sstore_clears: 15000,
280 max_refund_quotient: 2,
281 gas_suicide: 0,
282 gas_suicide_new_account: 0,
283 gas_call: 40,
284 gas_expbyte: 10,
285 gas_transaction_create: 21000,
286 gas_transaction_call: 21000,
287 gas_transaction_zero_data: 4,
288 gas_transaction_non_zero_data: 68,
289 gas_access_list_address: 0,
290 gas_access_list_storage_key: 0,
291 gas_account_access_cold: 0,
292 gas_storage_read_warm: 0,
293 sstore_gas_metering: false,
294 sstore_revert_under_stipend: false,
295 increase_state_access_gas: false,
296 decrease_clears_refund: false,
297 disallow_executable_format: false,
298 warm_coinbase_address: false,
299 err_on_call_with_more_gas: true,
300 empty_considered_exists: true,
301 create_increase_nonce: false,
302 call_l64_after_gas: false,
303 stack_limit: 1024,
304 memory_limit: usize::MAX,
305 call_stack_limit: 1024,
306 create_contract_limit: None,
307 max_initcode_size: None,
308 call_stipend: 2300,
309 has_delegate_call: false,
310 has_create2: false,
311 has_revert: false,
312 has_return_data: false,
313 has_bitwise_shifting: false,
314 has_chain_id: false,
315 has_self_balance: false,
316 has_ext_code_hash: false,
317 has_base_fee: false,
318 has_push0: false,
319 estimate: false,
320 has_blob_base_fee: false,
321 has_shard_blob_transactions: false,
322 has_transient_storage: false,
323 has_mcopy: false,
324 has_restricted_selfdestruct: false,
325 has_authorization_list: false,
326 gas_per_empty_account_cost: 0,
327 gas_per_auth_base_cost: 0,
328 has_floor_gas: false,
329 total_cost_floor_per_token: 0,
330 }
331 }
332
333 #[must_use]
335 pub const fn istanbul() -> Self {
336 Self {
337 gas_ext_code: 700,
338 gas_ext_code_hash: 700,
339 gas_balance: 700,
340 gas_sload: 800,
341 gas_sload_cold: 0,
342 gas_sstore_set: 20000,
343 gas_sstore_reset: 5000,
344 refund_sstore_clears: 15000,
345 max_refund_quotient: 2,
346 gas_suicide: 5000,
347 gas_suicide_new_account: 25000,
348 gas_call: 700,
349 gas_expbyte: 50,
350 gas_transaction_create: 53000,
351 gas_transaction_call: 21000,
352 gas_transaction_zero_data: 4,
353 gas_transaction_non_zero_data: 16,
354 gas_access_list_address: 0,
355 gas_access_list_storage_key: 0,
356 gas_account_access_cold: 0,
357 gas_storage_read_warm: 0,
358 sstore_gas_metering: true,
359 sstore_revert_under_stipend: true,
360 increase_state_access_gas: false,
361 decrease_clears_refund: false,
362 disallow_executable_format: false,
363 warm_coinbase_address: false,
364 err_on_call_with_more_gas: false,
365 empty_considered_exists: false,
366 create_increase_nonce: true,
367 call_l64_after_gas: true,
368 stack_limit: 1024,
369 memory_limit: usize::MAX,
370 call_stack_limit: 1024,
371 create_contract_limit: Some(0x6000),
372 max_initcode_size: None,
373 call_stipend: 2300,
374 has_delegate_call: true,
375 has_create2: true,
376 has_revert: true,
377 has_return_data: true,
378 has_bitwise_shifting: true,
379 has_chain_id: true,
380 has_self_balance: true,
381 has_ext_code_hash: true,
382 has_base_fee: false,
383 has_push0: false,
384 estimate: false,
385 has_blob_base_fee: false,
386 has_shard_blob_transactions: false,
387 has_transient_storage: false,
388 has_mcopy: false,
389 has_restricted_selfdestruct: false,
390 has_authorization_list: false,
391 gas_per_auth_base_cost: 0,
392 gas_per_empty_account_cost: 0,
393 has_floor_gas: false,
394 total_cost_floor_per_token: 0,
395 }
396 }
397
398 #[must_use]
400 pub const fn berlin() -> Self {
401 Self::config_with_derived_values(DerivedConfigInputs::berlin())
402 }
403
404 #[must_use]
406 pub const fn london() -> Self {
407 Self::config_with_derived_values(DerivedConfigInputs::london())
408 }
409
410 #[must_use]
412 pub const fn merge() -> Self {
413 Self::config_with_derived_values(DerivedConfigInputs::merge())
414 }
415
416 #[must_use]
418 pub const fn shanghai() -> Self {
419 Self::config_with_derived_values(DerivedConfigInputs::shanghai())
420 }
421
422 #[must_use]
424 pub const fn cancun() -> Self {
425 Self::config_with_derived_values(DerivedConfigInputs::cancun())
426 }
427
428 #[must_use]
430 pub const fn prague() -> Self {
431 Self::config_with_derived_values(DerivedConfigInputs::prague())
432 }
433
434 const fn config_with_derived_values(inputs: DerivedConfigInputs) -> Self {
435 let DerivedConfigInputs {
436 gas_storage_read_warm,
437 gas_sload_cold,
438 gas_access_list_storage_key,
439 decrease_clears_refund,
440 has_base_fee,
441 has_push0,
442 disallow_executable_format,
443 warm_coinbase_address,
444 max_initcode_size,
445 has_blob_base_fee,
446 has_shard_blob_transactions,
447 has_transient_storage,
448 has_mcopy,
449 has_restricted_selfdestruct,
450 has_authorization_list,
451 gas_per_empty_account_cost,
452 gas_per_auth_base_cost,
453 has_floor_gas,
454 total_cost_floor_per_token,
455 } = inputs;
456
457 let gas_sload = gas_storage_read_warm;
459 let gas_sstore_reset = 5000 - gas_sload_cold;
460
461 #[allow(clippy::as_conversions, clippy::cast_possible_wrap)]
463 let refund_sstore_clears = if decrease_clears_refund {
465 (gas_sstore_reset + gas_access_list_storage_key) as i64
466 } else {
467 15000
468 };
469 let max_refund_quotient = if decrease_clears_refund { 5 } else { 2 };
470
471 Self {
472 gas_ext_code: 0,
473 gas_ext_code_hash: 0,
474 gas_balance: 0,
475 gas_sload,
476 gas_sload_cold,
477 gas_sstore_set: 20000,
478 gas_sstore_reset,
479 refund_sstore_clears,
480 max_refund_quotient,
481 gas_suicide: 5000,
482 gas_suicide_new_account: 25000,
483 gas_call: 0,
484 gas_expbyte: 50,
485 gas_transaction_create: 53000,
486 gas_transaction_call: 21000,
487 gas_transaction_zero_data: 4,
488 gas_transaction_non_zero_data: 16,
489 gas_access_list_address: 2400,
490 gas_access_list_storage_key,
491 gas_account_access_cold: 2600,
492 gas_storage_read_warm,
493 sstore_gas_metering: true,
494 sstore_revert_under_stipend: true,
495 increase_state_access_gas: true,
496 decrease_clears_refund,
497 disallow_executable_format,
498 warm_coinbase_address,
499 err_on_call_with_more_gas: false,
500 empty_considered_exists: false,
501 create_increase_nonce: true,
502 call_l64_after_gas: true,
503 stack_limit: 1024,
504 memory_limit: usize::MAX,
505 call_stack_limit: 1024,
506 create_contract_limit: Some(0x6000),
507 max_initcode_size,
508 call_stipend: 2300,
509 has_delegate_call: true,
510 has_create2: true,
511 has_revert: true,
512 has_return_data: true,
513 has_bitwise_shifting: true,
514 has_chain_id: true,
515 has_self_balance: true,
516 has_ext_code_hash: true,
517 has_base_fee,
518 has_push0,
519 estimate: false,
520 has_blob_base_fee,
521 has_shard_blob_transactions,
522 has_transient_storage,
523 has_mcopy,
524 has_restricted_selfdestruct,
525 has_authorization_list,
526 gas_per_empty_account_cost,
527 gas_per_auth_base_cost,
528 has_floor_gas,
529 total_cost_floor_per_token,
530 }
531 }
532}
533
534#[allow(clippy::struct_excessive_bools)]
537#[derive(Debug, Copy, Clone, Eq, PartialEq)]
538struct DerivedConfigInputs {
539 gas_storage_read_warm: u64,
540 gas_sload_cold: u64,
541 gas_access_list_storage_key: u64,
542 decrease_clears_refund: bool,
543 has_base_fee: bool,
544 has_push0: bool,
545 disallow_executable_format: bool,
546 warm_coinbase_address: bool,
547 max_initcode_size: Option<usize>,
548 has_blob_base_fee: bool,
549 has_shard_blob_transactions: bool,
550 has_transient_storage: bool,
551 has_mcopy: bool,
552 has_restricted_selfdestruct: bool,
553 has_authorization_list: bool,
554 gas_per_empty_account_cost: u64,
555 gas_per_auth_base_cost: u64,
556 has_floor_gas: bool,
557 total_cost_floor_per_token: u64,
558}
559
560impl DerivedConfigInputs {
561 const fn berlin() -> Self {
562 Self {
563 gas_storage_read_warm: 100,
564 gas_sload_cold: 2100,
565 gas_access_list_storage_key: 1900,
566 decrease_clears_refund: false,
567 has_base_fee: false,
568 has_push0: false,
569 disallow_executable_format: false,
570 warm_coinbase_address: false,
571 max_initcode_size: None,
572 has_blob_base_fee: false,
573 has_shard_blob_transactions: false,
574 has_transient_storage: false,
575 has_mcopy: false,
576 has_restricted_selfdestruct: false,
577 has_authorization_list: false,
578 gas_per_auth_base_cost: 0,
579 gas_per_empty_account_cost: 0,
580 has_floor_gas: false,
581 total_cost_floor_per_token: 0,
582 }
583 }
584
585 const fn london() -> 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 has_authorization_list: false,
602 gas_per_auth_base_cost: 0,
603 gas_per_empty_account_cost: 0,
604 has_floor_gas: false,
605 total_cost_floor_per_token: 0,
606 }
607 }
608
609 const fn merge() -> Self {
610 Self {
611 gas_storage_read_warm: 100,
612 gas_sload_cold: 2100,
613 gas_access_list_storage_key: 1900,
614 decrease_clears_refund: true,
615 has_base_fee: true,
616 has_push0: false,
617 disallow_executable_format: true,
618 warm_coinbase_address: false,
619 max_initcode_size: None,
620 has_blob_base_fee: false,
621 has_shard_blob_transactions: false,
622 has_transient_storage: false,
623 has_mcopy: false,
624 has_restricted_selfdestruct: false,
625 has_authorization_list: false,
626 gas_per_auth_base_cost: 0,
627 gas_per_empty_account_cost: 0,
628 has_floor_gas: false,
629 total_cost_floor_per_token: 0,
630 }
631 }
632
633 const fn shanghai() -> Self {
634 Self {
635 gas_storage_read_warm: 100,
636 gas_sload_cold: 2100,
637 gas_access_list_storage_key: 1900,
638 decrease_clears_refund: true,
639 has_base_fee: true,
640 has_push0: true,
641 disallow_executable_format: true,
642 warm_coinbase_address: true,
643 max_initcode_size: Some(0xC000),
645 has_blob_base_fee: false,
646 has_shard_blob_transactions: false,
647 has_transient_storage: false,
648 has_mcopy: false,
649 has_restricted_selfdestruct: false,
650 has_authorization_list: false,
651 gas_per_auth_base_cost: 0,
652 gas_per_empty_account_cost: 0,
653 has_floor_gas: false,
654 total_cost_floor_per_token: 0,
655 }
656 }
657
658 const fn cancun() -> Self {
659 let mut config = Self::shanghai();
660 config.has_blob_base_fee = true;
661 config.has_shard_blob_transactions = true;
662 config.has_transient_storage = true;
663 config.has_mcopy = true;
664 config.has_restricted_selfdestruct = true;
665 config
666 }
667
668 const fn prague() -> Self {
669 let mut config = Self::cancun();
670 config.has_authorization_list = true;
671 config.gas_per_empty_account_cost = 25000;
672 config.gas_per_auth_base_cost = 12500;
673 config.has_floor_gas = true;
674 config.total_cost_floor_per_token = 10;
675 config
676 }
677}