arb_precompiles/
arbaddresstable.rs

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
13/// ArbAddressTable precompile address (0x66).
14pub 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    // `body_sloads` counts the framework `OpenArbosState` SLOAD plus the body's
314    // own reads; init already charged the framework SLOAD, so subtract one.
315    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}