1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, B256, Bytes, U256};
5use alloy_sol_types::{SolError, SolEvent, SolInterface};
6use arb_context::ArbPrecompileCtx;
7use arb_storage::ARBOS_STATE_ADDRESS;
8use revm::{
9 precompile::{PrecompileId, PrecompileOutput, PrecompileResult},
10 primitives::Log,
11};
12
13use crate::{ArbPrecompileError, interfaces::IArbDebug};
14
15pub const ARBDEBUG_ADDRESS: Address = Address::new([
17 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
18 0x00, 0x00, 0x00, 0xff,
19]);
20
21const SLOAD_GAS: u64 = 800;
22const SSTORE_GAS: u64 = 20_000;
23const COPY_GAS: u64 = 3;
24const LOG_GAS: u64 = 375;
25const LOG_TOPIC_GAS: u64 = 375;
26const LOG_DATA_GAS: u64 = 8;
27
28pub fn create_arbdebug_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
29 DynPrecompile::new_stateful(PrecompileId::custom("arbdebug"), move |input| {
30 handler(input, &ctx)
31 })
32}
33
34fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
35 let mut gas_used = 0u64;
36 let gas_limit = input.gas;
37 if !ctx.block.allow_debug_precompiles {
38 return crate::burn_all_revert(gas_limit);
39 }
40 crate::init_precompile_gas(&mut gas_used, ctx, input.data.len());
41
42 let call = match IArbDebug::ArbDebugCalls::abi_decode(input.data) {
43 Ok(c) => c,
44 Err(_) => return crate::burn_all_revert(gas_limit),
45 };
46
47 use IArbDebug::ArbDebugCalls;
48 let input_len = input.data.len();
49 let result = match call {
50 ArbDebugCalls::becomeChainOwner(_) => {
51 handle_become_chain_owner(&mut input, &mut gas_used, ctx)
52 }
53 ArbDebugCalls::events(c) => handle_events(&mut input, &mut gas_used, ctx, c.flag, c.value),
54 ArbDebugCalls::eventsView(_) => handle_events_view(&mut input, &mut gas_used, ctx),
55 ArbDebugCalls::customRevert(c) => {
56 gas_used = 0;
57 crate::init_precompile_gas_pure(&mut gas_used, ctx, input_len);
58 handle_custom_revert(&mut gas_used, ctx, c.number, gas_limit)
59 }
60 ArbDebugCalls::legacyError(_) => {
61 gas_used = 0;
62 crate::init_precompile_gas_pure(&mut gas_used, ctx, input_len);
63 Err(ArbPrecompileError::empty_revert(gas_used).into())
64 }
65 ArbDebugCalls::panic(_) => {
66 if let Some(r) = crate::check_method_version(
67 ctx,
68 gas_limit,
69 arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS,
70 0,
71 ) {
72 return r;
73 }
74 panic!("called ArbDebug's debug-only Panic method")
75 }
76 ArbDebugCalls::overwriteContractCode(c) => {
77 handle_overwrite_contract_code(&mut input, &mut gas_used, ctx, c.target, c.newCode)
78 }
79 };
80
81 crate::gas_check(ctx, gas_limit, gas_used, result)
82}
83
84fn handle_become_chain_owner(
85 input: &mut PrecompileInput<'_>,
86 gas_used: &mut u64,
87 ctx: &ArbPrecompileCtx,
88) -> PrecompileResult {
89 let caller = input.caller;
90 let gas_limit = input.gas;
91
92 input
93 .internals_mut()
94 .load_account(ARBOS_STATE_ADDRESS)
95 .map_err(ArbPrecompileError::fatal)?;
96
97 let internals = input.internals_mut();
98 let arb_state = ctx
99 .block
100 .arbos_state(internals)
101 .map_err(ArbPrecompileError::fatal)?;
102
103 let was_member = arb_state
104 .chain_owners
105 .is_member(internals, caller)
106 .map_err(ArbPrecompileError::fatal)?;
107 crate::charge_storage_read(gas_used, ctx, SLOAD_GAS);
108
109 if !was_member {
110 arb_state
111 .chain_owners
112 .add(internals, caller)
113 .map_err(ArbPrecompileError::fatal)?;
114 crate::charge_storage_read(gas_used, ctx, 2 * SLOAD_GAS);
115 crate::charge_storage_write(gas_used, ctx, 3 * SSTORE_GAS);
116 }
117
118 Ok(PrecompileOutput::new(
119 (*gas_used).min(gas_limit),
120 Vec::new().into(),
121 ))
122}
123
124fn handle_events(
125 input: &mut PrecompileInput<'_>,
126 gas_used: &mut u64,
127 ctx: &ArbPrecompileCtx,
128 flag: bool,
129 value: B256,
130) -> PrecompileResult {
131 let gas_limit = input.gas;
132 let caller = input.caller;
133 let value_received = input.value;
134
135 input
136 .internals_mut()
137 .load_account(ARBOS_STATE_ADDRESS)
138 .map_err(ArbPrecompileError::fatal)?;
139
140 emit_basic_event(input, !flag, value);
141 emit_mixed_event(input, flag, !flag, value, ARBDEBUG_ADDRESS, caller);
142
143 let mut out = Vec::with_capacity(64);
144 out.extend_from_slice(B256::left_padding_from(caller.as_slice()).as_slice());
145 out.extend_from_slice(&value_received.to_be_bytes::<32>());
146
147 let result_words = (out.len() as u64).div_ceil(32);
148 let basic_log_gas = LOG_GAS + LOG_TOPIC_GAS * 2 + LOG_DATA_GAS * 32;
149 let mixed_log_gas = LOG_GAS + LOG_TOPIC_GAS * 4 + LOG_DATA_GAS * 64;
150 crate::charge_history_growth(gas_used, ctx, basic_log_gas + mixed_log_gas);
151 crate::charge_computation(gas_used, ctx, COPY_GAS * result_words);
152 Ok(PrecompileOutput::new(
153 (*gas_used).min(gas_limit),
154 out.into(),
155 ))
156}
157
158fn handle_events_view(
159 input: &mut PrecompileInput<'_>,
160 gas_used: &mut u64,
161 ctx: &ArbPrecompileCtx,
162) -> PrecompileResult {
163 if ctx.block.arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_11 {
166 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
167 }
168
169 let gas_limit = input.gas;
170 let caller = input.caller;
171
172 input
173 .internals_mut()
174 .load_account(ARBOS_STATE_ADDRESS)
175 .map_err(ArbPrecompileError::fatal)?;
176
177 let value = B256::ZERO;
178 let flag = true;
179 emit_basic_event(input, !flag, value);
180 emit_mixed_event(input, flag, !flag, value, ARBDEBUG_ADDRESS, caller);
181
182 let basic_log_gas = LOG_GAS + LOG_TOPIC_GAS * 2 + LOG_DATA_GAS * 32;
183 let mixed_log_gas = LOG_GAS + LOG_TOPIC_GAS * 4 + LOG_DATA_GAS * 64;
184 crate::charge_history_growth(gas_used, ctx, basic_log_gas + mixed_log_gas);
185
186 Ok(PrecompileOutput::new(
187 (*gas_used).min(gas_limit),
188 Vec::new().into(),
189 ))
190}
191
192fn handle_custom_revert(
193 gas_used: &mut u64,
194 ctx: &ArbPrecompileCtx,
195 number: u64,
196 gas_limit: u64,
197) -> PrecompileResult {
198 let payload = IArbDebug::Custom {
199 _0: number,
200 _1: "This spider family wards off bugs: /\\oo/\\ //\\(oo)//\\ /\\oo/\\".to_string(),
201 _2: true,
202 }
203 .abi_encode();
204 crate::sol_error_revert(gas_used, ctx, payload, gas_limit)
205}
206
207fn emit_basic_event(input: &mut PrecompileInput<'_>, flag: bool, value: B256) {
208 let topic0 = IArbDebug::Basic::SIGNATURE_HASH;
209 let topic1 = value;
210 let mut data = [0u8; 32];
211 if flag {
212 data[31] = 1;
213 }
214 input.internals_mut().log(Log::new_unchecked(
215 ARBDEBUG_ADDRESS,
216 vec![topic0, topic1],
217 Bytes::copy_from_slice(&data),
218 ));
219}
220
221fn handle_overwrite_contract_code(
225 input: &mut PrecompileInput<'_>,
226 gas_used: &mut u64,
227 ctx: &ArbPrecompileCtx,
228 target: Address,
229 new_code: Bytes,
230) -> PrecompileResult {
231 let gas_limit = input.gas;
232
233 let old_code: Vec<u8> = match input.internals_mut().load_account_code(target) {
234 Ok(state_load) => state_load
235 .data
236 .code()
237 .map(|bc| bc.original_byte_slice().to_vec())
238 .unwrap_or_default(),
239 Err(e) => return Err(ArbPrecompileError::fatal(e).into()),
240 };
241
242 let bytecode = revm::bytecode::Bytecode::new_raw(new_code.clone());
243 if let Err(e) = input.internals_mut().set_code(target, bytecode) {
244 return Err(ArbPrecompileError::fatal(e).into());
245 }
246
247 let len = old_code.len();
249 let padded_len = len.div_ceil(32) * 32;
250 let mut out = Vec::with_capacity(64 + padded_len);
251 out.extend_from_slice(&U256::from(32u64).to_be_bytes::<32>());
252 out.extend_from_slice(&U256::from(len as u64).to_be_bytes::<32>());
253 out.extend_from_slice(&old_code);
254 out.resize(64 + padded_len, 0);
255
256 let result_words = (out.len() as u64).div_ceil(32);
257 crate::charge_computation(gas_used, ctx, COPY_GAS.saturating_mul(result_words));
258 Ok(PrecompileOutput::new(
259 (*gas_used).min(gas_limit),
260 out.into(),
261 ))
262}
263
264fn emit_mixed_event(
265 input: &mut PrecompileInput<'_>,
266 flag1: bool,
267 flag2: bool,
268 value: B256,
269 addr1: Address,
270 addr2: Address,
271) {
272 let topic0 = IArbDebug::Mixed::SIGNATURE_HASH;
273 let mut t1 = [0u8; 32];
274 if flag1 {
275 t1[31] = 1;
276 }
277 let topic1 = B256::from(t1);
278 let topic2 = value;
279 let topic3 = B256::left_padding_from(addr2.as_slice());
280 let mut data = Vec::with_capacity(64);
281 let mut flag2_word = [0u8; 32];
282 if flag2 {
283 flag2_word[31] = 1;
284 }
285 data.extend_from_slice(&flag2_word);
286 data.extend_from_slice(B256::left_padding_from(addr1.as_slice()).as_slice());
287 input.internals_mut().log(Log::new_unchecked(
288 ARBDEBUG_ADDRESS,
289 vec![topic0, topic1, topic2, topic3],
290 Bytes::copy_from_slice(&data),
291 ));
292}