Skip to content

Commit 24c7d3f

Browse files
EfeDurmaz16Slokh
andauthored
fix(session): preserve halved log scan ranges (#462)
Co-authored-by: Kartik <slokhx@gmail.com>
1 parent 6271619 commit 24c7d3f

2 files changed

Lines changed: 98 additions & 11 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
tempo-common: patch
3+
---
4+
5+
Preserve both halves of an oversized session channel log scan when an RPC requires a smaller `eth_getLogs` range, avoiding skipped upper-half ranges during wallet session recovery.

crates/tempo-common/src/payment/session/channel.rs

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ fn should_halve_range(err: &str, chunk_start: u64, chunk_end: u64) -> bool {
109109
is_rpc_range_error(err) && (chunk_end - chunk_start) > 1000
110110
}
111111

112+
fn split_log_query_range(chunk_start: u64, chunk_end: u64) -> Option<((u64, u64), (u64, u64))> {
113+
if chunk_end <= chunk_start {
114+
return None;
115+
}
116+
117+
let lower_end = chunk_start + ((chunk_end - chunk_start) / 2);
118+
if lower_end >= chunk_end {
119+
return None;
120+
}
121+
122+
Some(((chunk_start, lower_end), (lower_end + 1, chunk_end)))
123+
}
124+
112125
async fn append_discovered_channels_from_logs(
113126
logs: &[alloy::rpc::types::Log],
114127
provider: &alloy::providers::RootProvider<alloy::network::Ethereum>,
@@ -264,41 +277,80 @@ pub async fn find_all_channels_for_payer(
264277
"scanning blocks for ChannelOpened events"
265278
);
266279

280+
let mut pending_ranges = Vec::new();
267281
let mut chunk_end = latest;
268-
while chunk_end > earliest {
269-
let chunk_start = chunk_end
270-
.saturating_sub(LOG_QUERY_BLOCK_RANGE)
271-
.max(earliest);
282+
while chunk_end > earliest || !pending_ranges.is_empty() {
283+
let ((chunk_start, query_end), from_pending_range) = match pending_ranges.pop() {
284+
Some(range) => (range, true),
285+
None => (
286+
(
287+
chunk_end
288+
.saturating_sub(LOG_QUERY_BLOCK_RANGE)
289+
.max(earliest),
290+
chunk_end,
291+
),
292+
false,
293+
),
294+
};
272295

273296
let filter = Filter::new()
274297
.address(escrow)
275298
.event_signature(event_topic)
276299
.topic2(payer_topic)
277300
.from_block(BlockNumberOrTag::Number(chunk_start))
278-
.to_block(BlockNumberOrTag::Number(chunk_end));
301+
.to_block(BlockNumberOrTag::Number(query_end));
279302

280303
let logs = match provider.get_logs(&filter).await {
281304
Ok(logs) => logs,
282305
Err(e) => {
283306
let err_str = e.to_string();
284-
if should_halve_range(&err_str, chunk_start, chunk_end) {
285-
let halved = (chunk_end - chunk_start) / 2;
307+
if should_halve_range(&err_str, chunk_start, query_end) {
308+
let Some((lower, upper)) = split_log_query_range(chunk_start, query_end) else {
309+
tracing::warn!(
310+
network = network.as_str(),
311+
chunk_start,
312+
chunk_end = query_end,
313+
%e,
314+
"failed to split oversized log query range"
315+
);
316+
if !from_pending_range {
317+
if chunk_start == earliest {
318+
break;
319+
}
320+
chunk_end = chunk_start.saturating_sub(1);
321+
}
322+
continue;
323+
};
286324
tracing::debug!(
287325
network = network.as_str(),
288-
old_range = chunk_end - chunk_start,
289-
new_range = halved,
326+
old_range = query_end - chunk_start,
327+
lower_start = lower.0,
328+
lower_end = lower.1,
329+
upper_start = upper.0,
330+
upper_end = upper.1,
290331
"RPC range too large, halving"
291332
);
292-
chunk_end = chunk_start + halved;
333+
pending_ranges.push(upper);
334+
pending_ranges.push(lower);
335+
if !from_pending_range {
336+
if chunk_start == earliest {
337+
chunk_end = earliest;
338+
} else {
339+
chunk_end = chunk_start.saturating_sub(1);
340+
}
341+
}
293342
continue;
294343
}
295344
tracing::warn!(
296345
network = network.as_str(),
297346
chunk_start,
298-
chunk_end,
347+
chunk_end = query_end,
299348
%e,
300349
"failed to query logs, skipping block range"
301350
);
351+
if from_pending_range {
352+
continue;
353+
}
302354
if chunk_start == earliest {
303355
break;
304356
}
@@ -309,6 +361,9 @@ pub async fn find_all_channels_for_payer(
309361

310362
append_discovered_channels_from_logs(&logs, &provider, network, escrow, &mut results).await;
311363

364+
if from_pending_range {
365+
continue;
366+
}
312367
if chunk_start == earliest {
313368
break;
314369
}
@@ -389,3 +444,30 @@ pub async fn query_token_balance(
389444
})?;
390445
Ok(balance)
391446
}
447+
448+
#[cfg(test)]
449+
mod tests {
450+
use super::*;
451+
452+
#[test]
453+
fn split_log_query_range_covers_both_halves_without_gap() {
454+
let (lower, upper) = split_log_query_range(80, 100).expect("range should split");
455+
456+
assert_eq!(lower, (80, 90));
457+
assert_eq!(upper, (91, 100));
458+
}
459+
460+
#[test]
461+
fn split_log_query_range_preserves_single_block_boundary() {
462+
let (lower, upper) = split_log_query_range(0, 1).expect("two block range should split");
463+
464+
assert_eq!(lower, (0, 0));
465+
assert_eq!(upper, (1, 1));
466+
}
467+
468+
#[test]
469+
fn split_log_query_range_rejects_empty_range() {
470+
assert_eq!(split_log_query_range(42, 42), None);
471+
assert_eq!(split_log_query_range(43, 42), None);
472+
}
473+
}

0 commit comments

Comments
 (0)