1use alloy_primitives::{Address, B256, U256};
2
3pub const EVM_API_METHOD_REQ_OFFSET: u32 = 0x1000_0000;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8#[repr(u8)]
9pub enum ApiStatus {
10 Success = 0,
11 Failure = 1,
12 OutOfGas = 2,
13 WriteProtection = 3,
14}
15
16impl ApiStatus {
17 pub fn from_u8(val: u8) -> Option<Self> {
18 match val {
19 0 => Some(Self::Success),
20 1 => Some(Self::Failure),
21 2 => Some(Self::OutOfGas),
22 3 => Some(Self::WriteProtection),
23 _ => None,
24 }
25 }
26}
27
28pub trait HostIo {
33 type Error;
34
35 fn get_bytes32(&mut self, key: B256) -> Result<(B256, u64), Self::Error>;
37
38 fn set_trie_slots(&mut self, data: &[u8], gas_left: &mut u64)
40 -> Result<ApiStatus, Self::Error>;
41
42 fn get_transient_bytes32(&mut self, key: B256) -> Result<B256, Self::Error>;
44
45 fn set_transient_bytes32(&mut self, key: B256, value: B256) -> Result<ApiStatus, Self::Error>;
47
48 fn contract_call(
50 &mut self,
51 contract: Address,
52 calldata: &[u8],
53 gas_left: u64,
54 gas_req: u64,
55 value: U256,
56 call_type: super::types::RequestType,
57 ) -> Result<(Vec<u8>, u64), Self::Error>;
58
59 fn create(
61 &mut self,
62 code: &[u8],
63 endowment: U256,
64 salt: Option<U256>,
65 gas: u64,
66 ) -> Result<(Address, Vec<u8>, u64), Self::Error>;
67
68 fn emit_log(&mut self, topics: &[B256], data: &[u8]) -> Result<(), Self::Error>;
70
71 fn account_balance(&mut self, address: Address) -> Result<(U256, u64), Self::Error>;
73
74 fn account_code(&mut self, address: Address, gas: u64) -> Result<(Vec<u8>, u64), Self::Error>;
76
77 fn account_code_hash(&mut self, address: Address) -> Result<(B256, u64), Self::Error>;
79
80 fn add_pages(&mut self, pages: u16) -> Result<u64, Self::Error>;
82
83 fn capture_hostio(
85 &mut self,
86 name: &str,
87 args: &[u8],
88 outs: &[u8],
89 start_ink: u64,
90 end_ink: u64,
91 );
92}
93
94pub fn dispatch_request<H: HostIo>(
99 host: &mut H,
100 req: super::types::RequestType,
101 input: &[u8],
102) -> Result<(Vec<u8>, Vec<u8>, u64), H::Error> {
103 use super::types::RequestType;
104
105 let mut parser = RequestParser::new(input);
106
107 match req {
108 RequestType::GetBytes32 => {
109 let key = parser.take_hash().expect("expected hash");
110 let (out, cost) = host.get_bytes32(key)?;
111 Ok((out.as_slice().to_vec(), Vec::new(), cost))
112 }
113 RequestType::SetTrieSlots => {
114 let gas_left = parser.take_u64().expect("expected u64");
115 let mut gas = gas_left;
116 let status = host.set_trie_slots(parser.take_rest(), &mut gas)?;
117 Ok((vec![status as u8], Vec::new(), gas_left - gas))
118 }
119 RequestType::GetTransientBytes32 => {
120 let key = parser.take_hash().expect("expected hash");
121 let out = host.get_transient_bytes32(key)?;
122 Ok((out.as_slice().to_vec(), Vec::new(), 0))
123 }
124 RequestType::SetTransientBytes32 => {
125 let key = parser.take_hash().expect("expected hash");
126 let value = parser.take_hash().expect("expected hash");
127 let status = host.set_transient_bytes32(key, value)?;
128 Ok((vec![status as u8], Vec::new(), 0))
129 }
130 RequestType::ContractCall | RequestType::DelegateCall | RequestType::StaticCall => {
131 let contract = parser.take_address().expect("expected address");
132 let value = parser.take_u256().expect("expected value");
133 let gas_left = parser.take_u64().expect("expected gas");
134 let gas_req = parser.take_u64().expect("expected gas req");
135 let calldata = parser.take_rest();
136
137 let (ret, cost) =
138 host.contract_call(contract, calldata, gas_left, gas_req, value, req)?;
139 let status: u8 = 0; Ok((vec![status], ret, cost))
141 }
142 RequestType::Create1 | RequestType::Create2 => {
143 let gas = parser.take_u64().expect("expected gas");
144 let endowment = parser.take_u256().expect("expected endowment");
145 let salt = if req == RequestType::Create2 {
146 Some(parser.take_u256().expect("expected salt"))
147 } else {
148 None
149 };
150 let code = parser.take_rest();
151
152 let (addr, ret_val, cost) = host.create(code, endowment, salt, gas)?;
153 let mut res = vec![1u8];
154 res.extend_from_slice(addr.as_slice());
155 Ok((res, ret_val, cost))
156 }
157 RequestType::EmitLog => {
158 let topics = parser.take_u32().expect("expected topic count");
159 let mut hashes = Vec::with_capacity(topics as usize);
160 for _ in 0..topics {
161 hashes.push(parser.take_hash().expect("expected topic hash"));
162 }
163 host.emit_log(&hashes, parser.take_rest())?;
164 Ok((Vec::new(), Vec::new(), 0))
165 }
166 RequestType::AccountBalance => {
167 let address = parser.take_address().expect("expected address");
168 let (balance, cost) = host.account_balance(address)?;
169 Ok((balance.to_be_bytes::<32>().to_vec(), Vec::new(), cost))
170 }
171 RequestType::AccountCode => {
172 let address = parser.take_address().expect("expected address");
173 let gas = parser.take_u64().expect("expected gas");
174 let (code, cost) = host.account_code(address, gas)?;
175 Ok((Vec::new(), code, cost))
176 }
177 RequestType::AccountCodeHash => {
178 let address = parser.take_address().expect("expected address");
179 let (hash, cost) = host.account_code_hash(address)?;
180 Ok((hash.as_slice().to_vec(), Vec::new(), cost))
181 }
182 RequestType::AddPages => {
183 let pages = parser.take_u16().expect("expected pages");
184 let cost = host.add_pages(pages)?;
185 Ok((Vec::new(), Vec::new(), cost))
186 }
187 RequestType::CaptureHostIO => {
188 let start_ink = parser.take_u64().expect("expected start ink");
189 let end_ink = parser.take_u64().expect("expected end ink");
190 let name_len = parser.take_u32().expect("expected name len");
191 let args_len = parser.take_u32().expect("expected args len");
192 let outs_len = parser.take_u32().expect("expected outs len");
193 let name_bytes = parser.take_fixed(name_len as usize).expect("expected name");
194 let name = core::str::from_utf8(name_bytes).unwrap_or("");
195 let args = parser.take_fixed(args_len as usize).expect("expected args");
196 let outs = parser.take_fixed(outs_len as usize).expect("expected outs");
197 host.capture_hostio(name, args, outs, start_ink, end_ink);
198 Ok((Vec::new(), Vec::new(), 0))
199 }
200 }
201}
202
203pub struct RequestParser<'a> {
205 data: &'a [u8],
206 offset: usize,
207}
208
209impl<'a> RequestParser<'a> {
210 pub fn new(data: &'a [u8]) -> Self {
211 Self { data, offset: 0 }
212 }
213
214 pub fn remaining(&self) -> &'a [u8] {
215 &self.data[self.offset..]
216 }
217
218 pub fn take_fixed(&mut self, n: usize) -> Option<&'a [u8]> {
219 if self.offset + n > self.data.len() {
220 return None;
221 }
222 let slice = &self.data[self.offset..self.offset + n];
223 self.offset += n;
224 Some(slice)
225 }
226
227 pub fn take_address(&mut self) -> Option<Address> {
228 let bytes = self.take_fixed(20)?;
229 Some(Address::from_slice(bytes))
230 }
231
232 pub fn take_hash(&mut self) -> Option<B256> {
233 let bytes = self.take_fixed(32)?;
234 Some(B256::from_slice(bytes))
235 }
236
237 pub fn take_u256(&mut self) -> Option<U256> {
238 let bytes = self.take_fixed(32)?;
239 Some(U256::from_be_slice(bytes))
240 }
241
242 pub fn take_u64(&mut self) -> Option<u64> {
243 let bytes = self.take_fixed(8)?;
244 Some(u64::from_be_bytes(bytes.try_into().ok()?))
245 }
246
247 pub fn take_u32(&mut self) -> Option<u32> {
248 let bytes = self.take_fixed(4)?;
249 Some(u32::from_be_bytes(bytes.try_into().ok()?))
250 }
251
252 pub fn take_u16(&mut self) -> Option<u16> {
253 let bytes = self.take_fixed(2)?;
254 Some(u16::from_be_bytes(bytes.try_into().ok()?))
255 }
256
257 pub fn take_rest(&mut self) -> &'a [u8] {
258 let rest = &self.data[self.offset..];
259 self.offset = self.data.len();
260 rest
261 }
262}