cfx_execute_helper/observer/
access_list.rs1use 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#[derive(Debug, Default)]
17pub struct AccessListInspector {
18 excluded: HashSet<Address>,
20 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 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 pub fn excluded(&self) -> &HashSet<Address> { &self.excluded }
44
45 pub fn touched_slots(&self) -> &HashMap<Address, BTreeSet<H256>> {
48 &self.touched_slots
49 }
50
51 pub fn into_touched_slots(self) -> HashMap<Address, BTreeSet<H256>> {
54 self.touched_slots
55 }
56
57 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 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 {}