Skip to main content

fedimint_gateway_server/
events.rs

1use std::sync::Arc;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use fedimint_client::ClientHandle;
5use fedimint_eventlog::{
6    DBTransactionEventLogExt, Event, EventKind, EventLogId, PersistedLogEntry,
7};
8use fedimint_gwv2_client::events::{
9    CompleteLightningPaymentSucceeded, IncomingPaymentFailed, IncomingPaymentStarted,
10    IncomingPaymentSucceeded, OutgoingPaymentFailed, OutgoingPaymentStarted,
11    OutgoingPaymentSucceeded,
12};
13use fedimint_mint_client::events::{OOBNotesReissued, OOBNotesSpent};
14use fedimint_wallet_client::events::{DepositConfirmed, WithdrawRequest};
15
16/// The set of gateway payment-related event kinds used as the default filter
17/// for the `payment_log` API when no explicit `event_kinds` are provided.
18///
19/// This does not include all events in the log (e.g. `tx-created`,
20/// `tx-accepted`, `NoteCreated`, `NoteSpent` are excluded), so paginated
21/// results filtered to these kinds will have non-contiguous event IDs.
22pub const ALL_GATEWAY_EVENTS: [EventKind; 11] = [
23    OutgoingPaymentStarted::KIND,
24    OutgoingPaymentSucceeded::KIND,
25    OutgoingPaymentFailed::KIND,
26    IncomingPaymentStarted::KIND,
27    IncomingPaymentSucceeded::KIND,
28    IncomingPaymentFailed::KIND,
29    CompleteLightningPaymentSucceeded::KIND,
30    OOBNotesSpent::KIND,
31    OOBNotesReissued::KIND,
32    WithdrawRequest::KIND,
33    DepositConfirmed::KIND,
34];
35
36/// Searches through the event log for all events that occurred within the
37/// specified time bounds.
38///
39/// Because it is inefficient to search the log backwards, instead this function
40/// traverses the log forwards, but in batches.
41/// All events are appended to an array until the cutoff event where the
42/// timestamp is greater than the start timestamp or the end of the log is hit.
43pub async fn get_events_for_duration(
44    client: &Arc<ClientHandle>,
45    start: SystemTime,
46    end: SystemTime,
47) -> Vec<PersistedLogEntry> {
48    const BATCH_SIZE: u64 = 10_000;
49
50    let start_micros = start
51        .duration_since(UNIX_EPOCH)
52        .expect("before unix epoch")
53        .as_micros() as u64;
54
55    let end_micros = end
56        .duration_since(UNIX_EPOCH)
57        .expect("before unix epoch")
58        .as_micros() as u64;
59
60    let batch_end = {
61        let mut dbtx = client.db().begin_transaction_nc().await;
62        dbtx.get_next_event_log_id().await
63    };
64
65    let mut batch_start = batch_end.saturating_sub(BATCH_SIZE);
66
67    // Find the "rough start" in the log by reading the log backwards in batches.
68    // Once an event with a timestamp before our start time is found, then we start
69    // traversing forward to find events that fall within our time bound.
70    while batch_start != EventLogId::LOG_START {
71        let batch = client.get_event_log(Some(batch_start), BATCH_SIZE).await;
72
73        match batch.first() {
74            Some(first_event) => {
75                if first_event.as_raw().ts_usecs < start_micros {
76                    // Found the "rough start" where we can read forward
77                    break;
78                }
79            }
80            None => {
81                return vec![];
82            }
83        }
84
85        batch_start = batch_start.saturating_sub(BATCH_SIZE);
86    }
87
88    let mut all_events = Vec::new();
89    loop {
90        let batch = client.get_event_log(Some(batch_start), BATCH_SIZE).await;
91
92        if batch.is_empty() {
93            return all_events;
94        }
95
96        for event in batch {
97            if event.as_raw().ts_usecs < start_micros {
98                continue;
99            }
100
101            if event.as_raw().ts_usecs >= end_micros {
102                return all_events;
103            }
104
105            all_events.push(event);
106        }
107
108        batch_start = batch_start.saturating_add(BATCH_SIZE);
109    }
110}