evm_runtime/
lib.rs

1//! Runtime layer for EVM.
2
3#![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
107/// EVM runtime.
108///
109/// The runtime wraps an EVM `Machine` with support of return data and context.
110pub 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	/// Create a new runtime with given code and data.
121	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	/// Get a reference to the machine.
139	pub fn machine(&self) -> &Machine {
140		&self.machine
141	}
142
143	/// Get a reference to the execution context.
144	pub fn context(&self) -> &Context {
145		&self.context
146	}
147
148	/// Step the runtime.
149	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	/// Loop stepping the runtime until it stops.
157	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/// Runtime configuration.
191#[derive(Clone, Debug)]
192pub struct Config {
193	/// Gas paid for extcode.
194	pub gas_ext_code: u64,
195	/// Gas paid for extcodehash.
196	pub gas_ext_code_hash: u64,
197	/// Gas paid for sstore set.
198	pub gas_sstore_set: u64,
199	/// Gas paid for sstore reset.
200	pub gas_sstore_reset: u64,
201	/// Gas paid for sstore refund.
202	pub refund_sstore_clears: i64,
203	/// EIP-3529
204	pub max_refund_quotient: u64,
205	/// Gas paid for BALANCE opcode.
206	pub gas_balance: u64,
207	/// Gas paid for SLOAD opcode.
208	pub gas_sload: u64,
209	/// Gas paid for cold SLOAD opcode.
210	pub gas_sload_cold: u64,
211	/// Gas paid for SUICIDE opcode.
212	pub gas_suicide: u64,
213	/// Gas paid for SUICIDE opcode when it hits a new account.
214	pub gas_suicide_new_account: u64,
215	/// Gas paid for CALL opcode.
216	pub gas_call: u64,
217	/// Gas paid for EXP opcode for every byte.
218	pub gas_expbyte: u64,
219	/// Gas paid for a contract creation transaction.
220	pub gas_transaction_create: u64,
221	/// Gas paid for a message call transaction.
222	pub gas_transaction_call: u64,
223	/// Gas paid for zero data in a transaction.
224	pub gas_transaction_zero_data: u64,
225	/// Gas paid for non-zero data in a transaction.
226	pub gas_transaction_non_zero_data: u64,
227	/// Gas paid per address in transaction access list (see EIP-2930).
228	pub gas_access_list_address: u64,
229	/// Gas paid per storage key in transaction access list (see EIP-2930).
230	pub gas_access_list_storage_key: u64,
231	/// Gas paid for accessing cold account.
232	pub gas_account_access_cold: u64,
233	/// Gas paid for accessing ready storage.
234	pub gas_storage_read_warm: u64,
235	/// EIP-1283.
236	pub sstore_gas_metering: bool,
237	/// EIP-1706.
238	pub sstore_revert_under_stipend: bool,
239	/// EIP-2929
240	pub increase_state_access_gas: bool,
241	/// EIP-3529
242	pub decrease_clears_refund: bool,
243	/// EIP-3541
244	pub disallow_executable_format: bool,
245	/// EIP-3651
246	pub warm_coinbase_address: bool,
247	/// EIP-7702: Gas cost for processing each authorization tuple
248	pub gas_auth_base_cost: u64,
249	/// EIP-7702: Gas cost per empty account in authorization list
250	pub gas_per_empty_account_cost: u64,
251	/// Whether to throw out of gas error when
252	/// CALL/CALLCODE/DELEGATECALL requires more than maximum amount
253	/// of gas.
254	pub err_on_call_with_more_gas: bool,
255	/// Take l64 for callcreate after gas.
256	pub call_l64_after_gas: bool,
257	/// Whether empty account is considered exists.
258	pub empty_considered_exists: bool,
259	/// Whether create transactions and create opcode increases nonce by one.
260	pub create_increase_nonce: bool,
261	/// Stack limit.
262	pub stack_limit: usize,
263	/// Memory limit.
264	pub memory_limit: usize,
265	/// Call limit.
266	pub call_stack_limit: usize,
267	/// Create contract limit.
268	pub create_contract_limit: Option<usize>,
269	/// EIP-3860, maximum size limit of init_code.
270	pub max_initcode_size: Option<usize>,
271	/// Call stipend.
272	pub call_stipend: u64,
273	/// Has delegate call.
274	pub has_delegate_call: bool,
275	/// Has create2.
276	pub has_create2: bool,
277	/// Has revert.
278	pub has_revert: bool,
279	/// Has return data.
280	pub has_return_data: bool,
281	/// Has bitwise shifting.
282	pub has_bitwise_shifting: bool,
283	/// Has chain ID.
284	pub has_chain_id: bool,
285	/// Has self balance.
286	pub has_self_balance: bool,
287	/// Has ext code hash.
288	pub has_ext_code_hash: bool,
289	/// Has ext block fee. See [EIP-3198](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3198.md)
290	pub has_base_fee: bool,
291	/// Has PUSH0 opcode. See [EIP-3855](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3855.md)
292	pub has_push0: bool,
293	/// Has TLOAD and TSTORE opcode.
294	pub has_tloadstore: bool,
295	/// Has MCOPY opcode.
296	pub has_mcopy: bool,
297	/// Whether the gasometer is running in estimate mode.
298	pub estimate: bool,
299	/// Has EIP-6780. See [EIP-6780](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6780.md)
300	pub has_eip_6780: bool,
301	/// Has EIP-7702. See [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md)
302	pub has_eip_7702: bool,
303}
304
305impl Config {
306	/// Frontier hard fork configuration.
307	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	/// Istanbul hard fork configuration.
367	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	/// Berlin hard fork configuration.
427	pub const fn berlin() -> Config {
428		Self::config_with_derived_values(DerivedConfigInputs::berlin())
429	}
430
431	/// london hard fork configuration.
432	pub const fn london() -> Config {
433		Self::config_with_derived_values(DerivedConfigInputs::london())
434	}
435
436	/// The Merge (Paris) hard fork configuration.
437	pub const fn merge() -> Config {
438		Self::config_with_derived_values(DerivedConfigInputs::merge())
439	}
440
441	/// Shanghai hard fork configuration.
442	pub const fn shanghai() -> Config {
443		Self::config_with_derived_values(DerivedConfigInputs::shanghai())
444	}
445
446	/// Cancun hard fork configuration.
447	pub const fn cancun() -> Config {
448		Self::config_with_derived_values(DerivedConfigInputs::cancun())
449	}
450
451	/// Pectra hard fork configuration.
452	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		// See https://eips.ethereum.org/EIPS/eip-2929
476		let gas_sload = gas_storage_read_warm;
477		let gas_sstore_reset = 5000 - gas_sload_cold;
478
479		// See https://eips.ethereum.org/EIPS/eip-3529
480		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
546/// Independent inputs that are used to derive other config values.
547/// See `Config::config_with_derived_values` implementation for details.
548struct 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			// 2 * 24576 as per EIP-3860
638			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			// 2 * 24576 as per EIP-3860
659			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	/// Pectra hard fork configuration.
670	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			// 2 * 24576 as per EIP-3860
681			max_initcode_size: Some(0xC000),
682			has_eip_6780: true,
683			has_tloadstore: true,
684			has_mcopy: true,
685			has_eip_7702: true,
686			// PER_AUTH_BASE_COST from EIP-7702
687			gas_auth_base_cost: 12500,
688			// PER_EMPTY_ACCOUNT_COST from EIP-7702
689			gas_per_empty_account_cost: 25000,
690		}
691	}
692}