Skip to content

Commit a9ba8cd

Browse files
authored
Merge pull request #111 from jbcaron/events
fix: 🐛 use block storage events
2 parents f0f3373 + 28782f6 commit a9ba8cd

File tree

4 files changed

+73
-101
lines changed

4 files changed

+73
-101
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ git # Madara Changelog
22

33
## Next release
44

5+
- fix: get_events paging with continuation_token
56
- fux(getStorageAt): #28
67
- fix(genesis): #107
8+
79
- fix(class): #32 #33 #34
810
- fix(class): #116
911
- feat(class): download classes from sequencer

crates/client/rpc/src/events/mod.rs

Lines changed: 59 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
#[cfg(test)]
22
mod tests;
33

4-
use std::iter::Skip;
5-
use std::vec::IntoIter;
6-
74
use jsonrpsee::core::RpcResult;
85
use log::error;
96
use mc_rpc_core::utils::get_block_by_block_hash;
@@ -49,62 +46,49 @@ where
4946
StarknetRpcApiError::BlockNotFound
5047
})?;
5148

52-
let block_extrinsics = self
53-
.client
54-
.block_body(substrate_block_hash)
55-
.map_err(|e| {
56-
error!("Failed to retrieve block body. Substrate block hash: {substrate_block_hash}, error: {e}");
57-
StarknetRpcApiError::InternalServerError
58-
})?
59-
.ok_or(StarknetRpcApiError::BlockNotFound)?;
60-
61-
let chain_id = self.client.runtime_api().chain_id(substrate_block_hash).map_err(|_| {
62-
error!("Failed to retrieve chain id");
63-
StarknetRpcApiError::InternalServerError
49+
let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash).map_err(|e| {
50+
error!("'{e}'");
51+
StarknetRpcApiError::BlockNotFound
6452
})?;
6553

66-
let index_and_events =
67-
self.client.runtime_api().get_starknet_events_and_their_associated_tx_index(substrate_block_hash).map_err(
68-
|e| {
69-
error!(
70-
"Failed to retrieve starknet events and their associated transaction index. Substrate block \
71-
hash: {substrate_block_hash}, chain ID: {chain_id:?}, error: {e}"
72-
);
73-
StarknetRpcApiError::InternalServerError
74-
},
75-
)?;
54+
let block_hash = starknet_block.header().hash::<H>();
7655

77-
let starknet_block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash).map_err(|e| {
78-
error!("Failed to retrieve starknet block from substrate block hash: error: {e}");
56+
let chain_id = self.client.runtime_api().chain_id(substrate_block_hash).map_err(|_| {
57+
error!("Failed to retrieve chain id");
7958
StarknetRpcApiError::InternalServerError
8059
})?;
81-
let txn_hashes = self.get_cached_transaction_hashes(starknet_block.header().hash::<H>().into());
82-
let block_extrinsics_len = block_extrinsics.len();
83-
let starknet_txs =
84-
self.client.runtime_api().extrinsic_filter(substrate_block_hash, block_extrinsics).map_err(|e| {
85-
error!("Failed to filter extrinsics. Substrate block hash: {substrate_block_hash}, error: {e}");
86-
StarknetRpcApiError::InternalServerError
87-
})?;
88-
let inherent_count = block_extrinsics_len - starknet_txs.len();
89-
let mut tx_hash_and_events: Vec<(Felt252Wrapper, starknet_api::transaction::Event)> = vec![];
90-
for (index, event) in index_and_events {
91-
let tx_index = index as usize - inherent_count;
92-
let tx_hash = self.try_txn_hash_from_cache(
93-
tx_index,
94-
&txn_hashes,
95-
&starknet_txs,
96-
chain_id,
97-
Some(starknet_block.header().block_number),
98-
)?;
99-
tx_hash_and_events.push((tx_hash, event));
100-
}
10160

102-
let starknet_block = match get_block_by_block_hash(self.client.as_ref(), substrate_block_hash) {
103-
Ok(block) => block,
104-
Err(_) => return Err(StarknetRpcApiError::BlockNotFound),
61+
// get txs hashes from cache or compute them
62+
let block_txs_hashes: Vec<_> = if let Some(tx_hashes) = self.get_cached_transaction_hashes(block_hash.into()) {
63+
tx_hashes
64+
.into_iter()
65+
.map(|h| {
66+
Felt252Wrapper::try_from(h)
67+
.map(|f| f.0)
68+
.map_err(|e| {
69+
error!("'{e}'");
70+
StarknetRpcApiError::InternalServerError
71+
})
72+
.unwrap()
73+
})
74+
.collect()
75+
} else {
76+
starknet_block
77+
.transactions_hashes::<H>(chain_id.into(), Some(starknet_block.header().block_number))
78+
.map(FieldElement::from)
79+
.collect()
10580
};
10681

107-
let block_hash = starknet_block.header().hash::<H>();
82+
// get txs hashes and events from block
83+
// the txs hashes are found by the index of the ordered event
84+
let tx_hash_and_events: Vec<(Felt252Wrapper, _)> = starknet_block
85+
.events()
86+
.into_iter()
87+
.flat_map(|ordered_event| {
88+
let tx_hash = block_txs_hashes[ordered_event.index() as usize];
89+
ordered_event.events().into_iter().map(move |events| (tx_hash.into(), events.clone()))
90+
})
91+
.collect();
10892

10993
let emitted_events = tx_hash_and_events
11094
.into_iter()
@@ -134,52 +118,43 @@ where
134118
// get filter values
135119
let continuation_token = filter.continuation_token;
136120
// skip blocks with continuation token block number
137-
let from_block = filter.from_block + continuation_token.block_n;
121+
let from_block = continuation_token.block_n;
138122
let mut current_block = from_block;
139123
let to_block = filter.to_block;
140124
let from_address = filter.from_address;
141125
let keys = filter.keys;
142126
let chunk_size = filter.chunk_size;
143127

144-
let mut filtered_events = Vec::new();
128+
let mut filtered_events: Vec<EmittedEvent> = Vec::new();
145129

146130
// Iterate on block range
147131
while current_block <= to_block {
148132
let emitted_events = self.get_block_events(current_block)?;
149-
let mut unchecked_events = emitted_events.len();
150-
let events = if current_block == from_block {
151-
// check if continuation_token.event_n is not too big
152-
if (unchecked_events as u64) < continuation_token.event_n {
153-
return Err(StarknetRpcApiError::InvalidContinuationToken.into());
154-
}
155-
unchecked_events -= continuation_token.event_n as usize;
156-
emitted_events.into_iter().skip(continuation_token.event_n as usize)
157-
} else {
158-
#[allow(clippy::iter_skip_zero)]
159-
emitted_events.into_iter().skip(0)
160-
};
161-
162-
let mut n_visited = 0;
163-
let block_filtered_events = filter_events_by_params(
164-
events,
165-
from_address,
166-
&keys,
167-
chunk_size as usize - filtered_events.len(),
168-
&mut n_visited,
169-
);
170-
171-
filtered_events.extend(block_filtered_events);
133+
134+
let block_filtered_events: Vec<EmittedEvent> = filter_events_by_params(emitted_events, from_address, &keys);
135+
136+
if current_block == from_block && (block_filtered_events.len() as u64) < continuation_token.event_n {
137+
return Err(StarknetRpcApiError::InvalidContinuationToken.into());
138+
}
139+
140+
#[allow(clippy::iter_skip_zero)]
141+
let block_filtered_reduced_events: Vec<EmittedEvent> = block_filtered_events
142+
.into_iter()
143+
.skip(if current_block == from_block { continuation_token.event_n as usize } else { 0 })
144+
.take(chunk_size as usize - filtered_events.len())
145+
.collect();
146+
147+
let num_events = block_filtered_reduced_events.len();
148+
149+
filtered_events.extend(block_filtered_reduced_events);
172150

173151
if filtered_events.len() == chunk_size as usize {
174-
let token = if current_block < to_block || n_visited < unchecked_events {
175-
let mut event_n = n_visited as u64;
176-
if continuation_token.block_n == current_block {
177-
event_n += continuation_token.event_n;
178-
}
179-
Some(ContinuationToken { block_n: current_block - from_block, event_n }.to_string())
152+
let event_n = if current_block == from_block {
153+
continuation_token.event_n + chunk_size
180154
} else {
181-
None
155+
num_events as u64
182156
};
157+
let token = Some(ContinuationToken { block_n: current_block, event_n }.to_string());
183158

184159
return Ok(EventsPage { events: filtered_events, continuation_token: token });
185160
}
@@ -205,17 +180,14 @@ where
205180
/// * `(block_events: Vec<EventWrapper>, continuation_token: usize)` - A tuple of the filtered
206181
/// events and the first index which still hasn't been processed block_id and an instance of Block
207182
pub fn filter_events_by_params<'a, 'b: 'a>(
208-
events: Skip<IntoIter<EmittedEvent>>,
183+
events: Vec<EmittedEvent>,
209184
address: Option<Felt252Wrapper>,
210185
keys: &'a [Vec<FieldElement>],
211-
max_results: usize,
212-
n_visited: &'b mut usize,
213186
) -> Vec<EmittedEvent> {
214187
let mut filtered_events = vec![];
215188

216189
// Iterate on block events.
217190
for event in events {
218-
*n_visited += 1;
219191
let match_from_address = address.map_or(true, |addr| addr.0 == event.from_address);
220192
// Based on https://github.com/starkware-libs/papyrus
221193
let match_keys = keys
@@ -225,9 +197,6 @@ pub fn filter_events_by_params<'a, 'b: 'a>(
225197

226198
if match_from_address && match_keys {
227199
filtered_events.push(event);
228-
if filtered_events.len() >= max_results {
229-
break;
230-
}
231200
}
232201
}
233202
filtered_events

crates/client/rpc/src/lib.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,13 +1304,6 @@ where
13041304
/// errors, such as `PAGE_SIZE_TOO_BIG`, `INVALID_CONTINUATION_TOKEN`, `BLOCK_NOT_FOUND`, or
13051305
/// `TOO_MANY_KEYS_IN_FILTER`, returns a `StarknetRpcApiError` indicating the specific issue.
13061306
async fn get_events(&self, filter: EventFilterWithPage) -> RpcResult<EventsPage> {
1307-
let continuation_token = match filter.result_page_request.continuation_token {
1308-
Some(token) => types::ContinuationToken::parse(token).map_err(|e| {
1309-
error!("Failed to parse continuation token: {:?}", e);
1310-
StarknetRpcApiError::InvalidContinuationToken
1311-
})?,
1312-
None => types::ContinuationToken::default(),
1313-
};
13141307
let from_address = filter.event_filter.address.map(Felt252Wrapper::from);
13151308
let keys = filter.event_filter.keys.unwrap_or_default();
13161309
let chunk_size = filter.result_page_request.chunk_size;
@@ -1343,6 +1336,14 @@ where
13431336
StarknetRpcApiError::BlockNotFound
13441337
})?;
13451338

1339+
let continuation_token = match filter.result_page_request.continuation_token {
1340+
Some(token) => types::ContinuationToken::parse(token).map_err(|e| {
1341+
error!("Failed to parse continuation token: {:?}", e);
1342+
StarknetRpcApiError::InvalidContinuationToken
1343+
})?,
1344+
None => types::ContinuationToken { block_n: from_block.into(), event_n: 0 },
1345+
};
1346+
13461347
// Verify that the requested range is valid
13471348
if from_block > to_block {
13481349
return Ok(EventsPage { events: vec![], continuation_token: None });

crates/client/rpc/src/types.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@ pub enum ParseTokenError {
2727

2828
impl fmt::Display for ContinuationToken {
2929
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30-
write!(f, "{:x},{:x}", self.block_n, self.event_n)
30+
write!(f, "{}-{}", self.block_n, self.event_n)
3131
}
3232
}
3333

3434
impl ContinuationToken {
3535
pub fn parse(token: String) -> Result<Self, ParseTokenError> {
36-
let arr: Vec<&str> = token.split(',').collect();
36+
let arr: Vec<&str> = token.split('-').collect();
3737
if arr.len() != 2 {
3838
return Err(ParseTokenError::WrongToken);
3939
}
40-
let block_n = u64::from_str_radix(arr[0], 16).map_err(ParseTokenError::ParseFailed)?;
41-
let event_n = u64::from_str_radix(arr[1], 16).map_err(ParseTokenError::ParseFailed)?;
40+
let block_n = u64::from_str_radix(arr[0], 10).map_err(ParseTokenError::ParseFailed)?;
41+
let event_n = u64::from_str_radix(arr[1], 10).map_err(ParseTokenError::ParseFailed)?;
4242

4343
Ok(ContinuationToken { block_n, event_n })
4444
}

0 commit comments

Comments
 (0)