geth_tracer/
fourbyte.rs

1//! Fourbyte tracing inspector
2//!
3//! Solidity contract functions are addressed using the first four byte of the
4//! Keccak-256 hash of their signature. Therefore when calling the function of a
5//! contract, the caller must send this function selector as well as the
6//! ABI-encoded arguments as call data.
7//!
8//! The 4byteTracer collects the function selectors of every function executed
9//! in the lifetime of a transaction, along with the size of the supplied call
10//! data. The result is a map of SELECTOR-CALLDATASIZE to number of occurrences
11//! entries, where the keys are SELECTOR-CALLDATASIZE and the values are number
12//! of occurrences of this key. For example:
13//!
14//! ```json
15//! {
16//!   "0x27dc297e-128": 1,
17//!   "0x38cc4831-0": 2,
18//!   "0x524f3889-96": 1,
19//!   "0xadf59f99-288": 1,
20//!   "0xc281d19e-0": 1
21//! }
22//! ```
23
24use alloy_primitives::{hex, Selector};
25use alloy_rpc_types_trace::geth::{FourByteFrame, GethTrace};
26use cfx_vm_types::ActionParams;
27use std::collections::HashMap;
28
29/// Fourbyte tracing inspector that records all function selectors and their
30/// calldata sizes.
31#[derive(Clone, Debug, Default)]
32pub struct FourByteInspector {
33    /// The map of SELECTOR to number of occurrences entries
34    inner: HashMap<(Selector, usize), u64>,
35}
36
37impl FourByteInspector {
38    pub fn new() -> Self { Self::default() }
39
40    /// Returns the map of SELECTOR to number of occurrences entries
41    pub const fn inner(&self) -> &HashMap<(Selector, usize), u64> {
42        &self.inner
43    }
44
45    pub fn drain(self) -> GethTrace {
46        GethTrace::FourByteTracer(FourByteFrame::from(self))
47    }
48
49    pub fn record_call(&mut self, params: &ActionParams) {
50        if let Some(input) = &params.data {
51            if input.len() > 4 {
52                let selector = Selector::try_from(&input[..4])
53                    .expect("input is at least 4 bytes");
54                let calldata_size = input[4..].len();
55                *self.inner.entry((selector, calldata_size)).or_default() += 1;
56            }
57        }
58    }
59}
60
61impl From<FourByteInspector> for FourByteFrame {
62    fn from(value: FourByteInspector) -> Self {
63        Self(
64            value
65                .inner
66                .into_iter()
67                .map(|((selector, calldata_size), count)| {
68                    let key = format!(
69                        "0x{}-{}",
70                        hex::encode(&selector[..]),
71                        calldata_size
72                    );
73                    (key, count)
74                })
75                .collect(),
76        )
77    }
78}