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) = ¶ms.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}