edb_engine/eval/handlers/
debug.rs

1// EDB - Ethereum Debugger
2// Copyright (C) 2024 Zhuo Zhang and Wuqi Zhang
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17//! Debug handler implementations for testing and simulation.
18//!
19//! This module provides handler implementations designed for testing, debugging,
20//! and simulation scenarios. It includes two main types of handlers:
21//!
22//! # Handler Types
23//!
24//! ## [`DebugHandler`]
25//! A simple error-only handler that returns detailed error messages for all operations.
26//! Useful for testing error conditions and understanding the evaluation flow.
27//!
28//! ## [`SimulationDebugHandler`]
29//! An advanced simulation handler that:
30//! - Returns mock values for variables and function calls
31//! - Logs all operations for debugging
32//! - Allows pre-configuration of specific return values
33//! - Generates plausible default values based on naming conventions
34//!
35//! # Usage
36//!
37//! ## Error-Only Debug Handler
38//! ```rust,ignore
39//! let handlers = create_debug_handlers();
40//! let evaluator = ExpressionEvaluator::new(handlers);
41//! // All operations will return detailed error messages
42//! ```
43//!
44//! ## Simulation Handler
45//! ```rust,ignore
46//! let (handlers, debug_handler) = create_simulation_debug_handlers();
47//! debug_handler.set_variable("balance", DynSolValue::Uint(U256::from(1000), 256));
48//! debug_handler.set_function("totalSupply", DynSolValue::Uint(U256::from(1000000), 256));
49//!
50//! let evaluator = ExpressionEvaluator::new(handlers);
51//! let result = evaluator.eval("balance + totalSupply()", 0)?; // Returns mock values
52//!
53//! // Check execution log
54//! let log = debug_handler.get_log();
55//! for entry in log {
56//!     println!("Operation: {}", entry);
57//! }
58//! ```
59
60use std::collections::HashMap;
61use std::sync::{Arc, Mutex};
62
63use alloy_dyn_abi::DynSolValue;
64use alloy_primitives::{Address, U256};
65use eyre::{bail, Result};
66
67use super::*;
68
69/// Debug handler that returns errors but includes input values for debugging
70#[derive(Debug, Clone, Default)]
71pub struct DebugHandler;
72
73impl DebugHandler {
74    /// Create a new debug handler.
75    ///
76    /// Returns a handler that will return detailed error messages for all operations,
77    /// making it useful for testing error conditions and debugging evaluation flow.
78    pub fn new() -> Self {
79        Self
80    }
81}
82
83/// Enhanced debug handler that can simulate values and log evaluation flow
84#[derive(Debug, Clone)]
85pub struct SimulationDebugHandler {
86    /// Map of variable names to simulated values
87    variables: Arc<Mutex<HashMap<String, DynSolValue>>>,
88    /// Map of function names to simulated return values
89    functions: Arc<Mutex<HashMap<String, DynSolValue>>>,
90    /// Execution log for debugging
91    log: Arc<Mutex<Vec<String>>>,
92    /// Whether to log all operations
93    verbose: bool,
94}
95
96impl Default for SimulationDebugHandler {
97    fn default() -> Self {
98        Self::new()
99    }
100}
101
102impl SimulationDebugHandler {
103    /// Create a new simulation debug handler
104    pub fn new() -> Self {
105        Self {
106            variables: Arc::new(Mutex::new(HashMap::new())),
107            functions: Arc::new(Mutex::new(HashMap::new())),
108            log: Arc::new(Mutex::new(Vec::new())),
109            verbose: true,
110        }
111    }
112
113    /// Add a simulated variable value
114    pub fn set_variable(&self, name: &str, value: DynSolValue) {
115        if let Ok(mut vars) = self.variables.lock() {
116            vars.insert(name.to_string(), value);
117        }
118    }
119
120    /// Add a simulated function return value
121    pub fn set_function(&self, name: &str, return_value: DynSolValue) {
122        if let Ok(mut funcs) = self.functions.lock() {
123            funcs.insert(name.to_string(), return_value);
124        }
125    }
126
127    /// Get the execution log
128    pub fn get_log(&self) -> Vec<String> {
129        if let Ok(log) = self.log.lock() {
130            log.clone()
131        } else {
132            vec![]
133        }
134    }
135
136    /// Clear the execution log
137    pub fn clear_log(&self) {
138        if let Ok(mut log) = self.log.lock() {
139            log.clear();
140        }
141    }
142
143    /// Log an operation
144    fn log_operation(&self, message: String) {
145        if self.verbose {
146            if let Ok(mut log) = self.log.lock() {
147                log.push(message);
148            }
149        }
150    }
151
152    /// Generate a plausible default value for a type hint
153    fn generate_default_value(&self, hint: &str) -> DynSolValue {
154        match hint {
155            name if name.contains("balance")
156                || name.contains("amount")
157                || name.contains("value") =>
158            {
159                DynSolValue::Uint(U256::from(1000000), 256) // 1M as default balance
160            }
161            name if name.contains("address")
162                || name.contains("owner")
163                || name.contains("sender") =>
164            {
165                DynSolValue::Address(Address::from([0x42; 20])) // Mock address
166            }
167            name if name.contains("count") || name.contains("length") || name.contains("index") => {
168                DynSolValue::Uint(U256::from(5), 256) // Default count/length
169            }
170            name if name.contains("enabled")
171                || name.contains("active")
172                || name.contains("flag") =>
173            {
174                DynSolValue::Bool(true) // Default boolean
175            }
176            name if name.contains("name") || name.contains("symbol") || name.contains("uri") => {
177                DynSolValue::String(format!("Mock_{name}")) // Mock string
178            }
179            _ => DynSolValue::Uint(U256::from(42), 256), // Default fallback
180        }
181    }
182}
183
184impl VariableHandler for DebugHandler {
185    fn get_variable_value(&self, name: &str, snapshot_id: usize) -> Result<DynSolValue> {
186        bail!(
187            "DebugHandler::get_variable_value called with name='{}', snapshot_id={}",
188            name,
189            snapshot_id
190        )
191    }
192}
193
194impl MappingArrayHandler for DebugHandler {
195    fn get_mapping_or_array_value(
196        &self,
197        root: DynSolValue,
198        indices: Vec<DynSolValue>,
199        snapshot_id: usize,
200    ) -> Result<DynSolValue> {
201        let indices_str = indices.iter().map(|v| format!("{v:?}")).collect::<Vec<_>>().join(", ");
202        bail!(
203            "DebugHandler::get_mapping_or_array_value called with root={:?}, indices=[{}], snapshot_id={}",
204            root,
205            indices_str,
206            snapshot_id
207        )
208    }
209}
210
211impl FunctionCallHandler for DebugHandler {
212    fn call_function(
213        &self,
214        name: &str,
215        args: &[DynSolValue],
216        callee: Option<&DynSolValue>,
217        snapshot_id: usize,
218    ) -> Result<DynSolValue> {
219        let args_str = args.iter().map(|v| format!("{v:?}")).collect::<Vec<_>>().join(", ");
220        let callee_str = callee.map(|c| format!("{c:?}")).unwrap_or_else(|| "None".to_string());
221        bail!(
222            "DebugHandler::call_function called with name='{}', args=[{}], callee={}, snapshot_id={}",
223            name,
224            args_str,
225            callee_str,
226            snapshot_id
227        )
228    }
229}
230
231impl MemberAccessHandler for DebugHandler {
232    fn access_member(
233        &self,
234        value: DynSolValue,
235        member: &str,
236        snapshot_id: usize,
237    ) -> Result<DynSolValue> {
238        bail!(
239            "DebugHandler::access_member called with value={:?}, member='{}', snapshot_id={}",
240            value,
241            member,
242            snapshot_id
243        )
244    }
245}
246
247impl MsgHandler for DebugHandler {
248    fn get_msg_sender(&self, snapshot_id: usize) -> Result<DynSolValue> {
249        bail!("DebugHandler::get_msg_sender called with snapshot_id={}", snapshot_id)
250    }
251
252    fn get_msg_value(&self, snapshot_id: usize) -> Result<DynSolValue> {
253        bail!("DebugHandler::get_msg_value called with snapshot_id={}", snapshot_id)
254    }
255}
256
257impl TxHandler for DebugHandler {
258    fn get_tx_origin(&self, snapshot_id: usize) -> Result<DynSolValue> {
259        bail!("DebugHandler::get_tx_origin called with snapshot_id={}", snapshot_id)
260    }
261}
262
263impl BlockHandler for DebugHandler {
264    fn get_block_number(&self, snapshot_id: usize) -> Result<DynSolValue> {
265        bail!("DebugHandler::get_block_number called with snapshot_id={}", snapshot_id)
266    }
267
268    fn get_block_timestamp(&self, snapshot_id: usize) -> Result<DynSolValue> {
269        bail!("DebugHandler::get_block_timestamp called with snapshot_id={}", snapshot_id)
270    }
271}
272
273impl ValidationHandler for DebugHandler {
274    fn validate_value(&self, value: DynSolValue) -> Result<DynSolValue> {
275        Ok(value)
276    }
277}
278
279// Implement handler traits for SimulationDebugHandler
280impl VariableHandler for SimulationDebugHandler {
281    fn get_variable_value(&self, name: &str, snapshot_id: usize) -> Result<DynSolValue> {
282        self.log_operation(format!("get_variable_value: name='{name}', snapshot_id={snapshot_id}"));
283
284        if let Ok(vars) = self.variables.lock() {
285            if let Some(value) = vars.get(name) {
286                self.log_operation(format!("  -> returning stored value: {value:?}"));
287                return Ok(value.clone());
288            }
289        }
290
291        // Generate a plausible default based on variable name
292        let default_value = self.generate_default_value(name);
293        self.log_operation(format!("  -> generating default value: {default_value:?}"));
294        Ok(default_value)
295    }
296}
297
298impl MappingArrayHandler for SimulationDebugHandler {
299    fn get_mapping_or_array_value(
300        &self,
301        root: DynSolValue,
302        indices: Vec<DynSolValue>,
303        snapshot_id: usize,
304    ) -> Result<DynSolValue> {
305        let indices_str = indices.iter().map(|v| format!("{v:?}")).collect::<Vec<_>>().join(", ");
306        self.log_operation(format!(
307            "get_mapping_or_array_value: root={root:?}, indices=[{indices_str}], snapshot_id={snapshot_id}"
308        ));
309
310        // For arrays, return a mock element
311        // For mappings, return a value based on the key
312        let result = match indices.first() {
313            Some(DynSolValue::Uint(index, _)) => {
314                // Array access - return mock data based on index
315                DynSolValue::Uint(U256::from(1000 + index.to::<u64>()), 256)
316            }
317            Some(DynSolValue::Address(_)) => {
318                // Address mapping - return mock balance
319                DynSolValue::Uint(U256::from(1000000), 256)
320            }
321            Some(DynSolValue::String(key)) => {
322                // String mapping - return based on key
323                self.generate_default_value(key)
324            }
325            _ => DynSolValue::Uint(U256::from(42), 256),
326        };
327
328        self.log_operation(format!("  -> returning: {result:?}"));
329        Ok(result)
330    }
331}
332
333impl FunctionCallHandler for SimulationDebugHandler {
334    fn call_function(
335        &self,
336        name: &str,
337        args: &[DynSolValue],
338        callee: Option<&DynSolValue>,
339        snapshot_id: usize,
340    ) -> Result<DynSolValue> {
341        let args_str = args.iter().map(|v| format!("{v:?}")).collect::<Vec<_>>().join(", ");
342        let callee_str = callee.map(|c| format!("{c:?}")).unwrap_or_else(|| "None".to_string());
343        self.log_operation(format!(
344            "call_function: name='{name}', args=[{args_str}], callee={callee_str}, snapshot_id={snapshot_id}"
345        ));
346
347        if let Ok(funcs) = self.functions.lock() {
348            if let Some(value) = funcs.get(name) {
349                self.log_operation(format!("  -> returning stored value: {value:?}"));
350                return Ok(value.clone());
351            }
352        }
353
354        // Generate result based on function name
355        let result = match name {
356            "balanceOf" => DynSolValue::Uint(U256::from(1000000), 256),
357            "totalSupply" => DynSolValue::Uint(U256::from(1000000000), 256),
358            "approve" | "transfer" => DynSolValue::Bool(true),
359            "name" => DynSolValue::String("MockToken".to_string()),
360            "symbol" => DynSolValue::String("MTK".to_string()),
361            "decimals" => DynSolValue::Uint(U256::from(18), 256),
362            _ => self.generate_default_value(name),
363        };
364
365        self.log_operation(format!("  -> returning generated value: {result:?}"));
366        Ok(result)
367    }
368}
369
370impl MemberAccessHandler for SimulationDebugHandler {
371    fn access_member(
372        &self,
373        value: DynSolValue,
374        member: &str,
375        snapshot_id: usize,
376    ) -> Result<DynSolValue> {
377        self.log_operation(format!(
378            "access_member: value={value:?}, member='{member}', snapshot_id={snapshot_id}"
379        ));
380
381        // Handle common member accesses
382        let result = match member {
383            "length" => DynSolValue::Uint(U256::from(10), 256), // Mock array length
384            "balance" => DynSolValue::Uint(U256::from(1000000), 256), // Mock balance
385            "code" => DynSolValue::Bytes(vec![0x60, 0x80, 0x60, 0x40]), // Mock bytecode
386            _ => self.generate_default_value(member),
387        };
388
389        self.log_operation(format!("  -> returning: {result:?}"));
390        Ok(result)
391    }
392}
393
394impl MsgHandler for SimulationDebugHandler {
395    fn get_msg_sender(&self, snapshot_id: usize) -> Result<DynSolValue> {
396        self.log_operation(format!("get_msg_sender: snapshot_id={snapshot_id}"));
397        let result = DynSolValue::Address(Address::from([0x42; 20])); // Mock sender
398        self.log_operation(format!("  -> returning: {result:?}"));
399        Ok(result)
400    }
401
402    fn get_msg_value(&self, snapshot_id: usize) -> Result<DynSolValue> {
403        self.log_operation(format!("get_msg_value: snapshot_id={snapshot_id}"));
404        let result = DynSolValue::Uint(U256::from(1000000000000000000u64), 256); // 1 ETH
405        self.log_operation(format!("  -> returning: {result:?}"));
406        Ok(result)
407    }
408}
409
410impl TxHandler for SimulationDebugHandler {
411    fn get_tx_origin(&self, snapshot_id: usize) -> Result<DynSolValue> {
412        self.log_operation(format!("get_tx_origin: snapshot_id={snapshot_id}"));
413        let result = DynSolValue::Address(Address::from([0x11; 20])); // Mock origin
414        self.log_operation(format!("  -> returning: {result:?}"));
415        Ok(result)
416    }
417}
418
419impl BlockHandler for SimulationDebugHandler {
420    fn get_block_number(&self, snapshot_id: usize) -> Result<DynSolValue> {
421        self.log_operation(format!("get_block_number: snapshot_id={snapshot_id}"));
422        let result = DynSolValue::Uint(U256::from(18500000), 256); // Mock block number
423        self.log_operation(format!("  -> returning: {result:?}"));
424        Ok(result)
425    }
426
427    fn get_block_timestamp(&self, snapshot_id: usize) -> Result<DynSolValue> {
428        self.log_operation(format!("get_block_timestamp: snapshot_id={snapshot_id}"));
429        let result = DynSolValue::Uint(U256::from(1700000000), 256); // Mock timestamp
430        self.log_operation(format!("  -> returning: {result:?}"));
431        Ok(result)
432    }
433}
434
435// Implement traits for Arc<SimulationDebugHandler> to allow sharing
436impl VariableHandler for Arc<SimulationDebugHandler> {
437    fn get_variable_value(&self, name: &str, snapshot_id: usize) -> Result<DynSolValue> {
438        self.as_ref().get_variable_value(name, snapshot_id)
439    }
440}
441
442impl MappingArrayHandler for Arc<SimulationDebugHandler> {
443    fn get_mapping_or_array_value(
444        &self,
445        root: DynSolValue,
446        indices: Vec<DynSolValue>,
447        snapshot_id: usize,
448    ) -> Result<DynSolValue> {
449        self.as_ref().get_mapping_or_array_value(root, indices, snapshot_id)
450    }
451}
452
453impl FunctionCallHandler for Arc<SimulationDebugHandler> {
454    fn call_function(
455        &self,
456        name: &str,
457        args: &[DynSolValue],
458        callee: Option<&DynSolValue>,
459        snapshot_id: usize,
460    ) -> Result<DynSolValue> {
461        self.as_ref().call_function(name, args, callee, snapshot_id)
462    }
463}
464
465impl MemberAccessHandler for Arc<SimulationDebugHandler> {
466    fn access_member(
467        &self,
468        value: DynSolValue,
469        member: &str,
470        snapshot_id: usize,
471    ) -> Result<DynSolValue> {
472        self.as_ref().access_member(value, member, snapshot_id)
473    }
474}
475
476impl MsgHandler for Arc<SimulationDebugHandler> {
477    fn get_msg_sender(&self, snapshot_id: usize) -> Result<DynSolValue> {
478        self.as_ref().get_msg_sender(snapshot_id)
479    }
480
481    fn get_msg_value(&self, snapshot_id: usize) -> Result<DynSolValue> {
482        self.as_ref().get_msg_value(snapshot_id)
483    }
484}
485
486impl TxHandler for Arc<SimulationDebugHandler> {
487    fn get_tx_origin(&self, snapshot_id: usize) -> Result<DynSolValue> {
488        self.as_ref().get_tx_origin(snapshot_id)
489    }
490}
491
492impl BlockHandler for Arc<SimulationDebugHandler> {
493    fn get_block_number(&self, snapshot_id: usize) -> Result<DynSolValue> {
494        self.as_ref().get_block_number(snapshot_id)
495    }
496
497    fn get_block_timestamp(&self, snapshot_id: usize) -> Result<DynSolValue> {
498        self.as_ref().get_block_timestamp(snapshot_id)
499    }
500}
501
502impl ValidationHandler for Arc<SimulationDebugHandler> {
503    fn validate_value(&self, value: DynSolValue) -> Result<DynSolValue> {
504        Ok(value)
505    }
506}
507
508/// Create debug handlers for all traits (original error-only version)
509pub fn create_debug_handlers() -> EvaluatorHandlers {
510    EvaluatorHandlers::new()
511        .with_variable_handler(Box::new(DebugHandler::new()))
512        .with_mapping_array_handler(Box::new(DebugHandler::new()))
513        .with_function_call_handler(Box::new(DebugHandler::new()))
514        .with_member_access_handler(Box::new(DebugHandler::new()))
515        .with_msg_handler(Box::new(DebugHandler::new()))
516        .with_tx_handler(Box::new(DebugHandler::new()))
517        .with_block_handler(Box::new(DebugHandler::new()))
518        .with_validation_handler(Box::new(DebugHandler::new()))
519}
520
521/// Create simulation debug handlers that return mock values and log operations
522pub fn create_simulation_debug_handlers() -> (EvaluatorHandlers, Arc<SimulationDebugHandler>) {
523    let handler = Arc::new(SimulationDebugHandler::new());
524    let handlers = EvaluatorHandlers::new()
525        .with_variable_handler(Box::new(handler.clone()))
526        .with_mapping_array_handler(Box::new(handler.clone()))
527        .with_function_call_handler(Box::new(handler.clone()))
528        .with_member_access_handler(Box::new(handler.clone()))
529        .with_msg_handler(Box::new(handler.clone()))
530        .with_tx_handler(Box::new(handler.clone()))
531        .with_block_handler(Box::new(handler.clone()))
532        .with_validation_handler(Box::new(handler.clone()));
533
534    (handlers, handler)
535}