1use 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#[derive(Debug)]
40pub enum CallCreateTrap {
41 Call(CallTrap),
43 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
91pub enum CallScheme {
92 Call,
94 CallCode,
96 DelegateCall,
98 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
332pub enum CreateScheme {
333 Legacy {
335 caller: H160,
337 },
338 Create2 {
340 caller: H160,
342 code_hash: H256,
344 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}