1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{Address, B256, U256};
3use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
4
5use crate::storage_slot::{
6 derive_subspace_key, map_slot, map_slot_b256, ARBOS_STATE_ADDRESS, CACHE_MANAGERS_KEY,
7 PROGRAMS_DATA_KEY, PROGRAMS_SUBSPACE, ROOT_STORAGE_KEY,
8};
9
10pub const ARBWASMCACHE_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, 0x72,
14]);
15
16const IS_CACHE_MANAGER: [u8; 4] = [0xf1, 0x37, 0xfc, 0xda];
18const ALL_CACHE_MANAGERS: [u8; 4] = [0x35, 0x17, 0x3c, 0x26];
19const CACHE_CODEHASH: [u8; 4] = [0x0e, 0xa0, 0x7a, 0x7a];
20const CACHE_PROGRAM: [u8; 4] = [0xb6, 0xf4, 0xfb, 0x22];
21const EVICT_CODEHASH: [u8; 4] = [0xd4, 0x56, 0xcd, 0x34];
22const CODEHASH_IS_CACHED: [u8; 4] = [0x47, 0x97, 0x00, 0xf6];
23
24const SLOAD_GAS: u64 = 800;
25const COPY_GAS: u64 = 3;
26
27const BY_ADDRESS_KEY: &[u8] = &[0];
29
30pub fn create_arbwasmcache_precompile() -> DynPrecompile {
31 DynPrecompile::new_stateful(PrecompileId::custom("arbwasmcache"), handler)
32}
33
34fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
35 if let Some(result) =
37 crate::check_precompile_version(arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS)
38 {
39 return result;
40 }
41
42 let data = input.data;
43 if data.len() < 4 {
44 return Err(PrecompileError::other("input too short"));
45 }
46
47 let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
48
49 let result = match selector {
50 CACHE_CODEHASH => {
52 if let Some(result) = crate::check_method_version(
53 arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS,
54 arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS,
55 ) {
56 return result;
57 }
58 let _ = &mut input;
59 Err(PrecompileError::other("caller is not a cache manager"))
60 }
61 CACHE_PROGRAM => {
63 if let Some(result) = crate::check_method_version(
64 arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS_FIXES,
65 0,
66 ) {
67 return result;
68 }
69 let _ = &mut input;
70 Err(PrecompileError::other("caller is not a cache manager"))
71 }
72 IS_CACHE_MANAGER => handle_is_cache_manager(&mut input),
73 ALL_CACHE_MANAGERS => handle_all_cache_managers(&mut input),
74 CODEHASH_IS_CACHED => handle_codehash_is_cached(&mut input),
75 EVICT_CODEHASH => {
76 let _ = &mut input;
77 Err(PrecompileError::other("caller is not a cache manager"))
78 }
79 _ => Err(PrecompileError::other("unknown ArbWasmCache selector")),
80 };
81 crate::gas_check(input.gas, result)
82}
83
84fn load_arbos(input: &mut PrecompileInput<'_>) -> Result<(), PrecompileError> {
87 input
88 .internals_mut()
89 .load_account(ARBOS_STATE_ADDRESS)
90 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
91 Ok(())
92}
93
94fn sload_field(input: &mut PrecompileInput<'_>, slot: U256) -> Result<U256, PrecompileError> {
95 let val = input
96 .internals_mut()
97 .sload(ARBOS_STATE_ADDRESS, slot)
98 .map_err(|_| PrecompileError::other("sload failed"))?;
99 Ok(val.data)
100}
101
102fn cache_managers_key() -> B256 {
104 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
105 derive_subspace_key(programs_key.as_slice(), CACHE_MANAGERS_KEY)
106}
107
108fn handle_is_cache_manager(input: &mut PrecompileInput<'_>) -> PrecompileResult {
110 let data = input.data;
111 if data.len() < 36 {
112 return Err(PrecompileError::other("calldata too short for address arg"));
113 }
114 let mut addr_bytes = [0u8; 20];
116 addr_bytes.copy_from_slice(&data[16..36]);
117 let addr = Address::from(addr_bytes);
118
119 load_arbos(input)?;
120
121 let cm_key = cache_managers_key();
122 let by_addr_key = derive_subspace_key(cm_key.as_slice(), BY_ADDRESS_KEY);
123 let addr_hash = address_to_b256(addr);
124 let slot = map_slot_b256(by_addr_key.as_slice(), &addr_hash);
125 let value = sload_field(input, slot)?;
126
127 let is_member = value != U256::ZERO;
128 let result = if is_member {
129 U256::from(1u64)
130 } else {
131 U256::ZERO
132 };
133 Ok(PrecompileOutput::new(
134 SLOAD_GAS + COPY_GAS,
135 result.to_be_bytes::<32>().to_vec().into(),
136 ))
137}
138
139fn handle_all_cache_managers(input: &mut PrecompileInput<'_>) -> PrecompileResult {
141 load_arbos(input)?;
142
143 let cm_key = cache_managers_key();
144 let size_slot = map_slot(cm_key.as_slice(), 0);
145 let size = sload_field(input, size_slot)?.saturating_to::<u64>();
146 let mut sloads: u64 = 1;
147
148 let count = size.min(256);
150
151 let mut out = Vec::with_capacity(64 + count as usize * 32);
153 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
154 out.extend_from_slice(&U256::from(count).to_be_bytes::<32>());
155
156 for i in 1..=count {
157 let addr_slot = map_slot(cm_key.as_slice(), i);
158 let addr_value = sload_field(input, addr_slot)?;
159 out.extend_from_slice(&addr_value.to_be_bytes::<32>());
160 sloads += 1;
161 }
162
163 Ok(PrecompileOutput::new(
164 (sloads * SLOAD_GAS + COPY_GAS).min(input.gas),
165 out.into(),
166 ))
167}
168
169fn handle_codehash_is_cached(input: &mut PrecompileInput<'_>) -> PrecompileResult {
171 let data = input.data;
172 if data.len() < 36 {
173 return Err(PrecompileError::other("calldata too short for bytes32 arg"));
174 }
175 let mut bytes = [0u8; 32];
176 bytes.copy_from_slice(&data[4..36]);
177 let codehash = B256::from(bytes);
178
179 load_arbos(input)?;
180
181 let programs_key = derive_subspace_key(ROOT_STORAGE_KEY, PROGRAMS_SUBSPACE);
182 let data_key = derive_subspace_key(programs_key.as_slice(), PROGRAMS_DATA_KEY);
183 let program_slot = map_slot_b256(data_key.as_slice(), &codehash);
184 let program_word = sload_field(input, program_slot)?;
185
186 let word_bytes = program_word.to_be_bytes::<32>();
188 let is_cached = word_bytes[14] != 0;
189
190 let result = if is_cached {
191 U256::from(1u64)
192 } else {
193 U256::ZERO
194 };
195 Ok(PrecompileOutput::new(
196 SLOAD_GAS + COPY_GAS,
197 result.to_be_bytes::<32>().to_vec().into(),
198 ))
199}
200
201fn address_to_b256(addr: Address) -> B256 {
202 let mut bytes = [0u8; 32];
203 bytes[12..32].copy_from_slice(addr.as_slice());
204 B256::from(bytes)
205}