1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, Bytes, U256};
5use alloy_sol_types::SolInterface;
6use arb_context::ArbPrecompileCtx;
7use arb_storage::{ARBOS_STATE_ADDRESS, STORAGE_READ_GAS, STORAGE_WRITE_GAS, write_cost};
8use arbos::address_table::AddressTableError;
9use revm::precompile::{PrecompileId, PrecompileOutput, PrecompileResult};
10
11use crate::{ArbPrecompileError, interfaces::IArbAddressTable};
12
13pub const ARBADDRESSTABLE_ADDRESS: Address = Address::new([
15 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16 0x00, 0x00, 0x00, 0x66,
17]);
18
19const SLOAD_GAS: u64 = STORAGE_READ_GAS;
20const SSTORE_GAS: u64 = STORAGE_WRITE_GAS;
21const COPY_GAS: u64 = 3;
22
23pub fn create_arbaddresstable_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
24 DynPrecompile::new_stateful(PrecompileId::custom("arbaddresstable"), move |input| {
25 handler(input, &ctx)
26 })
27}
28
29fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
30 let mut gas_used = 0u64;
31 let gas_limit = input.gas;
32 crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
33
34 let call = match IArbAddressTable::ArbAddressTableCalls::abi_decode(input.data) {
35 Ok(c) => c,
36 Err(_) => return crate::burn_all_revert(gas_limit),
37 };
38 if let Some(r) = crate::reject_nonpayable_value(input.value, input.data, gas_limit, &[]) {
39 return r;
40 }
41 if let Some(r) = crate::reject_static_write(
42 input.is_static,
43 input.data,
44 gas_limit,
45 &[[0xf6, 0xa4, 0x55, 0xa2], [0x44, 0x20, 0xe4, 0x86]],
46 ) {
47 return r;
48 }
49 if let Some(r) = crate::reject_delegate_nonpure(
50 input.target_address != input.bytecode_address,
51 input.data,
52 gas_limit,
53 &[],
54 ) {
55 return r;
56 }
57
58 use IArbAddressTable::ArbAddressTableCalls as Calls;
59 let result = match call {
60 Calls::size(_) => handle_size(&mut input, &mut gas_used, ctx),
61 Calls::addressExists(c) => handle_address_exists(&mut input, &mut gas_used, c.addr, ctx),
62 Calls::lookup(c) => handle_lookup(&mut input, &mut gas_used, c.addr, ctx),
63 Calls::lookupIndex(c) => handle_lookup_index(&mut input, &mut gas_used, c.index, ctx),
64 Calls::register(c) => handle_register(&mut input, &mut gas_used, c.addr, ctx),
65 Calls::compress(c) => handle_compress(&mut input, &mut gas_used, c.addr, ctx),
66 Calls::decompress(c) => handle_decompress(&mut input, &mut gas_used, &c.buf, c.offset, ctx),
67 };
68 crate::gas_check(ctx, gas_limit, gas_used, result)
69}
70
71fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), ArbPrecompileError> {
72 input
73 .internals_mut()
74 .load_account(ARBOS_STATE_ADDRESS)
75 .map_err(ArbPrecompileError::fatal)?;
76 Ok(())
77}
78
79fn handle_size(
80 input: &mut PrecompileInput<'_>,
81 gas_used: &mut u64,
82 ctx: &ArbPrecompileCtx,
83) -> PrecompileResult {
84 let gas_limit = input.gas;
85 load_arbos(input)?;
86 let internals = input.internals_mut();
87 let arb_state = ctx
88 .block
89 .arbos_state(internals)
90 .map_err(ArbPrecompileError::fatal)?;
91 let size = arb_state
92 .address_table
93 .size(internals)
94 .map_err(ArbPrecompileError::fatal)?;
95
96 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
97 crate::charge_computation(gas_used, ctx, COPY_GAS);
98 Ok(PrecompileOutput::new(
99 (*gas_used).min(gas_limit),
100 U256::from(size).to_be_bytes::<32>().to_vec().into(),
101 ))
102}
103
104fn handle_address_exists(
105 input: &mut PrecompileInput<'_>,
106 gas_used: &mut u64,
107 addr: Address,
108 ctx: &ArbPrecompileCtx,
109) -> PrecompileResult {
110 let gas_limit = input.gas;
111 load_arbos(input)?;
112 let internals = input.internals_mut();
113 let arb_state = ctx
114 .block
115 .arbos_state(internals)
116 .map_err(ArbPrecompileError::fatal)?;
117 let exists = arb_state
118 .address_table
119 .address_exists(internals, addr)
120 .map_err(ArbPrecompileError::fatal)?;
121 let value = if exists { U256::from(1u64) } else { U256::ZERO };
122
123 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
124 crate::charge_computation(gas_used, ctx, COPY_GAS);
125 Ok(PrecompileOutput::new(
126 (*gas_used).min(gas_limit),
127 value.to_be_bytes::<32>().to_vec().into(),
128 ))
129}
130
131fn handle_lookup(
132 input: &mut PrecompileInput<'_>,
133 gas_used: &mut u64,
134 addr: Address,
135 ctx: &ArbPrecompileCtx,
136) -> PrecompileResult {
137 let gas_limit = input.gas;
138 load_arbos(input)?;
139 let internals = input.internals_mut();
140 let arb_state = ctx
141 .block
142 .arbos_state(internals)
143 .map_err(ArbPrecompileError::fatal)?;
144 let (index, exists) = arb_state
145 .address_table
146 .lookup(internals, addr)
147 .map_err(ArbPrecompileError::fatal)?;
148 if !exists {
149 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
150 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
151 }
152
153 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
154 crate::charge_computation(gas_used, ctx, COPY_GAS);
155 Ok(PrecompileOutput::new(
156 (*gas_used).min(gas_limit),
157 U256::from(index).to_be_bytes::<32>().to_vec().into(),
158 ))
159}
160
161fn handle_lookup_index(
162 input: &mut PrecompileInput<'_>,
163 gas_used: &mut u64,
164 index_u256: U256,
165 ctx: &ArbPrecompileCtx,
166) -> PrecompileResult {
167 let gas_limit = input.gas;
168 let index: u64 = index_u256
169 .try_into()
170 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
171 load_arbos(input)?;
172 let internals = input.internals_mut();
173 let arb_state = ctx
174 .block
175 .arbos_state(internals)
176 .map_err(ArbPrecompileError::fatal)?;
177 let addr = match arb_state
178 .address_table
179 .lookup_index(internals, index)
180 .map_err(ArbPrecompileError::fatal)?
181 {
182 Some(a) => a,
183 None => {
184 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
185 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
186 }
187 };
188
189 let mut out = [0u8; 32];
190 out[12..32].copy_from_slice(addr.as_slice());
191
192 crate::charge_storage_read(gas_used, ctx, 2 * SLOAD_GAS);
193 crate::charge_computation(gas_used, ctx, COPY_GAS);
194 Ok(PrecompileOutput::new(
195 (*gas_used).min(gas_limit),
196 out.to_vec().into(),
197 ))
198}
199
200fn handle_register(
201 input: &mut PrecompileInput<'_>,
202 gas_used: &mut u64,
203 addr: Address,
204 ctx: &ArbPrecompileCtx,
205) -> PrecompileResult {
206 let gas_limit = input.gas;
207 load_arbos(input)?;
208 let internals = input.internals_mut();
209 let arb_state = ctx
210 .block
211 .arbos_state(internals)
212 .map_err(ArbPrecompileError::fatal)?;
213
214 let (index, already_registered) = arb_state
215 .address_table
216 .register(internals, addr)
217 .map_err(ArbPrecompileError::fatal)?;
218
219 if already_registered {
220 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
221 crate::charge_computation(gas_used, ctx, COPY_GAS);
222 return Ok(PrecompileOutput::new(
223 (*gas_used).min(gas_limit),
224 U256::from(index).to_be_bytes::<32>().to_vec().into(),
225 ));
226 }
227
228 crate::charge_storage_read(gas_used, ctx, 2 * SLOAD_GAS);
229 crate::charge_storage_write(gas_used, ctx, 2 * SSTORE_GAS + write_cost(addr.is_zero()));
230 crate::charge_computation(gas_used, ctx, COPY_GAS);
231
232 Ok(PrecompileOutput::new(
233 (*gas_used).min(gas_limit),
234 U256::from(index).to_be_bytes::<32>().to_vec().into(),
235 ))
236}
237
238fn handle_compress(
239 input: &mut PrecompileInput<'_>,
240 gas_used: &mut u64,
241 addr: Address,
242 ctx: &ArbPrecompileCtx,
243) -> PrecompileResult {
244 let gas_limit = input.gas;
245 load_arbos(input)?;
246 let internals = input.internals_mut();
247 let arb_state = ctx
248 .block
249 .arbos_state(internals)
250 .map_err(ArbPrecompileError::fatal)?;
251 let rlp_bytes = arb_state
252 .address_table
253 .compress(internals, addr)
254 .map_err(ArbPrecompileError::fatal)?;
255
256 let mut output = Vec::with_capacity(96);
257 output.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
258 output.extend_from_slice(&U256::from(rlp_bytes.len() as u64).to_be_bytes::<32>());
259 output.extend_from_slice(&rlp_bytes);
260 let pad = (32 - rlp_bytes.len() % 32) % 32;
261 output.extend(std::iter::repeat_n(0u8, pad));
262
263 let result_words = (output.len() as u64).div_ceil(32);
264 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
265 crate::charge_computation(gas_used, ctx, result_words * COPY_GAS);
266 Ok(PrecompileOutput::new(
267 (*gas_used).min(gas_limit),
268 output.into(),
269 ))
270}
271
272fn handle_decompress(
273 input: &mut PrecompileInput<'_>,
274 gas_used: &mut u64,
275 buf: &Bytes,
276 offset: U256,
277 ctx: &ArbPrecompileCtx,
278) -> PrecompileResult {
279 let gas_limit = input.gas;
280 let ioffset: usize = offset
281 .try_into()
282 .map_err(|_| ArbPrecompileError::empty_revert(*gas_used))?;
283
284 if ioffset >= buf.len() {
285 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
286 }
287 let slice = &buf[ioffset..];
288
289 load_arbos(input)?;
290 let internals = input.internals_mut();
291 let arb_state = ctx
292 .block
293 .arbos_state(internals)
294 .map_err(ArbPrecompileError::fatal)?;
295
296 let (addr, bytes_read, raw_address) = match arb_state.address_table.decompress(internals, slice)
297 {
298 Ok(v) => v,
299 Err(AddressTableError::Storage(s)) => return Err(ArbPrecompileError::fatal(s).into()),
300 Err(AddressTableError::IndexOutOfRange(_)) => {
301 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
302 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
303 }
304 Err(AddressTableError::InvalidEncoding) => {
305 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
306 }
307 };
308
309 let mut output = Vec::with_capacity(64);
310 output.extend_from_slice(&alloy_primitives::B256::left_padding_from(addr.as_slice()).0);
311 output.extend_from_slice(&U256::from(bytes_read).to_be_bytes::<32>());
312
313 let body_sloads: u64 = if raw_address { 0 } else { 2 };
316 crate::charge_storage_read(gas_used, ctx, body_sloads * SLOAD_GAS);
317 crate::charge_computation(gas_used, ctx, 2 * COPY_GAS);
318 Ok(PrecompileOutput::new(
319 (*gas_used).min(gas_limit),
320 output.into(),
321 ))
322}