1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, Bytes, U256};
3use alloy_sol_types::SolInterface;
4use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
5
6use crate::{
7 interfaces::IArbAddressTable,
8 storage_slot::{
9 derive_subspace_key, map_slot, map_slot_b256, ADDRESS_TABLE_SUBSPACE, ARBOS_STATE_ADDRESS,
10 ROOT_STORAGE_KEY,
11 },
12};
13
14pub const ARBADDRESSTABLE_ADDRESS: Address = Address::new([
16 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
17 0x00, 0x00, 0x00, 0x66,
18]);
19
20const SLOAD_GAS: u64 = 800;
21const SSTORE_GAS: u64 = 20_000;
22const COPY_GAS: u64 = 3;
23
24pub fn create_arbaddresstable_precompile() -> DynPrecompile {
25 DynPrecompile::new_stateful(PrecompileId::custom("arbaddresstable"), handler)
26}
27
28fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
29 let gas_limit = input.gas;
30 crate::init_precompile_gas(input.data.len());
31
32 let call = match IArbAddressTable::ArbAddressTableCalls::abi_decode(input.data) {
33 Ok(c) => c,
34 Err(_) => return crate::burn_all_revert(gas_limit),
35 };
36
37 use IArbAddressTable::ArbAddressTableCalls as Calls;
38 let result = match call {
39 Calls::size(_) => handle_size(&mut input),
40 Calls::addressExists(c) => handle_address_exists(&mut input, c.addr),
41 Calls::lookup(c) => handle_lookup(&mut input, c.addr),
42 Calls::lookupIndex(c) => handle_lookup_index(&mut input, c.index),
43 Calls::register(c) => handle_register(&mut input, c.addr),
44 Calls::compress(c) => handle_compress(&mut input, c.addr),
45 Calls::decompress(c) => handle_decompress(&mut input, &c.buf, c.offset),
46 };
47 crate::gas_check(gas_limit, result)
48}
49
50fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), PrecompileError> {
53 input
54 .internals_mut()
55 .load_account(ARBOS_STATE_ADDRESS)
56 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
57 Ok(())
58}
59
60fn sload_field(input: &mut PrecompileInput<'_>, slot: U256) -> Result<U256, PrecompileError> {
61 let val = input
62 .internals_mut()
63 .sload(ARBOS_STATE_ADDRESS, slot)
64 .map_err(|_| PrecompileError::other("sload failed"))?;
65 Ok(val.data)
66}
67
68fn sstore_field(
69 input: &mut PrecompileInput<'_>,
70 slot: U256,
71 value: U256,
72) -> Result<(), PrecompileError> {
73 input
74 .internals_mut()
75 .sstore(ARBOS_STATE_ADDRESS, slot, value)
76 .map_err(|_| PrecompileError::other("sstore failed"))?;
77 Ok(())
78}
79
80fn handle_size(input: &mut PrecompileInput<'_>) -> PrecompileResult {
82 let gas_limit = input.gas;
83 load_arbos(input)?;
84
85 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
86 let size_slot = map_slot(table_key.as_slice(), 0);
87 let size = sload_field(input, size_slot)?;
88
89 Ok(PrecompileOutput::new(
90 (2 * SLOAD_GAS + COPY_GAS).min(gas_limit),
91 size.to_be_bytes::<32>().to_vec().into(),
92 ))
93}
94
95fn handle_address_exists(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
96 let gas_limit = input.gas;
97 load_arbos(input)?;
98
99 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
101 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
102
103 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
104 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
105
106 let value = sload_field(input, member_slot)?;
107 let exists = if value != U256::ZERO {
108 U256::from(1u64)
109 } else {
110 U256::ZERO
111 };
112
113 Ok(PrecompileOutput::new(
115 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
116 exists.to_be_bytes::<32>().to_vec().into(),
117 ))
118}
119
120fn handle_lookup(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
121 let gas_limit = input.gas;
122 load_arbos(input)?;
123
124 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
125 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
126
127 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
128 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
129
130 let value = sload_field(input, member_slot)?;
131 if value == U256::ZERO {
132 return Err(PrecompileError::other(
133 "address does not exist in AddressTable",
134 ));
135 }
136
137 let index = value.wrapping_sub(U256::from(1u64));
139 Ok(PrecompileOutput::new(
141 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
142 index.to_be_bytes::<32>().to_vec().into(),
143 ))
144}
145
146fn handle_lookup_index(input: &mut PrecompileInput<'_>, index_u256: U256) -> PrecompileResult {
148 let gas_limit = input.gas;
149 let index: u64 = index_u256
150 .try_into()
151 .map_err(|_| PrecompileError::other("index too large"))?;
152 load_arbos(input)?;
153
154 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
155 let entry_slot = map_slot(table_key.as_slice(), index + 1);
157 let value = sload_field(input, entry_slot)?;
158
159 if value == U256::ZERO {
160 return Err(PrecompileError::other(
161 "index does not exist in AddressTable",
162 ));
163 }
164
165 Ok(PrecompileOutput::new(
167 (3 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
168 value.to_be_bytes::<32>().to_vec().into(),
169 ))
170}
171
172fn handle_register(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
175 let gas_limit = input.gas;
176 load_arbos(input)?;
177
178 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
179 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
180 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
181
182 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
184 let existing = sload_field(input, member_slot)?;
185
186 if existing != U256::ZERO {
187 let index = existing.wrapping_sub(U256::from(1u64));
189 return Ok(PrecompileOutput::new(
191 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
192 index.to_be_bytes::<32>().to_vec().into(),
193 ));
194 }
195
196 let num_items_slot = map_slot(table_key.as_slice(), 0);
199 let num_items = sload_field(input, num_items_slot)?;
200 let num_items_u64: u64 = num_items
201 .try_into()
202 .map_err(|_| PrecompileError::other("invalid numItems"))?;
203 let new_num_items = num_items_u64 + 1;
204 sstore_field(input, num_items_slot, U256::from(new_num_items))?;
205
206 let reverse_slot = map_slot(table_key.as_slice(), new_num_items);
208 sstore_field(input, reverse_slot, U256::from_be_bytes(addr_as_b256.0))?;
209
210 sstore_field(input, member_slot, U256::from(new_num_items))?;
212
213 let index = new_num_items - 1;
215
216 let gas_used = 3 * SLOAD_GAS + 3 * SSTORE_GAS + 2 * COPY_GAS;
218
219 Ok(PrecompileOutput::new(
220 gas_used.min(gas_limit),
221 U256::from(index).to_be_bytes::<32>().to_vec().into(),
222 ))
223}
224
225fn handle_compress(input: &mut PrecompileInput<'_>, addr: Address) -> PrecompileResult {
226 let gas_limit = input.gas;
227 load_arbos(input)?;
228
229 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
230 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
231 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
232 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
233 let value = sload_field(input, member_slot)?;
234
235 let rlp_bytes = if value != U256::ZERO {
236 let index = value.wrapping_sub(U256::from(1u64)).to::<u64>();
238 rlp_encode_u64(index)
239 } else {
240 rlp_encode_bytes(addr.as_slice())
242 };
243
244 let mut output = Vec::with_capacity(96);
246 output.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
247 output.extend_from_slice(&U256::from(rlp_bytes.len() as u64).to_be_bytes::<32>());
248 output.extend_from_slice(&rlp_bytes);
249 let pad = (32 - rlp_bytes.len() % 32) % 32;
251 output.extend(std::iter::repeat_n(0u8, pad));
252
253 let result_words = (output.len() as u64).div_ceil(32);
255 Ok(PrecompileOutput::new(
256 (2 * SLOAD_GAS + COPY_GAS + result_words * COPY_GAS).min(gas_limit),
257 output.into(),
258 ))
259}
260
261fn handle_decompress(
264 input: &mut PrecompileInput<'_>,
265 buf: &Bytes,
266 offset: U256,
267) -> PrecompileResult {
268 let gas_limit = input.gas;
269 let data_len = input.data.len();
270 let ioffset: usize = offset
271 .try_into()
272 .map_err(|_| PrecompileError::other("offset too large"))?;
273
274 if ioffset >= buf.len() {
275 return Err(PrecompileError::other("offset out of bounds"));
276 }
277 let slice = &buf[ioffset..];
278
279 load_arbos(input)?;
280
281 let (decoded, bytes_read) =
283 rlp_decode_bytes(slice).map_err(|_| PrecompileError::other("RLP decode failed"))?;
284
285 let (addr, final_bytes_read) = if decoded.len() == 20 {
286 (Address::from_slice(&decoded), bytes_read)
288 } else {
289 let (index, idx_bytes_read) =
291 rlp_decode_u64(slice).map_err(|_| PrecompileError::other("RLP decode index failed"))?;
292
293 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
294
295 let num_items_slot = map_slot(table_key.as_slice(), 0);
297 let num_items = sload_field(input, num_items_slot)?;
298 if U256::from(index) >= num_items {
299 return Err(PrecompileError::other(
300 "index does not exist in AddressTable",
301 ));
302 }
303
304 let entry_slot = map_slot(table_key.as_slice(), index + 1);
305 let value = sload_field(input, entry_slot)?;
306
307 let value_bytes = value.to_be_bytes::<32>();
309 let result_addr = Address::from_slice(&value_bytes[12..32]);
310 (result_addr, idx_bytes_read)
311 };
312
313 let mut output = Vec::with_capacity(64);
315 output.extend_from_slice(&alloy_primitives::B256::left_padding_from(addr.as_slice()).0);
316 output.extend_from_slice(&U256::from(final_bytes_read as u64).to_be_bytes::<32>());
317
318 let body_sloads: u64 = if decoded.len() == 20 { 1 } else { 3 };
320 let arg_words = (data_len as u64).saturating_sub(4).div_ceil(32);
321 Ok(PrecompileOutput::new(
322 (body_sloads * SLOAD_GAS + (arg_words + 2) * COPY_GAS).min(gas_limit),
323 output.into(),
324 ))
325}
326
327fn rlp_encode_u64(val: u64) -> Vec<u8> {
331 if val == 0 {
332 return vec![0x80];
333 }
334 if val < 128 {
335 return vec![val as u8];
336 }
337 let bytes = val.to_be_bytes();
338 let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
339 let len = 8 - start;
340 let mut out = Vec::with_capacity(1 + len);
341 out.push(0x80 + len as u8);
342 out.extend_from_slice(&bytes[start..]);
343 out
344}
345
346fn rlp_encode_bytes(data: &[u8]) -> Vec<u8> {
348 if data.len() == 1 && data[0] < 128 {
349 return vec![data[0]];
350 }
351 if data.len() < 56 {
352 let mut out = Vec::with_capacity(1 + data.len());
353 out.push(0x80 + data.len() as u8);
354 out.extend_from_slice(data);
355 return out;
356 }
357 let len_bytes = {
358 let l = data.len() as u64;
359 let bytes = l.to_be_bytes();
360 let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
361 bytes[start..].to_vec()
362 };
363 let mut out = Vec::with_capacity(1 + len_bytes.len() + data.len());
364 out.push(0xb7 + len_bytes.len() as u8);
365 out.extend_from_slice(&len_bytes);
366 out.extend_from_slice(data);
367 out
368}
369
370fn rlp_decode_bytes(data: &[u8]) -> Result<(Vec<u8>, usize), &'static str> {
372 if data.is_empty() {
373 return Err("empty input");
374 }
375 let prefix = data[0];
376 if prefix < 0x80 {
377 Ok((vec![prefix], 1))
379 } else if prefix <= 0xb7 {
380 let len = (prefix - 0x80) as usize;
382 if data.len() < 1 + len {
383 return Err("truncated short string");
384 }
385 Ok((data[1..1 + len].to_vec(), 1 + len))
386 } else if prefix <= 0xbf {
387 let len_of_len = (prefix - 0xb7) as usize;
389 if data.len() < 1 + len_of_len {
390 return Err("truncated long string length");
391 }
392 let mut len_bytes = [0u8; 8];
393 len_bytes[8 - len_of_len..].copy_from_slice(&data[1..1 + len_of_len]);
394 let len = u64::from_be_bytes(len_bytes) as usize;
395 let total = 1 + len_of_len + len;
396 if data.len() < total {
397 return Err("truncated long string data");
398 }
399 Ok((data[1 + len_of_len..total].to_vec(), total))
400 } else {
401 Err("unexpected list prefix")
402 }
403}
404
405fn rlp_decode_u64(data: &[u8]) -> Result<(u64, usize), &'static str> {
407 if data.is_empty() {
408 return Err("empty input");
409 }
410 let prefix = data[0];
411 if prefix == 0x80 {
412 Ok((0, 1))
414 } else if prefix < 0x80 {
415 Ok((prefix as u64, 1))
417 } else if prefix <= 0x88 {
418 let len = (prefix - 0x80) as usize;
420 if len > 8 || data.len() < 1 + len {
421 return Err("invalid u64 encoding");
422 }
423 let mut bytes = [0u8; 8];
424 bytes[8 - len..].copy_from_slice(&data[1..1 + len]);
425 Ok((u64::from_be_bytes(bytes), 1 + len))
426 } else {
427 Err("value too large for u64")
428 }
429}