1use alloy_primitives::U256;
2use revm::bytecode::opcode;
3
4#[derive(Debug, PartialEq)]
6pub enum BufferKind {
7 Memory,
8 Calldata,
9 Returndata,
10}
11
12impl BufferKind {
13 pub fn next(&self) -> Self {
15 match self {
16 Self::Memory => Self::Calldata,
17 Self::Calldata => Self::Returndata,
18 Self::Returndata => Self::Memory,
19 }
20 }
21
22 pub fn title(&self, size: usize) -> String {
24 match self {
25 Self::Memory => format!("Memory (max expansion: {size} bytes)"),
26 Self::Calldata => format!("Calldata (size: {size} bytes)"),
27 Self::Returndata => format!("Returndata (size: {size} bytes)"),
28 }
29 }
30}
31
32pub struct BufferAccess {
34 pub offset: usize,
35 pub len: usize,
36}
37
38pub struct BufferAccesses {
40 pub read: Option<(BufferKind, BufferAccess)>,
42 pub write: Option<BufferAccess>,
44}
45
46pub fn get_buffer_accesses(op: u8, stack: &[U256]) -> Option<BufferAccesses> {
59 let buffer_access = match op {
60 opcode::KECCAK256 | opcode::RETURN | opcode::REVERT => {
61 (Some((BufferKind::Memory, 1, 2)), None)
62 }
63 opcode::CALLDATACOPY => (Some((BufferKind::Calldata, 2, 3)), Some((1, 3))),
64 opcode::RETURNDATACOPY => (Some((BufferKind::Returndata, 2, 3)), Some((1, 3))),
65 opcode::CALLDATALOAD => (Some((BufferKind::Calldata, 1, -1)), None),
66 opcode::CODECOPY => (None, Some((1, 3))),
67 opcode::EXTCODECOPY => (None, Some((2, 4))),
68 opcode::MLOAD => (Some((BufferKind::Memory, 1, -1)), None),
69 opcode::MSTORE => (None, Some((1, -1))),
70 opcode::MSTORE8 => (None, Some((1, -2))),
71 opcode::LOG0 | opcode::LOG1 | opcode::LOG2 | opcode::LOG3 | opcode::LOG4 => {
72 (Some((BufferKind::Memory, 1, 2)), None)
73 }
74 opcode::CREATE | opcode::CREATE2 => (Some((BufferKind::Memory, 2, 3)), None),
75 opcode::CALL | opcode::CALLCODE => (Some((BufferKind::Memory, 4, 5)), None),
76 opcode::DELEGATECALL | opcode::STATICCALL => (Some((BufferKind::Memory, 3, 4)), None),
77 opcode::MCOPY => (Some((BufferKind::Memory, 2, 3)), Some((1, 3))),
78 opcode::RETURNDATALOAD => (Some((BufferKind::Returndata, 1, -1)), None),
79 opcode::EOFCREATE => (Some((BufferKind::Memory, 3, 4)), None),
80 opcode::RETURNCONTRACT => (Some((BufferKind::Memory, 1, 2)), None),
81 opcode::DATACOPY => (None, Some((1, 3))),
82 opcode::EXTCALL | opcode::EXTSTATICCALL | opcode::EXTDELEGATECALL => {
83 (Some((BufferKind::Memory, 2, 3)), None)
84 }
85 _ => Default::default(),
86 };
87
88 let stack_len = stack.len();
89 let get_size = |stack_index| match stack_index {
90 -2 => Some(1),
91 -1 => Some(32),
92 0 => None,
93 1.. => {
94 if (stack_index as usize) <= stack_len {
95 Some(stack[stack_len - stack_index as usize].saturating_to())
96 } else {
97 None
98 }
99 }
100 _ => panic!("invalid stack index"),
101 };
102
103 if buffer_access.0.is_some() || buffer_access.1.is_some() {
104 let (read, write) = buffer_access;
105 let read_access = read.and_then(|b| {
106 let (buffer, offset, len) = b;
107 Some((buffer, BufferAccess { offset: get_size(offset)?, len: get_size(len)? }))
108 });
109 let write_access = write.and_then(|b| {
110 let (offset, len) = b;
111 Some(BufferAccess { offset: get_size(offset)?, len: get_size(len)? })
112 });
113 Some(BufferAccesses { read: read_access, write: write_access })
114 } else {
115 None
116 }
117}