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