cfx_rpc_common_impl/trace/
matcher.rs1use cfx_parity_trace_types::{Action, ExecTrace};
2
3pub fn construct_parity_trace<'a>(
6 tx_traces: &'a [ExecTrace],
7) -> Result<Box<dyn 'a + Iterator<Item = TraceWithPosition<'a>>>, String> {
8 let empty_traces = !tx_traces.iter().any(|x| {
9 !matches!(
10 x.action,
11 Action::InternalTransferAction(_) | Action::SetAuth(_)
12 )
13 });
14 if empty_traces {
15 return Ok(Box::new(std::iter::empty()));
16 }
17
18 let call_hierarchy = build_call_hierarchy(tx_traces)?;
19 Ok(call_hierarchy.flatten_into_traces(vec![]))
20}
21
22pub enum TraceWithPosition<'a> {
23 CallCreate(CallCreateTraceWithPosition<'a>),
24 Suicide(SelfDestructTraceWithPosition<'a>),
25}
26
27pub struct CallCreateTraceWithPosition<'a> {
29 pub action: &'a ExecTrace,
30 pub result: &'a ExecTrace,
31 pub child_count: usize,
32 pub trace_path: Vec<usize>,
33}
34
35pub struct SelfDestructTraceWithPosition<'a> {
36 pub action: &'a ExecTrace,
37 pub child_count: usize,
38 pub trace_path: Vec<usize>,
39}
40
41pub struct CallCreateTraceNode<'a> {
44 action_trace: ActionTrace<'a>,
45 result_trace: ResultTrace<'a>,
46 child_nodes: Vec<TraceNode<'a>>,
47 total_child_count: usize,
48}
49
50enum TraceNode<'a> {
51 CallCreate(CallCreateTraceNode<'a>),
52 Suicide(SelfDestructTrace<'a>),
53}
54
55impl<'a> CallCreateTraceNode<'a> {
56 fn new(
64 action: ActionTrace<'a>, result: ResultTrace<'a>,
65 children: Vec<TraceNode<'a>>,
66 ) -> Result<Self, String> {
67 match (&action.0.action, &result.0.action) {
69 (Action::Call(_), Action::CallResult(_))
70 | (Action::Create(_), Action::CreateResult(_)) => {}
71 _ => {
72 return Err(format!(
73 "Type mismatch. Action: {:?}, Result: {:?}",
74 action.0.action, result.0.action
75 ))
76 }
77 }
78
79 let total_child_count = children.len()
81 + children
82 .iter()
83 .map(|x| match x {
84 TraceNode::CallCreate(node) => node.total_child_count,
85 TraceNode::Suicide(_) => 0,
86 })
87 .sum::<usize>();
88
89 Ok(Self {
90 action_trace: action,
91 result_trace: result,
92 child_nodes: children,
93 total_child_count,
94 })
95 }
96
97 pub fn flatten_into_traces(
100 self, trace_path: Vec<usize>,
101 ) -> Box<dyn 'a + Iterator<Item = TraceWithPosition<'a>>> {
102 let root_entry = std::iter::once(TraceWithPosition::CallCreate(
104 CallCreateTraceWithPosition {
105 action: self.action_trace.0,
106 result: self.result_trace.0,
107 child_count: self.total_child_count,
108 trace_path: trace_path.clone(),
109 },
110 ));
111
112 let child_entries = self.child_nodes.into_iter().enumerate().flat_map(
114 move |(idx, child)| {
115 let mut child_path = trace_path.clone();
116 child_path.push(idx);
117 match child {
118 TraceNode::CallCreate(child) => {
119 child.flatten_into_traces(child_path)
120 }
121 TraceNode::Suicide(suicide) => {
122 Box::new(std::iter::once(TraceWithPosition::Suicide(
123 SelfDestructTraceWithPosition {
124 action: suicide.0,
125 child_count: 0,
126 trace_path: child_path,
127 },
128 )))
129 }
130 }
131 },
132 );
133
134 Box::new(root_entry.chain(child_entries))
135 }
136}
137
138pub fn build_call_hierarchy<'a>(
141 traces: &'a [ExecTrace],
142) -> Result<CallCreateTraceNode<'a>, String> {
143 let mut unclosed_actions: Vec<(ActionTrace, Vec<TraceNode>)> = vec![];
145
146 let mut iter = traces.iter().filter(|x| {
148 !matches!(
149 x.action,
150 Action::InternalTransferAction(_) | Action::SetAuth(_)
151 )
152 });
153
154 while let Some(trace) = iter.next() {
155 match trace.action {
156 Action::Call(_) | Action::Create(_) => {
158 let action = ActionTrace::try_from(trace).unwrap();
159 unclosed_actions.push((action, vec![]));
160 }
161
162 Action::CallResult(_) | Action::CreateResult(_) => {
164 let result = ResultTrace::try_from(trace).unwrap();
165
166 let Some((action, children)) = unclosed_actions.pop() else {
167 return Err(format!(
168 "Orphaned result without matching action: {:?}",
169 trace
170 ));
171 };
172
173 let node = CallCreateTraceNode::new(action, result, children)?;
174
175 if let Some((_, parent_children)) = unclosed_actions.last_mut()
177 {
178 parent_children.push(TraceNode::CallCreate(node));
179 } else {
180 return if let Some(trace) = iter.next() {
181 Err(format!(
182 "Trailing traces after root node closure: {:?}",
183 trace
184 ))
185 } else {
186 Ok(node)
187 };
188 }
189 }
190 Action::SelfDestruct(_) => {
191 let action = SelfDestructTrace::try_from(trace).unwrap();
192 if let Some((_, parent_children)) = unclosed_actions.last_mut()
194 {
195 parent_children.push(TraceNode::Suicide(action));
196 } else {
197 return Err("selfdestruct trace should have parent".into());
198 }
199 }
200 Action::InternalTransferAction(_) | Action::SetAuth(_) => {
202 unreachable!()
203 }
204 }
205 }
206 Err("Incomplete trace: missing result for the root-level".into())
208}
209
210struct ActionTrace<'a>(&'a ExecTrace);
212
213struct ResultTrace<'a>(&'a ExecTrace);
215
216impl<'a> TryFrom<&'a ExecTrace> for ActionTrace<'a> {
217 type Error = &'static str;
218
219 fn try_from(trace: &'a ExecTrace) -> Result<Self, Self::Error> {
220 match trace.action {
221 Action::Call(_) | Action::Create(_) => Ok(Self(trace)),
222 _ => Err("Not an action trace"),
223 }
224 }
225}
226
227impl<'a> TryFrom<&'a ExecTrace> for ResultTrace<'a> {
228 type Error = &'static str;
229
230 fn try_from(trace: &'a ExecTrace) -> Result<Self, Self::Error> {
231 match trace.action {
232 Action::CallResult(_) | Action::CreateResult(_) => Ok(Self(trace)),
233 _ => Err("Not a result trace"),
234 }
235 }
236}
237
238struct SelfDestructTrace<'a>(&'a ExecTrace);
239
240impl<'a> TryFrom<&'a ExecTrace> for SelfDestructTrace<'a> {
241 type Error = &'static str;
242
243 fn try_from(trace: &'a ExecTrace) -> Result<Self, Self::Error> {
244 match trace.action {
245 Action::SelfDestruct(_) => Ok(Self(trace)),
246 _ => Err("Not a self-destruct trace"),
247 }
248 }
249}