Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit e5be4da

Browse files
juanbonofannyguthmann
authored andcommitted
fix get_sorted_events issue (#1024)
* add failing test that reproduce the issue * fix the bug * fix test since now 2 events with the same order are ok * handle multiple events * fix comments * cargo fmt
1 parent 4c47b1a commit e5be4da

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

rpc_state_reader/tests/sir_tests.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ fn starknet_in_rust_test_case_tx(hash: &str, block_number: u64, chain: RpcChain)
262262
..
263263
} = call_info.unwrap();
264264

265+
// check Cairo VM execution resources
265266
assert_eq_sorted!(
266267
execution_resources,
267268
trace
@@ -271,6 +272,8 @@ fn starknet_in_rust_test_case_tx(hash: &str, block_number: u64, chain: RpcChain)
271272
.execution_resources,
272273
"execution resources mismatch"
273274
);
275+
276+
// check amount of internal calls
274277
assert_eq!(
275278
internal_calls.len(),
276279
trace
@@ -282,6 +285,7 @@ fn starknet_in_rust_test_case_tx(hash: &str, block_number: u64, chain: RpcChain)
282285
"internal calls length mismatch"
283286
);
284287

288+
// check actual fee calculation
285289
if receipt.actual_fee != actual_fee {
286290
let diff = 100 * receipt.actual_fee.abs_diff(actual_fee) / receipt.actual_fee;
287291

@@ -294,6 +298,37 @@ fn starknet_in_rust_test_case_tx(hash: &str, block_number: u64, chain: RpcChain)
294298
}
295299
}
296300

301+
#[test_case(
302+
"0x01e91fa12be4424264c8cad29f481a67d5d8e23f7abf94add734d64b91c90021",
303+
RpcChain::MainNet,
304+
219797,
305+
7
306+
)]
307+
#[test_case(
308+
"0x03ec45f8369513b0f48db25f2cf18c70c50e7d3119505ab15e39ae4ca2eb06cf",
309+
RpcChain::MainNet,
310+
219764,
311+
7
312+
)]
313+
#[test_case(
314+
"0x00164bfc80755f62de97ae7c98c9d67c1767259427bcf4ccfcc9683d44d54676",
315+
RpcChain::MainNet,
316+
197000,
317+
3
318+
)]
319+
fn test_sorted_events(
320+
tx_hash: &str,
321+
chain: RpcChain,
322+
block_number: u64,
323+
expected_amount_of_events: usize,
324+
) {
325+
let (tx_info, _trace, _receipt) = execute_tx(tx_hash, chain, BlockNumber(block_number));
326+
327+
let events_len = tx_info.get_sorted_events().unwrap().len();
328+
329+
assert_eq!(expected_amount_of_events, events_len);
330+
}
331+
297332
#[test_case(
298333
"0x00b6d59c19d5178886b4c939656167db0660fe325345138025a3cc4175b21897",
299334
200303, // real block 200304

src/execution/mod.rs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -104,48 +104,64 @@ impl CallInfo {
104104
)
105105
}
106106

107-
/// Yields the contract calls in DFS (preorder).
107+
/// Returns the contract calls in DFS (preorder).
108108
pub fn gen_call_topology(&self) -> Vec<CallInfo> {
109109
let mut calls = Vec::new();
110-
if self.internal_calls.is_empty() {
111-
calls.push(self.clone())
112-
} else {
113-
calls.push(self.clone());
114-
for call_info in self.internal_calls.clone() {
115-
calls.extend(call_info.gen_call_topology());
110+
// add the current call
111+
calls.push(self.clone());
112+
113+
// if it has internal calls we need to add them too.
114+
if !self.internal_calls.is_empty() {
115+
for inner_call in self.internal_calls.clone() {
116+
calls.extend(inner_call.gen_call_topology());
116117
}
117118
}
119+
118120
calls
119121
}
120122

121-
/// Returns a list of Starknet Event objects collected during the execution, sorted by the order
123+
/// Returns a list of [`Event`] objects collected during the execution, sorted by the order
122124
/// in which they were emitted.
123125
pub fn get_sorted_events(&self) -> Result<Vec<Event>, TransactionError> {
126+
// collect a vector of the full call topology (all the internal
127+
// calls performed during the current call)
124128
let calls = self.gen_call_topology();
125-
let n_events = calls.iter().fold(0, |acc, c| acc + c.events.len());
126-
127-
let mut starknet_events: Vec<Option<Event>> = (0..n_events).map(|_| None).collect();
128-
129-
for call in calls {
130-
for ordered_event in call.events {
131-
let event = Event::new(ordered_event.clone(), call.contract_address.clone());
132-
starknet_events.remove((ordered_event.order as isize - 1).max(0) as usize);
133-
starknet_events.insert(
134-
(ordered_event.order as isize - 1).max(0) as usize,
135-
Some(event),
136-
);
129+
let mut collected_events = Vec::new();
130+
131+
// for each call, collect its ordered events
132+
for c in calls {
133+
collected_events.extend(
134+
c.events
135+
.iter()
136+
.map(|oe| (oe.clone(), c.contract_address.clone())),
137+
);
138+
}
139+
// sort the collected events using the ordering given by the order
140+
collected_events.sort_by_key(|(oe, _)| oe.order);
141+
142+
// check that there is no holes.
143+
// since it is already sorted, we only need to check for continuity
144+
let mut i = 0;
145+
for (oe, _) in collected_events.iter() {
146+
if i == oe.order {
147+
continue;
148+
}
149+
i += 1;
150+
if i != oe.order {
151+
return Err(TransactionError::UnexpectedHolesInEventOrder);
137152
}
138153
}
139154

140-
let are_all_some = starknet_events.iter().all(|e| e.is_some());
141-
142-
if !are_all_some {
143-
return Err(TransactionError::UnexpectedHolesInEventOrder);
144-
}
145-
Ok(starknet_events.into_iter().flatten().collect())
155+
// now that it is ordered and without holes, we can discard the order and
156+
// convert each [`OrderedEvent`] to the underlying [`Event`].
157+
let collected_events = collected_events
158+
.into_iter()
159+
.map(|(oe, ca)| Event::new(oe, ca))
160+
.collect();
161+
Ok(collected_events)
146162
}
147163

148-
/// Returns a list of Starknet L2ToL1MessageInfo objects collected during the execution, sorted
164+
/// Returns a list of L2ToL1MessageInfo objects collected during the execution, sorted
149165
/// by the order in which they were sent.
150166
pub fn get_sorted_l2_to_l1_messages(&self) -> Result<Vec<L2toL1MessageInfo>, TransactionError> {
151167
let calls = self.gen_call_topology();
@@ -586,6 +602,8 @@ impl TransactionExecutionInfo {
586602
})
587603
}
588604

605+
/// Returns an ordered vector with all the event emitted during the transaction.
606+
/// Including the ones emitted by internal calls.
589607
pub fn get_sorted_events(&self) -> Result<Vec<Event>, TransactionError> {
590608
let calls = self.non_optional_calls();
591609
let mut sorted_events: Vec<Event> = Vec::new();
@@ -838,7 +856,7 @@ mod tests {
838856

839857
call_root.internal_calls = [child1, child2].to_vec();
840858

841-
assert!(call_root.get_sorted_events().is_err())
859+
assert!(call_root.get_sorted_events().is_ok())
842860
}
843861

844862
#[test]

0 commit comments

Comments
 (0)