1use std::io::Cursor;
2
3use bitcoin::hashes::sha256;
4use fedimint_core::core::OperationId;
5use fedimint_core::encoding::{Decodable, Encodable};
6use fedimint_core::module::registry::ModuleDecoderRegistry;
7use fedimint_core::secp256k1::{Keypair, PublicKey};
8use fedimint_core::{impl_db_lookup, impl_db_record, OutPoint, TransactionId};
9use fedimint_ln_common::{LightningGateway, LightningGatewayRegistration};
10use lightning_invoice::Bolt11Invoice;
11use serde::Serialize;
12use strum_macros::EnumIter;
13
14use crate::pay::lightningpay::LightningPayStates;
15use crate::pay::{
16 LightningPayCommon, LightningPayFunded, LightningPayRefund, LightningPayStateMachine,
17 PayInvoicePayload,
18};
19use crate::receive::{
20 LightningReceiveConfirmedInvoice, LightningReceiveStateMachine, LightningReceiveStates,
21 LightningReceiveSubmittedOffer, LightningReceiveSubmittedOfferV0,
22};
23use crate::{LightningClientStateMachines, OutgoingLightningPayment, ReceivingKey};
24
25#[repr(u8)]
26#[derive(Clone, EnumIter, Debug)]
27pub enum DbKeyPrefix {
28 ActiveGateway = 0x28,
30 PaymentResult = 0x29,
31 MetaOverridesDeprecated = 0x30,
32 LightningGateway = 0x45,
33}
34
35impl std::fmt::Display for DbKeyPrefix {
36 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37 write!(f, "{self:?}")
38 }
39}
40
41#[derive(Debug, Encodable, Decodable, Serialize)]
42pub struct ActiveGatewayKey;
43
44#[derive(Debug, Encodable, Decodable)]
45pub struct ActiveGatewayKeyPrefix;
46
47impl_db_record!(
48 key = ActiveGatewayKey,
49 value = LightningGatewayRegistration,
50 db_prefix = DbKeyPrefix::ActiveGateway,
51);
52impl_db_lookup!(
53 key = ActiveGatewayKey,
54 query_prefix = ActiveGatewayKeyPrefix
55);
56
57#[derive(Debug, Encodable, Decodable, Serialize)]
58pub struct PaymentResultKey {
59 pub payment_hash: sha256::Hash,
60}
61
62#[derive(Debug, Encodable, Decodable, Serialize)]
63pub struct PaymentResultPrefix;
64
65#[derive(Debug, Encodable, Decodable, Serialize)]
66pub struct PaymentResult {
67 pub index: u16,
68 pub completed_payment: Option<OutgoingLightningPayment>,
69}
70
71impl_db_record!(
72 key = PaymentResultKey,
73 value = PaymentResult,
74 db_prefix = DbKeyPrefix::PaymentResult,
75);
76
77impl_db_lookup!(key = PaymentResultKey, query_prefix = PaymentResultPrefix);
78
79#[derive(Debug, Encodable, Decodable, Serialize)]
80pub struct LightningGatewayKey(pub PublicKey);
81
82#[derive(Debug, Encodable, Decodable)]
83pub struct LightningGatewayKeyPrefix;
84
85impl_db_record!(
86 key = LightningGatewayKey,
87 value = LightningGatewayRegistration,
88 db_prefix = DbKeyPrefix::LightningGateway,
89);
90impl_db_lookup!(
91 key = LightningGatewayKey,
92 query_prefix = LightningGatewayKeyPrefix
93);
94
95pub(crate) fn get_v1_migrated_state(
98 operation_id: OperationId,
99 cursor: &mut Cursor<&[u8]>,
100) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
101 #[derive(Debug, Clone, Decodable)]
102 pub struct LightningReceiveConfirmedInvoiceV0 {
103 invoice: Bolt11Invoice,
104 receiving_key: Keypair,
105 }
106
107 let decoders = ModuleDecoderRegistry::default();
108 let ln_sm_variant = u16::consensus_decode(cursor, &decoders)?;
109
110 if ln_sm_variant != 2 {
112 return Ok(None);
113 }
114
115 let _ln_sm_len = u16::consensus_decode(cursor, &decoders)?;
116 let _operation_id = OperationId::consensus_decode(cursor, &decoders)?;
117 let receive_sm_variant = u16::consensus_decode(cursor, &decoders)?;
118
119 let new = match receive_sm_variant {
120 0 => {
122 let _receive_sm_len = u16::consensus_decode(cursor, &decoders)?;
123
124 let v0 = LightningReceiveSubmittedOfferV0::consensus_decode(cursor, &decoders)?;
125
126 let new_offer = LightningReceiveSubmittedOffer {
127 offer_txid: v0.offer_txid,
128 invoice: v0.invoice,
129 receiving_key: ReceivingKey::Personal(v0.payment_keypair),
130 };
131 let new_recv = LightningReceiveStateMachine {
132 operation_id,
133 state: LightningReceiveStates::SubmittedOffer(new_offer),
134 };
135 LightningClientStateMachines::Receive(new_recv)
136 }
137 2 => {
139 let _receive_sm_len = u16::consensus_decode(cursor, &decoders)?;
140 let confirmed_old =
141 LightningReceiveConfirmedInvoiceV0::consensus_decode(cursor, &decoders)?;
142 let confirmed_new = LightningReceiveConfirmedInvoice {
143 invoice: confirmed_old.invoice,
144 receiving_key: ReceivingKey::Personal(confirmed_old.receiving_key),
145 };
146 LightningClientStateMachines::Receive(LightningReceiveStateMachine {
147 operation_id,
148 state: LightningReceiveStates::ConfirmedInvoice(confirmed_new),
149 })
150 }
151 _ => return Ok(None),
152 };
153
154 let bytes = new.consensus_encode_to_vec();
155 Ok(Some((bytes, operation_id)))
156}
157
158pub(crate) fn get_v2_migrated_state(
160 operation_id: OperationId,
161 cursor: &mut Cursor<&[u8]>,
162) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
163 let decoders = ModuleDecoderRegistry::default();
164 let ln_sm_variant = u16::consensus_decode(cursor, &decoders)?;
165
166 if ln_sm_variant != 2 {
168 return Ok(None);
169 }
170
171 let _ln_sm_len = u16::consensus_decode(cursor, &decoders)?;
172 let _operation_id = OperationId::consensus_decode(cursor, &decoders)?;
173 let receive_sm_variant = u16::consensus_decode(cursor, &decoders)?;
174 if receive_sm_variant != 5 {
175 return Ok(None);
176 }
177
178 let _receive_sm_len = u16::consensus_decode(cursor, &decoders)?;
179 let old = LightningReceiveSubmittedOffer::consensus_decode(cursor, &decoders)?;
180
181 let new_recv = LightningClientStateMachines::Receive(LightningReceiveStateMachine {
182 operation_id,
183 state: LightningReceiveStates::SubmittedOffer(old),
184 });
185
186 let bytes = new_recv.consensus_encode_to_vec();
187 Ok(Some((bytes, operation_id)))
188}
189
190pub(crate) fn get_v3_migrated_state(
193 operation_id: OperationId,
194 cursor: &mut Cursor<&[u8]>,
195) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
196 let decoders = ModuleDecoderRegistry::default();
197 let ln_sm_variant = u16::consensus_decode(cursor, &decoders)?;
198
199 if ln_sm_variant != 1 {
201 return Ok(None);
202 }
203
204 let _ln_sm_len = u16::consensus_decode(cursor, &decoders)?;
205 let common = LightningPayCommon::consensus_decode(cursor, &decoders)?;
206 let pay_sm_variant = u16::consensus_decode(cursor, &decoders)?;
207
208 let _pay_sm_len = u16::consensus_decode(cursor, &decoders)?;
209
210 match pay_sm_variant {
212 2 => {
214 #[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
215 pub struct LightningPayFundedV0 {
216 pub payload: PayInvoicePayload,
217 pub gateway: LightningGateway,
218 pub timelock: u32,
219 }
220
221 let v0 = LightningPayFundedV0::consensus_decode(cursor, &decoders)?;
222 let v1 = LightningPayFunded {
223 payload: v0.payload,
224 gateway: v0.gateway,
225 timelock: v0.timelock,
226 funding_time: fedimint_core::time::now(),
227 };
228
229 let new_pay = LightningPayStateMachine {
230 common,
231 state: LightningPayStates::Funded(v1),
232 };
233 let new_sm = LightningClientStateMachines::LightningPay(new_pay);
234 let bytes = new_sm.consensus_encode_to_vec();
235 Ok(Some((bytes, operation_id)))
236 }
237 5 => {
239 #[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
240 pub struct LightningPayRefundV0 {
241 txid: TransactionId,
242 out_points: Vec<OutPoint>,
243 }
244
245 let v0 = LightningPayRefundV0::consensus_decode(cursor, &decoders)?;
246 let v1 = LightningPayRefund {
247 txid: v0.txid,
248 out_points: v0.out_points,
249 error_reason: "unknown error (database migration)".to_string(),
250 };
251 let new_pay = LightningPayStateMachine {
252 common,
253 state: LightningPayStates::Refund(v1),
254 };
255 let new_sm = LightningClientStateMachines::LightningPay(new_pay);
256 let bytes = new_sm.consensus_encode_to_vec();
257 Ok(Some((bytes, operation_id)))
258 }
259 _ => Ok(None),
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use std::str::FromStr;
266
267 use fedimint_client::db::migrate_state;
268 use fedimint_core::core::{IntoDynInstance, OperationId};
269 use fedimint_core::encoding::Encodable;
270 use fedimint_core::secp256k1::{Keypair, SECP256K1};
271 use fedimint_core::{BitcoinHash, TransactionId};
272 use lightning_invoice::Bolt11Invoice;
273 use rand::thread_rng;
274
275 use crate::db::{get_v1_migrated_state, get_v2_migrated_state};
276 use crate::receive::{
277 LightningReceiveConfirmedInvoice, LightningReceiveStateMachine, LightningReceiveStates,
278 LightningReceiveSubmittedOffer,
279 };
280 use crate::{LightningClientStateMachines, ReceivingKey};
281
282 #[tokio::test]
283 async fn test_sm_migration_to_v2_submitted() {
284 let instance_id = 0x42;
285
286 let dummy_invoice = Bolt11Invoice::from_str("lntbs1u1pj8308gsp5xhxz908q5usddjjm6mfq6nwc2nu62twwm6za69d32kyx8h49a4hqpp5j5egfqw9kf5e96nk\
287 6htr76a8kggl0xyz3pzgemv887pya4flguzsdp5235xzmntwvsxvmmjypex2en4dejxjmn8yp6xsefqvesh2cm9wsss\
288 cqp2rzjq0ag45qspt2vd47jvj3t5nya5vsn0hlhf5wel8h779npsrspm6eeuqtjuuqqqqgqqyqqqqqqqqqqqqqqqc9q\
289 yysgqddrv0jqhyf3q6z75rt7nrwx0crxme87s8rx2rt8xr9slzu0p3xg3f3f0zmqavtmsnqaj5v0y5mdzszah7thrmg\
290 2we42dvjggjkf44egqheymyw",).expect("Invalid invoice");
291 let claim_key = Keypair::new(SECP256K1, &mut thread_rng());
292 let operation_id = OperationId::new_random();
293 let txid = TransactionId::from_byte_array([42; 32]);
294
295 let submitted_offer_variant_old = {
296 let mut submitted_offer_variant = Vec::<u8>::new();
297 txid.consensus_encode(&mut submitted_offer_variant)
298 .expect("TransactionId is encodable");
299 dummy_invoice
300 .consensus_encode(&mut submitted_offer_variant)
301 .expect("Invoice is encodable");
302 claim_key
303 .consensus_encode(&mut submitted_offer_variant)
304 .expect("Keypair is encodable");
305
306 submitted_offer_variant
307 };
308
309 let receive_variant = {
310 let mut receive_variant = Vec::<u8>::new();
311 operation_id
312 .consensus_encode(&mut receive_variant)
313 .expect("OperationId is encodable");
314 0u64.consensus_encode(&mut receive_variant)
315 .expect("u64 is encodable"); submitted_offer_variant_old
317 .consensus_encode(&mut receive_variant)
318 .expect("State is encodable");
319 receive_variant
320 };
321
322 let old_state = {
323 let mut sm_bytes = Vec::<u8>::new();
324 instance_id
325 .consensus_encode(&mut sm_bytes)
326 .expect("u16 is encodable");
327 2u64.consensus_encode(&mut sm_bytes)
328 .expect("u64 is encodable"); receive_variant
330 .consensus_encode(&mut sm_bytes)
331 .expect("receive variant is encodable");
332 sm_bytes
333 };
334
335 let old_states = vec![(old_state, operation_id)];
336
337 let new_state = LightningClientStateMachines::Receive(LightningReceiveStateMachine {
338 operation_id,
339 state: LightningReceiveStates::SubmittedOffer(LightningReceiveSubmittedOffer {
340 offer_txid: txid,
341 invoice: dummy_invoice,
342 receiving_key: ReceivingKey::Personal(claim_key),
343 }),
344 })
345 .into_dyn(instance_id);
346
347 let (new_active_states, new_inactive_states) =
348 migrate_state(old_states.clone(), old_states, get_v1_migrated_state)
349 .expect("Migration failed")
350 .expect("Migration produced output");
351
352 assert_eq!(new_inactive_states.len(), 1);
353 assert_eq!(
354 new_inactive_states[0],
355 (new_state.consensus_encode_to_vec(), operation_id)
356 );
357
358 assert_eq!(new_active_states.len(), 1);
359 assert_eq!(
360 new_active_states[0],
361 (new_state.consensus_encode_to_vec(), operation_id)
362 );
363 }
364
365 #[tokio::test]
366 async fn test_sm_migration_to_v2_confirmed() -> anyhow::Result<()> {
367 let operation_id = OperationId::new_random();
368 let instance_id = 0x42;
369 let claim_key = Keypair::new(SECP256K1, &mut thread_rng());
370 let dummy_invoice = Bolt11Invoice::from_str("lntbs1u1pj8308gsp5xhxz908q5usddjjm6mfq6nwc2nu62twwm6za69d32kyx8h49a4hqpp5j5egfqw9kf5e96nk\
371 6htr76a8kggl0xyz3pzgemv887pya4flguzsdp5235xzmntwvsxvmmjypex2en4dejxjmn8yp6xsefqvesh2cm9wsss\
372 cqp2rzjq0ag45qspt2vd47jvj3t5nya5vsn0hlhf5wel8h779npsrspm6eeuqtjuuqqqqgqqyqqqqqqqqqqqqqqqc9q\
373 yysgqddrv0jqhyf3q6z75rt7nrwx0crxme87s8rx2rt8xr9slzu0p3xg3f3f0zmqavtmsnqaj5v0y5mdzszah7thrmg\
374 2we42dvjggjkf44egqheymyw",).expect("Invalid invoice");
375
376 let confirmed_variant = {
377 let mut confirmed_variant = Vec::<u8>::new();
378 dummy_invoice.consensus_encode(&mut confirmed_variant)?;
379 claim_key.consensus_encode(&mut confirmed_variant)?;
380 confirmed_variant
381 };
382
383 let receive_variant = {
384 let mut receive_variant = Vec::<u8>::new();
385 operation_id.consensus_encode(&mut receive_variant)?;
386 2u64.consensus_encode(&mut receive_variant)?; confirmed_variant.consensus_encode(&mut receive_variant)?;
388 receive_variant
389 };
390
391 let old_sm_bytes = {
392 let mut sm_bytes_old = Vec::<u8>::new();
393 instance_id.consensus_encode(&mut sm_bytes_old)?;
394 2u64.consensus_encode(&mut sm_bytes_old)?; receive_variant.consensus_encode(&mut sm_bytes_old)?;
396 sm_bytes_old
397 };
398
399 let old_states = vec![(old_sm_bytes, operation_id)];
400
401 let new_state = LightningClientStateMachines::Receive(LightningReceiveStateMachine {
402 operation_id,
403 state: LightningReceiveStates::ConfirmedInvoice(LightningReceiveConfirmedInvoice {
404 invoice: dummy_invoice,
405 receiving_key: ReceivingKey::Personal(claim_key),
406 }),
407 })
408 .into_dyn(instance_id);
409
410 let (new_active_states, new_inactive_states) =
411 migrate_state(old_states.clone(), old_states, get_v1_migrated_state)
412 .expect("Migration failed")
413 .expect("Migration produced output");
414
415 assert_eq!(new_inactive_states.len(), 1);
416 assert_eq!(
417 new_inactive_states[0],
418 (new_state.consensus_encode_to_vec(), operation_id)
419 );
420
421 assert_eq!(new_active_states.len(), 1);
422 assert_eq!(
423 new_active_states[0],
424 (new_state.consensus_encode_to_vec(), operation_id)
425 );
426
427 Ok(())
428 }
429
430 #[tokio::test]
431 async fn test_sm_migration_to_v3_submitted() {
432 let instance_id = 0x42;
433
434 let dummy_invoice = Bolt11Invoice::from_str("lntbs1u1pj8308gsp5xhxz908q5usddjjm6mfq6nwc2nu62twwm6za69d32kyx8h49a4hqpp5j5egfqw9kf5e96nk\
435 6htr76a8kggl0xyz3pzgemv887pya4flguzsdp5235xzmntwvsxvmmjypex2en4dejxjmn8yp6xsefqvesh2cm9wsss\
436 cqp2rzjq0ag45qspt2vd47jvj3t5nya5vsn0hlhf5wel8h779npsrspm6eeuqtjuuqqqqgqqyqqqqqqqqqqqqqqqc9q\
437 yysgqddrv0jqhyf3q6z75rt7nrwx0crxme87s8rx2rt8xr9slzu0p3xg3f3f0zmqavtmsnqaj5v0y5mdzszah7thrmg\
438 2we42dvjggjkf44egqheymyw",).expect("Invalid invoice");
439 let claim_key = Keypair::new(SECP256K1, &mut thread_rng());
440 let operation_id = OperationId::new_random();
441 let txid = TransactionId::from_byte_array([42; 32]);
442
443 let submitted_offer_variant_deleted = {
444 let mut submitted_offer_variant = Vec::<u8>::new();
445 txid.consensus_encode(&mut submitted_offer_variant)
446 .expect("TransactionId is encodable");
447 dummy_invoice
448 .consensus_encode(&mut submitted_offer_variant)
449 .expect("Invoice is encodable");
450 ReceivingKey::Personal(claim_key)
451 .consensus_encode(&mut submitted_offer_variant)
452 .expect("Keypair is encodable");
453
454 submitted_offer_variant
455 };
456
457 let receive_variant = {
458 let mut receive_variant = Vec::<u8>::new();
459 operation_id
460 .consensus_encode(&mut receive_variant)
461 .expect("OperationId is encodable");
462 5u64.consensus_encode(&mut receive_variant)
463 .expect("u64 is encodable"); submitted_offer_variant_deleted
465 .consensus_encode(&mut receive_variant)
466 .expect("State is encodable");
467 receive_variant
468 };
469
470 let old_state = {
471 let mut sm_bytes = Vec::<u8>::new();
472 instance_id
473 .consensus_encode(&mut sm_bytes)
474 .expect("u16 is encodable");
475 2u64.consensus_encode(&mut sm_bytes)
476 .expect("u64 is encodable"); receive_variant
478 .consensus_encode(&mut sm_bytes)
479 .expect("receive variant is encodable");
480 sm_bytes
481 };
482
483 let old_states = vec![(old_state, operation_id)];
484
485 let new_state = LightningClientStateMachines::Receive(LightningReceiveStateMachine {
486 operation_id,
487 state: LightningReceiveStates::SubmittedOffer(LightningReceiveSubmittedOffer {
488 offer_txid: txid,
489 invoice: dummy_invoice,
490 receiving_key: ReceivingKey::Personal(claim_key),
491 }),
492 })
493 .into_dyn(instance_id);
494
495 let (new_active_states, new_inactive_states) =
496 migrate_state(old_states.clone(), old_states, get_v2_migrated_state)
497 .expect("Migration failed")
498 .expect("Migration produced output");
499
500 assert_eq!(new_inactive_states.len(), 1);
501 assert_eq!(
502 new_inactive_states[0],
503 (new_state.consensus_encode_to_vec(), operation_id)
504 );
505
506 assert_eq!(new_active_states.len(), 1);
507 assert_eq!(
508 new_active_states[0],
509 (new_state.consensus_encode_to_vec(), operation_id)
510 );
511 }
512}