fedimint_gwv2_client/
events.rs

1use std::time::SystemTime;
2
3use fedimint_core::Amount;
4use fedimint_core::config::FederationId;
5use fedimint_core::core::ModuleKind;
6use fedimint_eventlog::{
7    Event, EventKind, PersistedLogEntry, StructuredPaymentEvents, filter_events_by_kind,
8    join_events,
9};
10use fedimint_lnv2_common::contracts::{Commitment, OutgoingContract, PaymentImage};
11use serde::{Deserialize, Serialize};
12use serde_millis;
13
14use super::send_sm::Cancelled;
15
16/// Event that is emitted when an outgoing payment attempt is initiated.
17#[derive(Serialize, Deserialize, Debug)]
18pub struct OutgoingPaymentStarted {
19    /// The timestamp that the operation begins, including the API calls to the
20    /// federation to get the consensus block height.
21    #[serde(with = "serde_millis")]
22    pub operation_start: SystemTime,
23
24    /// The outgoing contract for this payment.
25    pub outgoing_contract: OutgoingContract,
26
27    /// The minimum amount that must be escrowed for the payment (includes the
28    /// gateway's fee)
29    pub min_contract_amount: Amount,
30
31    /// The amount requested in the invoice.
32    pub invoice_amount: Amount,
33
34    /// The max delay of the payment in blocks.
35    pub max_delay: u64,
36}
37
38impl Event for OutgoingPaymentStarted {
39    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
40
41    const KIND: EventKind = EventKind::from_static("outgoing-payment-started");
42}
43
44/// Event that is emitted when an outgoing payment attempt has succeeded.
45#[derive(Serialize, Deserialize, Debug)]
46pub struct OutgoingPaymentSucceeded {
47    /// The payment image of the invoice that was paid.
48    pub payment_image: PaymentImage,
49
50    /// The target federation ID if a swap was performed, otherwise `None`.
51    pub target_federation: Option<FederationId>,
52}
53
54impl Event for OutgoingPaymentSucceeded {
55    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
56
57    const KIND: EventKind = EventKind::from_static("outgoing-payment-succeeded");
58}
59
60/// Event that is emitted when an outgoing payment attempt has failed.
61#[derive(Serialize, Deserialize, Debug)]
62pub struct OutgoingPaymentFailed {
63    /// The payment image of the invoice that failed.
64    pub payment_image: PaymentImage,
65
66    /// The reason the outgoing payment was cancelled.
67    pub error: Cancelled,
68}
69
70impl Event for OutgoingPaymentFailed {
71    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
72
73    const KIND: EventKind = EventKind::from_static("outgoing-payment-failed");
74}
75
76/// Event that is emitted when an incoming payment attempt has started. Includes
77/// both internal swaps and outside LN payments.
78#[derive(Serialize, Deserialize, Debug)]
79pub struct IncomingPaymentStarted {
80    /// The timestamp that the operation begins, including any metadata checks
81    /// before the state machine has spawned.
82    #[serde(with = "serde_millis")]
83    pub operation_start: SystemTime,
84
85    /// The commitment for the incoming contract.
86    pub incoming_contract_commitment: Commitment,
87
88    /// The amount requested in the invoice.
89    pub invoice_amount: Amount,
90}
91
92impl Event for IncomingPaymentStarted {
93    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
94
95    const KIND: EventKind = EventKind::from_static("incoming-payment-started");
96}
97
98/// Event that is emitted when an incoming payment attempt has succeeded.
99/// Includes both internal swaps and outside LN payments.
100#[derive(Serialize, Deserialize, Debug)]
101pub struct IncomingPaymentSucceeded {
102    /// The payment image of the invoice that was paid.
103    pub payment_image: PaymentImage,
104}
105
106impl Event for IncomingPaymentSucceeded {
107    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
108
109    const KIND: EventKind = EventKind::from_static("incoming-payment-succeeded");
110}
111
112/// Event that is emitted when an incoming payment attempt has failed.
113#[derive(Serialize, Deserialize, Debug)]
114pub struct IncomingPaymentFailed {
115    /// The payment image of the invoice that failed
116    pub payment_image: PaymentImage,
117
118    /// The reason the incoming payment failed
119    pub error: String,
120}
121
122impl Event for IncomingPaymentFailed {
123    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
124
125    const KIND: EventKind = EventKind::from_static("incoming-payment-failed");
126}
127
128/// Event that is emitted when a preimage is revealed to the Lightning network.
129/// Only emitted for payments that are received from an external Lightning node,
130/// not internal swaps.
131#[derive(Serialize, Deserialize, Debug)]
132pub struct CompleteLightningPaymentSucceeded {
133    /// The payment image of the invoice that was paid.
134    pub payment_image: PaymentImage,
135}
136
137impl Event for CompleteLightningPaymentSucceeded {
138    const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
139
140    const KIND: EventKind = EventKind::from_static("complete-lightning-payment-succeeded");
141}
142
143/// Computes the `StructurePaymentEvents` for all LNv2 payments.
144///
145/// Filters the event set for LNv2 events and joins them together.
146pub fn compute_lnv2_stats(
147    all_events: &[PersistedLogEntry],
148) -> (StructuredPaymentEvents, StructuredPaymentEvents) {
149    let outgoing_start_events = filter_events_by_kind(
150        all_events,
151        fedimint_lnv2_common::KIND,
152        OutgoingPaymentStarted::KIND,
153    )
154    .collect::<Vec<_>>();
155    let outgoing_success_events = filter_events_by_kind(
156        all_events,
157        fedimint_lnv2_common::KIND,
158        OutgoingPaymentSucceeded::KIND,
159    )
160    .collect::<Vec<_>>();
161    let outgoing_failure_events = filter_events_by_kind(
162        all_events,
163        fedimint_lnv2_common::KIND,
164        OutgoingPaymentFailed::KIND,
165    )
166    .collect::<Vec<_>>();
167
168    let outgoing_success_stats =
169        join_events::<OutgoingPaymentStarted, OutgoingPaymentSucceeded, (u64, Amount)>(
170            &outgoing_start_events,
171            &outgoing_success_events,
172            |start_event, success_event, latency| {
173                if start_event.outgoing_contract.payment_image == success_event.payment_image {
174                    start_event
175                        .min_contract_amount
176                        .checked_sub(start_event.invoice_amount)
177                        .map(|fee| (latency, fee))
178                } else {
179                    None
180                }
181            },
182        )
183        .collect::<Vec<_>>();
184
185    let outgoing_failure_stats = join_events::<OutgoingPaymentStarted, OutgoingPaymentFailed, u64>(
186        &outgoing_start_events,
187        &outgoing_failure_events,
188        |start_event, fail_event, latency| {
189            if start_event.outgoing_contract.payment_image == fail_event.payment_image {
190                Some(latency)
191            } else {
192                None
193            }
194        },
195    )
196    .collect::<Vec<_>>();
197
198    let incoming_start_events = filter_events_by_kind(
199        all_events,
200        fedimint_lnv2_common::KIND,
201        IncomingPaymentStarted::KIND,
202    )
203    .collect::<Vec<_>>();
204    let incoming_success_events = filter_events_by_kind(
205        all_events,
206        fedimint_lnv2_common::KIND,
207        IncomingPaymentSucceeded::KIND,
208    )
209    .collect::<Vec<_>>();
210    let incoming_failure_events = filter_events_by_kind(
211        all_events,
212        fedimint_lnv2_common::KIND,
213        IncomingPaymentFailed::KIND,
214    )
215    .collect::<Vec<_>>();
216
217    let incoming_success_stats =
218        join_events::<IncomingPaymentStarted, IncomingPaymentSucceeded, (u64, Amount)>(
219            &incoming_start_events,
220            &incoming_success_events,
221            |start_event, success_event, latency| {
222                if start_event.incoming_contract_commitment.payment_image
223                    == success_event.payment_image
224                {
225                    start_event
226                        .invoice_amount
227                        .checked_sub(start_event.incoming_contract_commitment.amount)
228                        .map(|fee| (latency, fee))
229                } else {
230                    None
231                }
232            },
233        )
234        .collect::<Vec<_>>();
235
236    let incoming_failure_stats = join_events::<IncomingPaymentStarted, IncomingPaymentFailed, u64>(
237        &incoming_start_events,
238        &incoming_failure_events,
239        |start_event, fail_event, latency| {
240            if start_event.incoming_contract_commitment.payment_image == fail_event.payment_image {
241                Some(latency)
242            } else {
243                None
244            }
245        },
246    )
247    .collect::<Vec<_>>();
248
249    let outgoing = StructuredPaymentEvents::new(&outgoing_success_stats, outgoing_failure_stats);
250    let incoming = StructuredPaymentEvents::new(&incoming_success_stats, incoming_failure_stats);
251    (outgoing, incoming)
252}