evm_interpreter/
trap.rs

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