1use 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
16pub trait TrapConsume<T> {
23 type Rest;
25
26 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
38pub enum CallCreateOpcode {
40 Call,
42 CallCode,
44 DelegateCall,
46 StaticCall,
48 Create,
50 Create2,
52}
53
54#[derive(Debug)]
56pub enum CallCreateTrap {
57 Call(CallTrap),
59 Create(CreateTrap),
61}
62
63impl CallCreateTrap {
64 #[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 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 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
110pub enum CallScheme {
111 Call,
113 CallCode,
115 DelegateCall,
117 StaticCall,
119}
120
121#[derive(Debug)]
123pub struct CallTrap {
124 pub target: H160,
126 pub transfer: Option<Transfer>,
128 pub input: Vec<u8>,
130 pub gas: U256,
132 pub is_static: bool,
134 pub out_offset: U256,
136 pub out_len: U256,
138 pub context: Context,
140 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 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 #[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#[derive(Debug)]
291pub struct CallFeedback {
292 pub trap: CallTrap,
294 pub reason: ExitResult,
296 pub retbuf: Vec<u8>,
298}
299
300impl CallFeedback {
301 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
367pub enum CreateScheme {
368 Legacy {
370 caller: H160,
372 },
373 Create2 {
375 caller: H160,
377 code_hash: H256,
379 salt: H256,
381 },
382}
383
384impl CreateScheme {
385 #[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 #[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#[derive(Debug)]
423pub struct CreateTrap {
424 pub scheme: CreateScheme,
426 pub value: U256,
428 pub code: Vec<u8>,
430}
431
432impl CreateTrap {
433 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 #[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#[derive(Debug)]
527pub struct CreateFeedback {
528 pub trap: CreateTrap,
530 pub reason: Result<H160, ExitError>,
532 pub retbuf: Vec<u8>,
534}
535
536impl CreateFeedback {
537 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}