1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, U256};
3use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
4
5use crate::storage_slot::{
6 derive_subspace_key, map_slot, map_slot_b256, ADDRESS_TABLE_SUBSPACE, ARBOS_STATE_ADDRESS,
7 ROOT_STORAGE_KEY,
8};
9
10pub const ARBADDRESSTABLE_ADDRESS: Address = Address::new([
12 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13 0x00, 0x00, 0x00, 0x66,
14]);
15
16const ADDRESS_EXISTS: [u8; 4] = [0xa5, 0x02, 0x52, 0x22];
18const COMPRESS: [u8; 4] = [0xf6, 0xa4, 0x55, 0xa2];
19const DECOMPRESS: [u8; 4] = [0x31, 0x86, 0x2a, 0xda];
20const LOOKUP: [u8; 4] = [0xd4, 0xb6, 0xb5, 0xda];
21const LOOKUP_INDEX: [u8; 4] = [0x8a, 0x18, 0x67, 0x88];
22const REGISTER: [u8; 4] = [0x44, 0x20, 0xe4, 0x86];
23const SIZE: [u8; 4] = [0x94, 0x9d, 0x22, 0x5d];
24
25const SLOAD_GAS: u64 = 800;
26const SSTORE_GAS: u64 = 20_000;
27const COPY_GAS: u64 = 3;
28
29pub fn create_arbaddresstable_precompile() -> DynPrecompile {
30 DynPrecompile::new_stateful(PrecompileId::custom("arbaddresstable"), handler)
31}
32
33fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
34 let gas_limit = input.gas;
35 let data = input.data;
36 if data.len() < 4 {
37 return Err(PrecompileError::other("input too short"));
38 }
39
40 let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
41
42 let result = match selector {
43 SIZE => handle_size(&mut input),
44 ADDRESS_EXISTS => handle_address_exists(&mut input),
45 LOOKUP => handle_lookup(&mut input),
46 LOOKUP_INDEX => handle_lookup_index(&mut input),
47 REGISTER => handle_register(&mut input),
48 COMPRESS => handle_compress(&mut input),
49 DECOMPRESS => handle_decompress(&mut input),
50 _ => Err(PrecompileError::other("unknown ArbAddressTable selector")),
51 };
52 crate::gas_check(gas_limit, result)
53}
54
55fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), PrecompileError> {
58 input
59 .internals_mut()
60 .load_account(ARBOS_STATE_ADDRESS)
61 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
62 Ok(())
63}
64
65fn sload_field(input: &mut PrecompileInput<'_>, slot: U256) -> Result<U256, PrecompileError> {
66 let val = input
67 .internals_mut()
68 .sload(ARBOS_STATE_ADDRESS, slot)
69 .map_err(|_| PrecompileError::other("sload failed"))?;
70 Ok(val.data)
71}
72
73fn sstore_field(
74 input: &mut PrecompileInput<'_>,
75 slot: U256,
76 value: U256,
77) -> Result<(), PrecompileError> {
78 input
79 .internals_mut()
80 .sstore(ARBOS_STATE_ADDRESS, slot, value)
81 .map_err(|_| PrecompileError::other("sstore failed"))?;
82 Ok(())
83}
84
85fn handle_size(input: &mut PrecompileInput<'_>) -> PrecompileResult {
87 let gas_limit = input.gas;
88 load_arbos(input)?;
89
90 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
91 let size_slot = map_slot(table_key.as_slice(), 0);
92 let size = sload_field(input, size_slot)?;
93
94 Ok(PrecompileOutput::new(
95 (2 * SLOAD_GAS + COPY_GAS).min(gas_limit),
96 size.to_be_bytes::<32>().to_vec().into(),
97 ))
98}
99
100fn handle_address_exists(input: &mut PrecompileInput<'_>) -> PrecompileResult {
102 let data = input.data;
103 if data.len() < 36 {
104 return Err(PrecompileError::other("input too short"));
105 }
106
107 let gas_limit = input.gas;
108 let addr = Address::from_slice(&data[16..36]);
109 load_arbos(input)?;
110
111 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
113 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
114
115 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
116 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
117
118 let value = sload_field(input, member_slot)?;
119 let exists = if value != U256::ZERO {
120 U256::from(1u64)
121 } else {
122 U256::ZERO
123 };
124
125 Ok(PrecompileOutput::new(
127 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
128 exists.to_be_bytes::<32>().to_vec().into(),
129 ))
130}
131
132fn handle_lookup(input: &mut PrecompileInput<'_>) -> PrecompileResult {
134 let data = input.data;
135 if data.len() < 36 {
136 return Err(PrecompileError::other("input too short"));
137 }
138
139 let gas_limit = input.gas;
140 let addr = Address::from_slice(&data[16..36]);
141 load_arbos(input)?;
142
143 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
144 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
145
146 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
147 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
148
149 let value = sload_field(input, member_slot)?;
150 if value == U256::ZERO {
151 return Err(PrecompileError::other(
152 "address does not exist in AddressTable",
153 ));
154 }
155
156 let index = value.wrapping_sub(U256::from(1u64));
158 Ok(PrecompileOutput::new(
160 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
161 index.to_be_bytes::<32>().to_vec().into(),
162 ))
163}
164
165fn handle_lookup_index(input: &mut PrecompileInput<'_>) -> PrecompileResult {
168 let data = input.data;
169 if data.len() < 36 {
170 return Err(PrecompileError::other("input too short"));
171 }
172
173 let gas_limit = input.gas;
174 let index: u64 = U256::from_be_slice(&data[4..36])
175 .try_into()
176 .map_err(|_| PrecompileError::other("index too large"))?;
177 load_arbos(input)?;
178
179 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
180 let entry_slot = map_slot(table_key.as_slice(), index + 1);
182 let value = sload_field(input, entry_slot)?;
183
184 if value == U256::ZERO {
185 return Err(PrecompileError::other(
186 "index does not exist in AddressTable",
187 ));
188 }
189
190 Ok(PrecompileOutput::new(
192 (3 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
193 value.to_be_bytes::<32>().to_vec().into(),
194 ))
195}
196
197fn handle_register(input: &mut PrecompileInput<'_>) -> PrecompileResult {
205 let data = input.data;
206 if data.len() < 36 {
207 return Err(PrecompileError::other("input too short"));
208 }
209
210 let gas_limit = input.gas;
211 let addr = Address::from_slice(&data[16..36]);
212 load_arbos(input)?;
213
214 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
215 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
216 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
217
218 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
220 let existing = sload_field(input, member_slot)?;
221
222 if existing != U256::ZERO {
223 let index = existing.wrapping_sub(U256::from(1u64));
225 return Ok(PrecompileOutput::new(
227 (2 * SLOAD_GAS + 2 * COPY_GAS).min(gas_limit),
228 index.to_be_bytes::<32>().to_vec().into(),
229 ));
230 }
231
232 let num_items_slot = map_slot(table_key.as_slice(), 0);
235 let num_items = sload_field(input, num_items_slot)?;
236 let num_items_u64: u64 = num_items
237 .try_into()
238 .map_err(|_| PrecompileError::other("invalid numItems"))?;
239 let new_num_items = num_items_u64 + 1;
240 sstore_field(input, num_items_slot, U256::from(new_num_items))?;
241
242 let reverse_slot = map_slot(table_key.as_slice(), new_num_items);
244 sstore_field(input, reverse_slot, U256::from_be_bytes(addr_as_b256.0))?;
245
246 sstore_field(input, member_slot, U256::from(new_num_items))?;
248
249 let index = new_num_items - 1;
251
252 let gas_used = 3 * SLOAD_GAS + 3 * SSTORE_GAS + 2 * COPY_GAS;
254
255 Ok(PrecompileOutput::new(
256 gas_used.min(gas_limit),
257 U256::from(index).to_be_bytes::<32>().to_vec().into(),
258 ))
259}
260
261fn handle_compress(input: &mut PrecompileInput<'_>) -> PrecompileResult {
264 let data = input.data;
265 if data.len() < 36 {
266 return Err(PrecompileError::other("input too short"));
267 }
268
269 let gas_limit = input.gas;
270 let addr = Address::from_slice(&data[16..36]);
271 load_arbos(input)?;
272
273 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
274 let by_address_key = derive_subspace_key(table_key.as_slice(), &[]);
275 let addr_as_b256 = alloy_primitives::B256::left_padding_from(addr.as_slice());
276 let member_slot = map_slot_b256(by_address_key.as_slice(), &addr_as_b256);
277 let value = sload_field(input, member_slot)?;
278
279 let rlp_bytes = if value != U256::ZERO {
280 let index = value.wrapping_sub(U256::from(1u64)).to::<u64>();
282 rlp_encode_u64(index)
283 } else {
284 rlp_encode_bytes(addr.as_slice())
286 };
287
288 let mut output = Vec::with_capacity(96);
290 output.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
291 output.extend_from_slice(&U256::from(rlp_bytes.len() as u64).to_be_bytes::<32>());
292 output.extend_from_slice(&rlp_bytes);
293 let pad = (32 - rlp_bytes.len() % 32) % 32;
295 output.extend(std::iter::repeat_n(0u8, pad));
296
297 let result_words = (output.len() as u64).div_ceil(32);
299 Ok(PrecompileOutput::new(
300 (2 * SLOAD_GAS + COPY_GAS + result_words * COPY_GAS).min(gas_limit),
301 output.into(),
302 ))
303}
304
305fn handle_decompress(input: &mut PrecompileInput<'_>) -> PrecompileResult {
309 let data = input.data;
310 if data.len() < 68 {
314 return Err(PrecompileError::other("input too short"));
315 }
316
317 let gas_limit = input.gas;
318
319 let bytes_offset = U256::from_be_slice(&data[4..36]).to::<usize>();
321 let ioffset = U256::from_be_slice(&data[36..68]).to::<usize>();
322
323 let bytes_start = 4 + bytes_offset;
325 if data.len() < bytes_start + 32 {
326 return Err(PrecompileError::other("invalid bytes offset"));
327 }
328 let bytes_len = U256::from_be_slice(&data[bytes_start..bytes_start + 32]).to::<usize>();
329 let bytes_data_start = bytes_start + 32;
330 if data.len() < bytes_data_start + bytes_len {
331 return Err(PrecompileError::other("bytes data truncated"));
332 }
333 let buf = &data[bytes_data_start..bytes_data_start + bytes_len];
334
335 if ioffset >= buf.len() {
336 return Err(PrecompileError::other("offset out of bounds"));
337 }
338 let slice = &buf[ioffset..];
339
340 load_arbos(input)?;
341
342 let (decoded, bytes_read) =
344 rlp_decode_bytes(slice).map_err(|_| PrecompileError::other("RLP decode failed"))?;
345
346 let (addr, final_bytes_read) = if decoded.len() == 20 {
347 (Address::from_slice(&decoded), bytes_read)
349 } else {
350 let (index, idx_bytes_read) =
352 rlp_decode_u64(slice).map_err(|_| PrecompileError::other("RLP decode index failed"))?;
353
354 let table_key = derive_subspace_key(ROOT_STORAGE_KEY, ADDRESS_TABLE_SUBSPACE);
355
356 let num_items_slot = map_slot(table_key.as_slice(), 0);
358 let num_items = sload_field(input, num_items_slot)?;
359 if U256::from(index) >= num_items {
360 return Err(PrecompileError::other(
361 "index does not exist in AddressTable",
362 ));
363 }
364
365 let entry_slot = map_slot(table_key.as_slice(), index + 1);
366 let value = sload_field(input, entry_slot)?;
367
368 let value_bytes = value.to_be_bytes::<32>();
370 let result_addr = Address::from_slice(&value_bytes[12..32]);
371 (result_addr, idx_bytes_read)
372 };
373
374 let mut output = Vec::with_capacity(64);
376 output.extend_from_slice(&alloy_primitives::B256::left_padding_from(addr.as_slice()).0);
377 output.extend_from_slice(&U256::from(final_bytes_read as u64).to_be_bytes::<32>());
378
379 let body_sloads: u64 = if decoded.len() == 20 { 1 } else { 3 };
382 let arg_words = (input.data.len() as u64).saturating_sub(4).div_ceil(32);
383 Ok(PrecompileOutput::new(
384 (body_sloads * SLOAD_GAS + (arg_words + 2) * COPY_GAS).min(gas_limit),
385 output.into(),
386 ))
387}
388
389fn rlp_encode_u64(val: u64) -> Vec<u8> {
393 if val == 0 {
394 return vec![0x80];
395 }
396 if val < 128 {
397 return vec![val as u8];
398 }
399 let bytes = val.to_be_bytes();
400 let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
401 let len = 8 - start;
402 let mut out = Vec::with_capacity(1 + len);
403 out.push(0x80 + len as u8);
404 out.extend_from_slice(&bytes[start..]);
405 out
406}
407
408fn rlp_encode_bytes(data: &[u8]) -> Vec<u8> {
410 if data.len() == 1 && data[0] < 128 {
411 return vec![data[0]];
412 }
413 if data.len() < 56 {
414 let mut out = Vec::with_capacity(1 + data.len());
415 out.push(0x80 + data.len() as u8);
416 out.extend_from_slice(data);
417 return out;
418 }
419 let len_bytes = {
420 let l = data.len() as u64;
421 let bytes = l.to_be_bytes();
422 let start = bytes.iter().position(|&b| b != 0).unwrap_or(7);
423 bytes[start..].to_vec()
424 };
425 let mut out = Vec::with_capacity(1 + len_bytes.len() + data.len());
426 out.push(0xb7 + len_bytes.len() as u8);
427 out.extend_from_slice(&len_bytes);
428 out.extend_from_slice(data);
429 out
430}
431
432fn rlp_decode_bytes(data: &[u8]) -> Result<(Vec<u8>, usize), &'static str> {
434 if data.is_empty() {
435 return Err("empty input");
436 }
437 let prefix = data[0];
438 if prefix < 0x80 {
439 Ok((vec![prefix], 1))
441 } else if prefix <= 0xb7 {
442 let len = (prefix - 0x80) as usize;
444 if data.len() < 1 + len {
445 return Err("truncated short string");
446 }
447 Ok((data[1..1 + len].to_vec(), 1 + len))
448 } else if prefix <= 0xbf {
449 let len_of_len = (prefix - 0xb7) as usize;
451 if data.len() < 1 + len_of_len {
452 return Err("truncated long string length");
453 }
454 let mut len_bytes = [0u8; 8];
455 len_bytes[8 - len_of_len..].copy_from_slice(&data[1..1 + len_of_len]);
456 let len = u64::from_be_bytes(len_bytes) as usize;
457 let total = 1 + len_of_len + len;
458 if data.len() < total {
459 return Err("truncated long string data");
460 }
461 Ok((data[1 + len_of_len..total].to_vec(), total))
462 } else {
463 Err("unexpected list prefix")
464 }
465}
466
467fn rlp_decode_u64(data: &[u8]) -> Result<(u64, usize), &'static str> {
469 if data.is_empty() {
470 return Err("empty input");
471 }
472 let prefix = data[0];
473 if prefix == 0x80 {
474 Ok((0, 1))
476 } else if prefix < 0x80 {
477 Ok((prefix as u64, 1))
479 } else if prefix <= 0x88 {
480 let len = (prefix - 0x80) as usize;
482 if len > 8 || data.len() < 1 + len {
483 return Err("invalid u64 encoding");
484 }
485 let mut bytes = [0u8; 8];
486 bytes[8 - len..].copy_from_slice(&data[1..1 + len]);
487 Ok((u64::from_be_bytes(bytes), 1 + len))
488 } else {
489 Err("value too large for u64")
490 }
491}