cfx_execute_helper/observer/
access_list.rs

1use cfx_executor::observer::{
2    CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer,
3    OpcodeTracer, SetAuthTracer, StorageTracer,
4};
5use cfx_types::{u256_to_address_be, u256_to_h256_be, Address, H256};
6use cfx_vm_interpreter::instructions::Instruction;
7use cfx_vm_types::InterpreterInfo;
8use primitives::{AccessList, AccessListItem};
9use std::collections::{BTreeSet, HashMap, HashSet};
10use typemap::ShareDebugMap;
11
12/// An [Inspector] that collects touched accounts and storage slots.
13///
14/// This can be used to construct an [AccessList] for a transaction via
15/// `eth_createAccessList`
16#[derive(Debug, Default)]
17pub struct AccessListInspector {
18    /// All addresses that should be excluded from the final accesslist
19    excluded: HashSet<Address>,
20    /// All addresses and touched slots
21    touched_slots: HashMap<Address, BTreeSet<H256>>,
22}
23
24impl From<(AccessList, HashSet<Address>)> for AccessListInspector {
25    fn from(data: (AccessList, HashSet<Address>)) -> Self {
26        Self::new(data.0, data.1)
27    }
28}
29
30impl AccessListInspector {
31    /// Creates a new [AccessListInspector] with the given excluded addresses.
32    pub fn new(access_list: AccessList, excluded: HashSet<Address>) -> Self {
33        Self {
34            excluded,
35            touched_slots: access_list
36                .into_iter()
37                .map(|v| (v.address, v.storage_keys.into_iter().collect()))
38                .collect(),
39        }
40    }
41
42    /// Returns the excluded addresses.
43    pub fn excluded(&self) -> &HashSet<Address> { &self.excluded }
44
45    /// Returns a reference to the map of addresses and their corresponding
46    /// touched storage slots.
47    pub fn touched_slots(&self) -> &HashMap<Address, BTreeSet<H256>> {
48        &self.touched_slots
49    }
50
51    /// Consumes the inspector and returns the map of addresses and their
52    /// corresponding touched storage slots.
53    pub fn into_touched_slots(self) -> HashMap<Address, BTreeSet<H256>> {
54        self.touched_slots
55    }
56
57    /// Returns list of addresses and storage keys used by the transaction. It
58    /// gives you the list of addresses and storage keys that were touched
59    /// during execution.
60    pub fn into_access_list(self) -> AccessList {
61        let items = self.touched_slots.into_iter().map(|(address, slots)| {
62            AccessListItem {
63                address,
64                storage_keys: slots.into_iter().collect(),
65            }
66        });
67        items.collect()
68    }
69
70    /// Returns list of addresses and storage keys used by the transaction. It
71    /// gives you the list of addresses and storage keys that were touched
72    /// during execution.
73    pub fn access_list(&self) -> AccessList {
74        let items =
75            self.touched_slots
76                .iter()
77                .map(|(address, slots)| AccessListItem {
78                    address: *address,
79                    storage_keys: slots.iter().copied().collect(),
80                });
81        items.collect()
82    }
83
84    pub fn collcect_excluded_addresses(&mut self, item: Address) {
85        self.excluded.insert(item);
86    }
87}
88
89impl DrainTrace for AccessListInspector {
90    fn drain_trace(self, map: &mut ShareDebugMap) {
91        map.insert::<AccessListKey>(self.into_access_list());
92    }
93}
94
95pub struct AccessListKey;
96
97impl typemap::Key for AccessListKey {
98    type Value = AccessList;
99}
100
101impl OpcodeTracer for AccessListInspector {
102    fn step(&mut self, interp: &dyn InterpreterInfo) {
103        let ins = Instruction::from_u8(interp.current_opcode())
104            .expect("valid opcode");
105        match ins {
106            Instruction::SLOAD | Instruction::SSTORE => {
107                if let Some(slot) = interp.stack().last() {
108                    let cur_contract = interp.contract_address();
109                    self.touched_slots
110                        .entry(cur_contract)
111                        .or_default()
112                        .insert(u256_to_h256_be(*slot));
113                }
114            }
115            Instruction::EXTCODECOPY
116            | Instruction::EXTCODEHASH
117            | Instruction::EXTCODESIZE
118            | Instruction::BALANCE
119            | Instruction::SUICIDE => {
120                if let Some(slot) = interp.stack().last() {
121                    let addr = u256_to_address_be(*slot);
122                    if !self.excluded.contains(&addr) {
123                        self.touched_slots.entry(addr).or_default();
124                    }
125                }
126            }
127            Instruction::DELEGATECALL
128            | Instruction::CALL
129            | Instruction::STATICCALL
130            | Instruction::CALLCODE => {
131                if let Some(slot) = interp.stack().last() {
132                    let addr = u256_to_address_be(*slot);
133                    if !self.excluded.contains(&addr) {
134                        self.touched_slots.entry(addr).or_default();
135                    }
136                }
137            }
138            _ => (),
139        }
140    }
141}
142
143impl CallTracer for AccessListInspector {}
144impl CheckpointTracer for AccessListInspector {}
145impl InternalTransferTracer for AccessListInspector {}
146impl StorageTracer for AccessListInspector {}
147impl SetAuthTracer for AccessListInspector {}