evm_interpreter/
trap.rs

1//! Call and create trap handler.
2
3use alloc::vec::Vec;
4use core::{cmp::min, convert::Infallible};
5use sha3::{Digest, Keccak256};
6
7#[allow(unused_imports)]
8use crate::uint::{H160, H256, U256, U256Ext};
9use crate::{
10	error::{ExitError, ExitException, ExitResult},
11	machine::{Machine, Memory},
12	runtime::{Context, RuntimeBackend, RuntimeState, Transfer},
13	utils::u256_to_usize,
14};
15
16/// Consume `T` to get `Rest`.
17///
18/// For example, an interpreter may return two types of traps, a [CallCreateTrap], and another customized trap.
19/// The standard invoker, however, can only handle [CallCreateTrap]. By implementing this trait, the standard
20/// invoker can handle just the [CallCreateTrap], and then it returns `Rest` as an additional interrupt that
21/// is handled by the user.
22pub trait TrapConsume<T> {
23	/// Rest type.
24	type Rest;
25
26	/// Consume `T` to get `Rest`.
27	fn consume(self) -> Result<T, Self::Rest>;
28}
29
30impl<T> TrapConsume<T> for T {
31	type Rest = Infallible;
32
33	fn consume(self) -> Result<T, Infallible> {
34		Ok(self)
35	}
36}
37
38/// Call create opcodes.
39pub enum CallCreateOpcode {
40	/// `CALL`
41	Call,
42	/// `CALLCODE`
43	CallCode,
44	/// `DELEGATECALL`
45	DelegateCall,
46	/// `STATICCALL`
47	StaticCall,
48	/// `CREATE`
49	Create,
50	/// `CREATE2`
51	Create2,
52}
53
54/// Combined call create trap data.
55#[derive(Debug)]
56pub enum CallCreateTrap {
57	/// A call trap data.
58	Call(CallTrap),
59	/// A create trap data.
60	Create(CreateTrap),
61}
62
63impl CallCreateTrap {
64	/// Target gas value.
65	#[must_use]
66	pub const fn target_gas(&self) -> Option<U256> {
67		match self {
68			Self::Call(CallTrap { gas, .. }) => Some(*gas),
69			Self::Create(_) => None,
70		}
71	}
72
73	/// Create a new trap from the given opcode and the machine state.
74	pub fn new_from<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
75		opcode: CallCreateOpcode,
76		machine: &mut Machine<S>,
77	) -> Result<Self, ExitError> {
78		match opcode {
79			CallCreateOpcode::Create => Ok(Self::Create(CreateTrap::new_create_from(machine)?)),
80			CallCreateOpcode::Create2 => Ok(Self::Create(CreateTrap::new_create2_from(machine)?)),
81			CallCreateOpcode::Call => {
82				Ok(Self::Call(CallTrap::new_from(CallScheme::Call, machine)?))
83			}
84			CallCreateOpcode::CallCode => Ok(Self::Call(CallTrap::new_from(
85				CallScheme::CallCode,
86				machine,
87			)?)),
88			CallCreateOpcode::DelegateCall => Ok(Self::Call(CallTrap::new_from(
89				CallScheme::DelegateCall,
90				machine,
91			)?)),
92			CallCreateOpcode::StaticCall => Ok(Self::Call(CallTrap::new_from(
93				CallScheme::StaticCall,
94				machine,
95			)?)),
96		}
97	}
98
99	/// Target code or init code.
100	pub fn code<H: RuntimeBackend>(&self, handler: &H) -> Vec<u8> {
101		match self {
102			Self::Call(trap) => handler.code(trap.target),
103			Self::Create(trap) => trap.code.clone(),
104		}
105	}
106}
107
108/// Call scheme.
109#[derive(Clone, Copy, Eq, PartialEq, Debug)]
110pub enum CallScheme {
111	/// `CALL`
112	Call,
113	/// `CALLCODE`
114	CallCode,
115	/// `DELEGATECALL`
116	DelegateCall,
117	/// `STATICCALL`
118	StaticCall,
119}
120
121/// Trap for a call.
122#[derive(Debug)]
123pub struct CallTrap {
124	/// Call target.
125	pub target: H160,
126	/// Transfer instruction, if any.
127	pub transfer: Option<Transfer>,
128	/// Input data.
129	pub input: Vec<u8>,
130	/// Gas.
131	pub gas: U256,
132	/// Whether it is `STATICCALL`
133	pub is_static: bool,
134	/// Out value offset.
135	pub out_offset: U256,
136	/// Out value length.
137	pub out_len: U256,
138	/// Call context.
139	pub context: Context,
140	/// Call scheme.
141	pub scheme: CallScheme,
142}
143
144impl CallTrap {
145	#[allow(clippy::too_many_arguments)]
146	fn new_from_params<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
147		scheme: CallScheme,
148		memory: &mut Memory,
149		state: &mut S,
150		gas: U256,
151		to: H256,
152		value: Option<U256>,
153		in_offset: U256,
154		in_len: U256,
155		out_offset: U256,
156		out_len: U256,
157	) -> Result<((), Self), ExitError> {
158		let value = value.unwrap_or(U256::ZERO);
159
160		let in_end = in_offset
161			.checked_add(in_len)
162			.ok_or(ExitException::InvalidRange)?;
163		if in_len != U256::ZERO {
164			memory.resize_end(in_end)?;
165		}
166		let out_end = out_offset
167			.checked_add(out_len)
168			.ok_or(ExitException::InvalidRange)?;
169		if out_len != U256::ZERO {
170			memory.resize_end(out_end)?;
171		}
172
173		let in_offset_len = if in_len == U256::ZERO {
174			None
175		} else {
176			Some((u256_to_usize(in_offset)?, u256_to_usize(in_len)?))
177		};
178
179		let input = in_offset_len
180			.map(|(in_offset, in_len)| memory.get(in_offset, in_len))
181			.unwrap_or(Vec::new());
182
183		let context = match scheme {
184			CallScheme::Call | CallScheme::StaticCall => Context {
185				address: to.into(),
186				caller: state.as_ref().context.address,
187				apparent_value: value,
188			},
189			CallScheme::CallCode => Context {
190				address: state.as_ref().context.address,
191				caller: state.as_ref().context.address,
192				apparent_value: value,
193			},
194			CallScheme::DelegateCall => Context {
195				address: state.as_ref().context.address,
196				caller: state.as_ref().context.caller,
197				apparent_value: state.as_ref().context.apparent_value,
198			},
199		};
200
201		let transfer = if scheme == CallScheme::Call {
202			Some(Transfer {
203				source: state.as_ref().context.address,
204				target: to.into(),
205				value,
206			})
207		} else if scheme == CallScheme::CallCode {
208			Some(Transfer {
209				source: state.as_ref().context.address,
210				target: state.as_ref().context.address,
211				value,
212			})
213		} else {
214			None
215		};
216
217		state.as_mut().retbuf = Vec::new();
218
219		Ok((
220			(),
221			Self {
222				target: to.into(),
223				transfer,
224				input,
225				gas,
226				is_static: scheme == CallScheme::StaticCall,
227				context,
228				out_offset,
229				out_len,
230				scheme,
231			},
232		))
233	}
234
235	/// Create a new call trap from the given call scheme and the machine state.
236	pub fn new_from<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
237		scheme: CallScheme,
238		machine: &mut Machine<S>,
239	) -> Result<Self, ExitError> {
240		let stack = &mut machine.stack;
241		let memory = &mut machine.memory;
242		let state = &mut machine.state;
243
244		match scheme {
245			CallScheme::Call | CallScheme::CallCode => stack.perform_pop7_push0(
246				|gas, to, value, in_offset, in_len, out_offset, out_len| {
247					Self::new_from_params(
248						scheme,
249						memory,
250						state,
251						*gas,
252						to.to_h256(),
253						Some(*value),
254						*in_offset,
255						*in_len,
256						*out_offset,
257						*out_len,
258					)
259				},
260			),
261			CallScheme::DelegateCall | CallScheme::StaticCall => {
262				stack.perform_pop6_push0(|gas, to, in_offset, in_len, out_offset, out_len| {
263					Self::new_from_params(
264						scheme,
265						memory,
266						state,
267						*gas,
268						to.to_h256(),
269						None,
270						*in_offset,
271						*in_len,
272						*out_offset,
273						*out_len,
274					)
275				})
276			}
277		}
278	}
279
280	/// Whether the call has value.
281	#[must_use]
282	pub fn has_value(&self) -> bool {
283		self.transfer
284			.as_ref()
285			.is_some_and(|t| t.value != U256::ZERO)
286	}
287}
288
289/// Feedback value of a call trap.
290#[derive(Debug)]
291pub struct CallFeedback {
292	/// The original call trap.
293	pub trap: CallTrap,
294	/// Exit reason.
295	pub reason: ExitResult,
296	/// Return value.
297	pub retbuf: Vec<u8>,
298}
299
300impl CallFeedback {
301	/// Apply the call feedback into a machine state.
302	pub fn to_machine<
303		S: AsRef<RuntimeState> + AsMut<RuntimeState>,
304		I: AsRef<Machine<S>> + AsMut<Machine<S>>,
305	>(
306		self,
307		interpreter: &mut I,
308	) -> Result<(), ExitError> {
309		let machine: &mut Machine<S> = interpreter.as_mut();
310
311		let reason = self.reason;
312		let retbuf = self.retbuf;
313		let target_len = min(self.trap.out_len, U256::from_usize(retbuf.len()));
314		let out_offset = self.trap.out_offset;
315
316		let ret = match reason {
317			Ok(_) => {
318				match machine
319					.memory
320					.copy_large(out_offset, U256::ZERO, target_len, &retbuf[..])
321				{
322					Ok(()) => {
323						machine.stack.push(U256::ONE)?;
324
325						Ok(())
326					}
327					Err(_) => {
328						machine.stack.push(U256::ZERO)?;
329
330						Ok(())
331					}
332				}
333			}
334			Err(ExitError::Reverted) => {
335				machine.stack.push(U256::ZERO)?;
336
337				let _ = machine
338					.memory
339					.copy_large(out_offset, U256::ZERO, target_len, &retbuf[..]);
340
341				Ok(())
342			}
343			Err(ExitError::Exception(_)) => {
344				machine.stack.push(U256::ZERO)?;
345
346				Ok(())
347			}
348			Err(ExitError::Fatal(e)) => {
349				machine.stack.push(U256::ZERO)?;
350
351				Err(e.into())
352			}
353		};
354
355		match ret {
356			Ok(()) => {
357				machine.state.as_mut().retbuf = retbuf;
358				Ok(())
359			}
360			Err(e) => Err(e),
361		}
362	}
363}
364
365/// Create scheme.
366#[derive(Clone, Copy, Eq, PartialEq, Debug)]
367pub enum CreateScheme {
368	/// Legacy create scheme of `CREATE`.
369	Legacy {
370		/// Caller of the create call.
371		caller: H160,
372	},
373	/// Create scheme of `CREATE2`.
374	Create2 {
375		/// Caller of the create call.
376		caller: H160,
377		/// Code hash.
378		code_hash: H256,
379		/// Salt.
380		salt: H256,
381	},
382}
383
384impl CreateScheme {
385	/// Resolved address.
386	#[allow(deprecated)]
387	pub fn address<H: RuntimeBackend>(&self, handler: &H) -> H160 {
388		match self {
389			Self::Create2 {
390				caller,
391				code_hash,
392				salt,
393			} => {
394				let mut hasher = Keccak256::new();
395				hasher.update([0xff]);
396				hasher.update(&caller[..]);
397				hasher.update(&salt[..]);
398				hasher.update(&code_hash[..]);
399				H256::from_slice(hasher.finalize().as_slice()).into()
400			}
401			Self::Legacy { caller } => {
402				let nonce = handler.nonce(*caller);
403				let mut stream = rlp::RlpStream::new_list(2);
404				stream.append(caller);
405				nonce.append_to_rlp_stream(&mut stream);
406				H256::from_slice(Keccak256::digest(stream.out()).as_slice()).into()
407			}
408		}
409	}
410
411	/// Caller address.
412	#[must_use]
413	pub const fn caller(&self) -> H160 {
414		match self {
415			Self::Create2 { caller, .. } => *caller,
416			Self::Legacy { caller } => *caller,
417		}
418	}
419}
420
421/// Call trap.
422#[derive(Debug)]
423pub struct CreateTrap {
424	/// Call scheme.
425	pub scheme: CreateScheme,
426	/// Value passed to the call.
427	pub value: U256,
428	/// Init code.
429	pub code: Vec<u8>,
430}
431
432impl CreateTrap {
433	/// Create a new `CREATE` trap from the machine state.
434	pub fn new_create_from<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
435		machine: &mut Machine<S>,
436	) -> Result<Self, ExitError> {
437		let stack = &mut machine.stack;
438		let memory = &mut machine.memory;
439		let state = &mut machine.state;
440
441		stack.perform_pop3_push0(|value, code_offset, code_len| {
442			let code_end = code_offset
443				.checked_add(*code_len)
444				.ok_or(ExitException::InvalidRange)?;
445
446			let code_offset_len = if code_len == &U256::ZERO {
447				None
448			} else {
449				Some((u256_to_usize(*code_offset)?, u256_to_usize(*code_len)?))
450			};
451
452			if *code_len != U256::ZERO {
453				memory.resize_end(code_end)?;
454			}
455
456			let code = code_offset_len
457				.map(|(code_offset, code_len)| memory.get(code_offset, code_len))
458				.unwrap_or(Vec::new());
459
460			let scheme = CreateScheme::Legacy {
461				caller: state.as_ref().context.address,
462			};
463
464			state.as_mut().retbuf = Vec::new();
465
466			Ok((
467				(),
468				Self {
469					scheme,
470					value: *value,
471					code,
472				},
473			))
474		})
475	}
476
477	/// Create a new `CREATE2` trap from the machine state.
478	#[allow(deprecated)]
479	pub fn new_create2_from<S: AsRef<RuntimeState> + AsMut<RuntimeState>>(
480		machine: &mut Machine<S>,
481	) -> Result<Self, ExitError> {
482		let stack = &mut machine.stack;
483		let memory = &mut machine.memory;
484		let state = &mut machine.state;
485
486		stack.perform_pop4_push0(|value, code_offset, code_len, salt| {
487			let code_end = code_offset
488				.checked_add(*code_len)
489				.ok_or(ExitException::InvalidRange)?;
490
491			let code_offset_len = if code_len == &U256::ZERO {
492				None
493			} else {
494				Some((u256_to_usize(*code_offset)?, u256_to_usize(*code_len)?))
495			};
496
497			memory.resize_end(code_end)?;
498
499			let code = code_offset_len
500				.map(|(code_offset, code_len)| memory.get(code_offset, code_len))
501				.unwrap_or(Vec::new());
502
503			let code_hash = H256::from_slice(Keccak256::digest(&code).as_slice());
504
505			let scheme = CreateScheme::Create2 {
506				caller: state.as_ref().context.address,
507				salt: salt.to_h256(),
508				code_hash,
509			};
510
511			state.as_mut().retbuf = Vec::new();
512
513			Ok((
514				(),
515				Self {
516					scheme,
517					value: *value,
518					code,
519				},
520			))
521		})
522	}
523}
524
525/// Feedback value of the create trap.
526#[derive(Debug)]
527pub struct CreateFeedback {
528	/// Original create trap.
529	pub trap: CreateTrap,
530	/// Exit reason and new contract address.
531	pub reason: Result<H160, ExitError>,
532	/// Return value.
533	pub retbuf: Vec<u8>,
534}
535
536impl CreateFeedback {
537	/// Apply the trap feedback to the machine state.
538	pub fn to_machine<
539		S: AsRef<RuntimeState> + AsMut<RuntimeState>,
540		I: AsRef<Machine<S>> + AsMut<Machine<S>>,
541	>(
542		self,
543		interpreter: &mut I,
544	) -> Result<(), ExitError> {
545		let machine: &mut Machine<S> = interpreter.as_mut();
546
547		let reason = self.reason;
548		let retbuf = self.retbuf;
549
550		let ret = match reason {
551			Ok(address) => {
552				machine.stack.push(U256::from_h160(address))?;
553				Ok(())
554			}
555			Err(ExitError::Reverted) => {
556				machine.stack.push(U256::ZERO)?;
557				Ok(())
558			}
559			Err(ExitError::Exception(_)) => {
560				machine.stack.push(U256::ZERO)?;
561				Ok(())
562			}
563			Err(ExitError::Fatal(e)) => {
564				machine.stack.push(U256::ZERO)?;
565				Err(e.into())
566			}
567		};
568
569		match ret {
570			Ok(()) => {
571				machine.state.as_mut().retbuf = retbuf;
572				Ok(())
573			}
574			Err(e) => Err(e),
575		}
576	}
577}