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::{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> {
22 type Rest;
24
25 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
37pub enum CallCreateOpcode {
39 Call,
41 CallCode,
43 DelegateCall,
45 StaticCall,
47 Create,
49 Create2,
51}
52
53#[derive(Debug)]
55pub enum CallCreateTrap {
56 Call(CallTrap),
58 Create(CreateTrap),
60}
61
62impl CallCreateTrap {
63 #[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 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 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#[derive(Clone, Copy, Eq, PartialEq, Debug)]
109pub enum CallScheme {
110 Call,
112 CallCode,
114 DelegateCall,
116 StaticCall,
118}
119
120#[derive(Debug)]
122pub struct CallTrap {
123 pub target: H160,
125 pub transfer: Option<Transfer>,
127 pub input: Vec<u8>,
129 pub gas: U256,
131 pub is_static: bool,
133 pub out_offset: U256,
135 pub out_len: U256,
137 pub context: Context,
139 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 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 #[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#[derive(Debug)]
290pub struct CallFeedback {
291 pub trap: CallTrap,
293 pub reason: ExitResult,
295 pub retbuf: Vec<u8>,
297}
298
299impl CallFeedback {
300 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#[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 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 #[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#[derive(Debug)]
422pub struct CreateTrap {
423 pub scheme: CreateScheme,
425 pub value: U256,
427 pub code: Vec<u8>,
429}
430
431impl CreateTrap {
432 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 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#[derive(Debug)]
525pub struct CreateFeedback {
526 pub trap: CreateTrap,
528 pub reason: Result<H160, ExitError>,
530 pub retbuf: Vec<u8>,
532}
533
534impl CreateFeedback {
535 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}