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, EventPersistence, PersistedLogEntry, StructuredPaymentEvents,
8 filter_events_by_kind, 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#[derive(Serialize, Deserialize, Debug)]
18pub struct OutgoingPaymentStarted {
19 #[serde(with = "serde_millis")]
22 pub operation_start: SystemTime,
23
24 pub outgoing_contract: OutgoingContract,
26
27 pub min_contract_amount: Amount,
30
31 pub invoice_amount: Amount,
33
34 pub max_delay: u64,
36}
37
38impl Event for OutgoingPaymentStarted {
39 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
40 const KIND: EventKind = EventKind::from_static("outgoing-payment-started");
41 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
42}
43
44#[derive(Serialize, Deserialize, Debug)]
46pub struct OutgoingPaymentSucceeded {
47 pub payment_image: PaymentImage,
49
50 pub target_federation: Option<FederationId>,
52}
53
54impl Event for OutgoingPaymentSucceeded {
55 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
56 const KIND: EventKind = EventKind::from_static("outgoing-payment-succeeded");
57 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
58}
59
60#[derive(Serialize, Deserialize, Debug)]
62pub struct OutgoingPaymentFailed {
63 pub payment_image: PaymentImage,
65
66 pub error: Cancelled,
68}
69
70impl Event for OutgoingPaymentFailed {
71 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
72 const KIND: EventKind = EventKind::from_static("outgoing-payment-failed");
73 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
74}
75
76#[derive(Serialize, Deserialize, Debug)]
79pub struct IncomingPaymentStarted {
80 #[serde(with = "serde_millis")]
83 pub operation_start: SystemTime,
84
85 pub incoming_contract_commitment: Commitment,
87
88 pub invoice_amount: Amount,
90}
91
92impl Event for IncomingPaymentStarted {
93 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
94 const KIND: EventKind = EventKind::from_static("incoming-payment-started");
95 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
96}
97
98#[derive(Serialize, Deserialize, Debug)]
101pub struct IncomingPaymentSucceeded {
102 pub payment_image: PaymentImage,
104}
105
106impl Event for IncomingPaymentSucceeded {
107 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
108 const KIND: EventKind = EventKind::from_static("incoming-payment-succeeded");
109 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
110}
111
112#[derive(Serialize, Deserialize, Debug)]
114pub struct IncomingPaymentFailed {
115 pub payment_image: PaymentImage,
117
118 pub error: String,
120}
121
122impl Event for IncomingPaymentFailed {
123 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
124 const KIND: EventKind = EventKind::from_static("incoming-payment-failed");
125 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
126}
127
128#[derive(Serialize, Deserialize, Debug)]
132pub struct CompleteLightningPaymentSucceeded {
133 pub payment_image: PaymentImage,
135}
136
137impl Event for CompleteLightningPaymentSucceeded {
138 const MODULE: Option<ModuleKind> = Some(fedimint_lnv2_common::KIND);
139 const KIND: EventKind = EventKind::from_static("complete-lightning-payment-succeeded");
140 const PERSISTENCE: EventPersistence = EventPersistence::Persistent;
141}
142
143pub 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 None,
173 |start_event, success_event, latency| {
174 if start_event.outgoing_contract.payment_image == success_event.payment_image {
175 start_event
176 .min_contract_amount
177 .checked_sub(start_event.invoice_amount)
178 .map(|fee| (latency.as_millis() as u64, fee))
179 } else {
180 None
181 }
182 },
183 )
184 .collect::<Vec<_>>();
185
186 let outgoing_failure_stats = join_events::<OutgoingPaymentStarted, OutgoingPaymentFailed, u64>(
187 &outgoing_start_events,
188 &outgoing_failure_events,
189 None,
190 |start_event, fail_event, latency| {
191 if start_event.outgoing_contract.payment_image == fail_event.payment_image {
192 Some(latency.as_millis() as u64)
193 } else {
194 None
195 }
196 },
197 )
198 .collect::<Vec<_>>();
199
200 let incoming_start_events = filter_events_by_kind(
201 all_events,
202 fedimint_lnv2_common::KIND,
203 IncomingPaymentStarted::KIND,
204 )
205 .collect::<Vec<_>>();
206 let incoming_success_events = filter_events_by_kind(
207 all_events,
208 fedimint_lnv2_common::KIND,
209 IncomingPaymentSucceeded::KIND,
210 )
211 .collect::<Vec<_>>();
212 let incoming_failure_events = filter_events_by_kind(
213 all_events,
214 fedimint_lnv2_common::KIND,
215 IncomingPaymentFailed::KIND,
216 )
217 .collect::<Vec<_>>();
218
219 let incoming_success_stats =
220 join_events::<IncomingPaymentStarted, IncomingPaymentSucceeded, (u64, Amount)>(
221 &incoming_start_events,
222 &incoming_success_events,
223 None,
224 |start_event, success_event, latency| {
225 if start_event.incoming_contract_commitment.payment_image
226 == success_event.payment_image
227 {
228 start_event
229 .invoice_amount
230 .checked_sub(start_event.incoming_contract_commitment.amount)
231 .map(|fee| (latency.as_millis() as u64, fee))
232 } else {
233 None
234 }
235 },
236 )
237 .collect::<Vec<_>>();
238
239 let incoming_failure_stats = join_events::<IncomingPaymentStarted, IncomingPaymentFailed, u64>(
240 &incoming_start_events,
241 &incoming_failure_events,
242 None,
243 |start_event, fail_event, latency| {
244 if start_event.incoming_contract_commitment.payment_image == fail_event.payment_image {
245 Some(latency.as_millis() as u64)
246 } else {
247 None
248 }
249 },
250 )
251 .collect::<Vec<_>>();
252
253 let outgoing = StructuredPaymentEvents::new(&outgoing_success_stats, outgoing_failure_stats);
254 let incoming = StructuredPaymentEvents::new(&incoming_success_stats, incoming_failure_stats);
255 (outgoing, incoming)
256}