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 #[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 stream.append(&nonce);
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: u256_to_h256(*salt),
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(h256_to_u256(address.into()))?;
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}