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::event::{OOBNotesReissued, OOBNotesSpent};
14use fedimint_wallet_client::events::{DepositConfirmed, WithdrawRequest};
15
16pub const ALL_GATEWAY_EVENTS: [EventKind; 11] = [
17    OutgoingPaymentStarted::KIND,
18    OutgoingPaymentSucceeded::KIND,
19    OutgoingPaymentFailed::KIND,
20    IncomingPaymentStarted::KIND,
21    IncomingPaymentSucceeded::KIND,
22    IncomingPaymentFailed::KIND,
23    CompleteLightningPaymentSucceeded::KIND,
24    OOBNotesSpent::KIND,
25    OOBNotesReissued::KIND,
26    WithdrawRequest::KIND,
27    DepositConfirmed::KIND,
28];
29
30/// Searches through the event log for all events that occurred within the
31/// specified time bounds.
32///
33/// Because it is inefficient to search the log backwards, instead this function
34/// traverses the log forwards, but in batches.
35/// All events are appended to an array until the cutoff event where the
36/// timestamp is greater than the start timestamp or the end of the log is hit.
37pub async fn get_events_for_duration(
38    client: &Arc<ClientHandle>,
39    start: SystemTime,
40    end: SystemTime,
41) -> Vec<PersistedLogEntry> {
42    const BATCH_SIZE: u64 = 10_000;
43
44    let start_micros = start
45        .duration_since(UNIX_EPOCH)
46        .expect("before unix epoch")
47        .as_micros() as u64;
48
49    let end_micros = end
50        .duration_since(UNIX_EPOCH)
51        .expect("before unix epoch")
52        .as_micros() as u64;
53
54    let batch_end = {
55        let mut dbtx = client.db().begin_transaction_nc().await;
56        dbtx.get_next_event_log_id().await
57    };
58
59    let mut batch_start = batch_end.saturating_sub(BATCH_SIZE);
60
61    // Find the "rough start" in the log by reading the log backwards in batches.
62    // Once an event with a timestamp before our start time is found, then we start
63    // traversing forward to find events that fall within our time bound.
64    while batch_start != EventLogId::LOG_START {
65        let batch = client.get_event_log(Some(batch_start), BATCH_SIZE).await;
66
67        match batch.first() {
68            Some(first_event) => {
69                if first_event.timestamp < start_micros {
70                    // Found the "rough start" where we can read forward
71                    break;
72                }
73            }
74            None => {
75                return vec![];
76            }
77        }
78
79        batch_start = batch_start.saturating_sub(BATCH_SIZE);
80    }
81
82    let mut all_events = Vec::new();
83    loop {
84        let batch = client.get_event_log(Some(batch_start), BATCH_SIZE).await;
85
86        if batch.is_empty() {
87            return all_events;
88        }
89
90        for event in batch {
91            if event.timestamp < start_micros {
92                continue;
93            }
94
95            if event.timestamp >= end_micros {
96                return all_events;
97            }
98
99            all_events.push(event);
100        }
101
102        batch_start = batch_start.saturating_add(BATCH_SIZE);
103    }
104}