fedimint_gw_client/
events.rs

1use fedimint_core::Amount;
2use fedimint_core::core::{ModuleKind, OperationId};
3use fedimint_eventlog::{
4    Event, EventKind, EventPersistence, PersistedLogEntry, StructuredPaymentEvents,
5    filter_events_by_kind, join_events,
6};
7use fedimint_ln_common::contracts::ContractId;
8use fedimint_ln_common::contracts::outgoing::OutgoingContractAccount;
9use serde::{Deserialize, Serialize};
10
11use super::pay::OutgoingPaymentError;
12
13/// LNv1 event that is emitted when an outgoing payment attempt is initiated.
14#[derive(Serialize, Deserialize, Debug)]
15pub struct OutgoingPaymentStarted {
16    /// The contract ID that uniquely identifies the outgoing contract.
17    pub contract_id: ContractId,
18
19    /// The amount of the invoice that is being paid.
20    pub invoice_amount: Amount,
21
22    /// The operation ID of the outgoing payment
23    pub operation_id: OperationId,
24}
25
26impl Event for OutgoingPaymentStarted {
27    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
28    const KIND: EventKind = EventKind::from_static("outgoing-payment-started");
29    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
30}
31
32/// LNv1 event that is emitted when an outgoing payment attempt has succeeded.
33#[derive(Serialize, Deserialize, Debug)]
34pub struct OutgoingPaymentSucceeded {
35    /// LNv1 outgoing contract
36    pub outgoing_contract: OutgoingContractAccount,
37
38    /// The contract ID that uniquely identifies the outgoing contract.
39    pub contract_id: ContractId,
40
41    /// The preimage acquired from successfully paying the invoice.
42    pub preimage: String,
43}
44
45impl Event for OutgoingPaymentSucceeded {
46    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
47    const KIND: EventKind = EventKind::from_static("outgoing-payment-succeeded");
48    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
49}
50
51/// LNv1 event that is emitted when an outgoing payment attempt has failed.
52#[derive(Serialize, Deserialize, Debug)]
53pub struct OutgoingPaymentFailed {
54    /// LNv1 outgoing contract
55    pub outgoing_contract: OutgoingContractAccount,
56
57    /// The contract ID that uniquely identifies the outgoing contract.
58    pub contract_id: ContractId,
59
60    /// The reason the outgoing payment failed.
61    pub error: OutgoingPaymentError,
62}
63
64impl Event for OutgoingPaymentFailed {
65    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
66    const KIND: EventKind = EventKind::from_static("outgoing-payment-failed");
67    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
68}
69
70/// LNv1 event that is emitted when an incoming payment attempt has started.
71#[derive(Serialize, Deserialize, Debug)]
72pub struct IncomingPaymentStarted {
73    /// The contract ID that uniquely identifies the incoming contract.
74    pub contract_id: ContractId,
75
76    /// The payment hash of the invoice that is being paid.
77    pub payment_hash: bitcoin::hashes::sha256::Hash,
78
79    /// The amount specified in the invoice.
80    pub invoice_amount: Amount,
81
82    /// The amount offered in the contract.
83    pub contract_amount: Amount,
84
85    /// The operation ID of the outgoing payment
86    pub operation_id: OperationId,
87}
88
89impl Event for IncomingPaymentStarted {
90    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
91    const KIND: EventKind = EventKind::from_static("incoming-payment-started");
92    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
93}
94
95/// LNv1 event that is emitted when an incoming payment attempt was successful.
96#[derive(Serialize, Deserialize, Debug)]
97pub struct IncomingPaymentSucceeded {
98    /// The payment hash of the invoice that was paid.
99    pub payment_hash: bitcoin::hashes::sha256::Hash,
100
101    /// The decrypted preimage that was acquired from the federation.
102    pub preimage: String,
103}
104
105impl Event for IncomingPaymentSucceeded {
106    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
107    const KIND: EventKind = EventKind::from_static("incoming-payment-succeeded");
108    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
109}
110
111/// LNv1 event that is emitted when an incoming payment attempt has failed.
112#[derive(Serialize, Deserialize, Debug)]
113pub struct IncomingPaymentFailed {
114    /// The payment hash of the invoice that failed to be paid.
115    pub payment_hash: bitcoin::hashes::sha256::Hash,
116
117    /// The reason the incoming payment attempt failed.
118    pub error: String,
119}
120
121impl Event for IncomingPaymentFailed {
122    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
123    const KIND: EventKind = EventKind::from_static("incoming-payment-failed");
124    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
125}
126
127/// LNv1 event that is emitted when a preimage was successfully revealed to the
128/// Lightning Network.
129#[derive(Serialize, Deserialize, Debug)]
130pub struct CompleteLightningPaymentSucceeded {
131    /// The payment hash of the payment.
132    pub payment_hash: bitcoin::hashes::sha256::Hash,
133}
134
135impl Event for CompleteLightningPaymentSucceeded {
136    const MODULE: Option<ModuleKind> = Some(fedimint_ln_common::KIND);
137    const KIND: EventKind = EventKind::from_static("complete-lightning-payment-succeeded");
138    const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
139}
140
141/// Computes the `StructurePaymentEvents` for all LNv1 payments.
142///
143/// Filters the event set for LNv1 events and joins them together.
144pub fn compute_lnv1_stats(
145    all_events: &[PersistedLogEntry],
146) -> (StructuredPaymentEvents, StructuredPaymentEvents) {
147    let outgoing_start_events = filter_events_by_kind(
148        all_events,
149        fedimint_ln_common::KIND,
150        OutgoingPaymentStarted::KIND,
151    )
152    .collect::<Vec<_>>();
153    let outgoing_success_events = filter_events_by_kind(
154        all_events,
155        fedimint_ln_common::KIND,
156        OutgoingPaymentSucceeded::KIND,
157    )
158    .collect::<Vec<_>>();
159    let outgoing_failure_events = filter_events_by_kind(
160        all_events,
161        fedimint_ln_common::KIND,
162        OutgoingPaymentFailed::KIND,
163    )
164    .collect::<Vec<_>>();
165
166    let outgoing_success_stats =
167        join_events::<OutgoingPaymentStarted, OutgoingPaymentSucceeded, (u64, Amount)>(
168            &outgoing_start_events,
169            &outgoing_success_events,
170            None,
171            |start_event, success_event, latency| {
172                if start_event.contract_id == success_event.contract_id {
173                    success_event
174                        .outgoing_contract
175                        .amount
176                        .checked_sub(start_event.invoice_amount)
177                        .map(|fee| (latency.as_millis() as u64, 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        None,
189        |start_event, fail_event, latency| {
190            if start_event.contract_id == fail_event.contract_id {
191                Some(latency.as_millis() as u64)
192            } else {
193                None
194            }
195        },
196    )
197    .collect::<Vec<_>>();
198
199    let incoming_start_events = filter_events_by_kind(
200        all_events,
201        fedimint_ln_common::KIND,
202        IncomingPaymentStarted::KIND,
203    )
204    .collect::<Vec<_>>();
205    let incoming_success_events = filter_events_by_kind(
206        all_events,
207        fedimint_ln_common::KIND,
208        IncomingPaymentSucceeded::KIND,
209    )
210    .collect::<Vec<_>>();
211    let incoming_failure_events = filter_events_by_kind(
212        all_events,
213        fedimint_ln_common::KIND,
214        IncomingPaymentFailed::KIND,
215    )
216    .collect::<Vec<_>>();
217    let incoming_success_stats =
218        join_events::<IncomingPaymentStarted, IncomingPaymentSucceeded, (u64, Amount)>(
219            &incoming_start_events,
220            &incoming_success_events,
221            None,
222            |start_event, success_event, latency| {
223                if start_event.payment_hash == success_event.payment_hash {
224                    start_event
225                        .contract_amount
226                        .checked_sub(start_event.invoice_amount)
227                        .map(|fee| (latency.as_millis() as u64, fee))
228                } else {
229                    None
230                }
231            },
232        )
233        .collect::<Vec<_>>();
234
235    let incoming_failure_stats = join_events::<IncomingPaymentStarted, IncomingPaymentFailed, u64>(
236        &incoming_start_events,
237        &incoming_failure_events,
238        None,
239        |start_event, fail_event, latency| {
240            if start_event.payment_hash == fail_event.payment_hash {
241                Some(latency.as_millis() as u64)
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}