1use std::io::Cursor;
2
3use bitcoin::hashes::sha256;
4use fedimint_core::core::OperationId;
5use fedimint_core::db::DbMigrationError;
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::module::registry::ModuleDecoderRegistry;
8use fedimint_core::secp256k1::{Keypair, PublicKey};
9use fedimint_core::{OutPoint, TransactionId, impl_db_lookup, impl_db_record};
10use fedimint_ln_common::{LightningGateway, LightningGatewayRegistration};
11use lightning_invoice::Bolt11Invoice;
12use serde::Serialize;
13use strum_macros::EnumIter;
14
15use crate::pay::lightningpay::LightningPayStates;
16use crate::pay::{
17 LightningPayCommon, LightningPayFunded, LightningPayRefund, LightningPayStateMachine,
18 PayInvoicePayload,
19};
20use crate::receive::{
21 LightningReceiveConfirmedInvoice, LightningReceiveStateMachine, LightningReceiveStates,
22 LightningReceiveSubmittedOffer, LightningReceiveSubmittedOfferV0,
23};
24use crate::recurring::RecurringPaymentCodeEntry;
25use crate::{LightningClientStateMachines, OutgoingLightningPayment, ReceivingKey};
26
27#[repr(u8)]
28#[derive(Clone, EnumIter, Debug)]
29pub enum DbKeyPrefix {
30 ActiveGateway = 0x28,
32 PaymentResult = 0x29,
33 MetaOverridesDeprecated = 0x30,
34 LightningGateway = 0x45,
35 RecurringPaymentKey = 0x46,
36 ExternalReservedStart = 0xb0,
39 CoreInternalReservedStart = 0xd0,
42 CoreInternalReservedEnd = 0xff,
43}
44
45impl std::fmt::Display for DbKeyPrefix {
46 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47 write!(f, "{self:?}")
48 }
49}
50
51#[derive(Debug, Encodable, Decodable, Serialize)]
52pub struct ActiveGatewayKey;
53
54#[derive(Debug, Encodable, Decodable)]
55pub struct ActiveGatewayKeyPrefix;
56
57impl_db_record!(
58 key = ActiveGatewayKey,
59 value = LightningGatewayRegistration,
60 db_prefix = DbKeyPrefix::ActiveGateway,
61);
62impl_db_lookup!(
63 key = ActiveGatewayKey,
64 query_prefix = ActiveGatewayKeyPrefix
65);
66
67#[derive(Debug, Encodable, Decodable, Serialize)]
68pub struct PaymentResultKey {
69 pub payment_hash: sha256::Hash,
70}
71
72#[derive(Debug, Encodable, Decodable, Serialize)]
73pub struct PaymentResultPrefix;
74
75#[derive(Debug, Encodable, Decodable, Serialize)]
76pub struct PaymentResult {
77 pub index: u16,
78 pub completed_payment: Option<OutgoingLightningPayment>,
79}
80
81impl_db_record!(
82 key = PaymentResultKey,
83 value = PaymentResult,
84 db_prefix = DbKeyPrefix::PaymentResult,
85);
86
87impl_db_lookup!(key = PaymentResultKey, query_prefix = PaymentResultPrefix);
88
89#[derive(Debug, Encodable, Decodable, Serialize)]
90pub struct LightningGatewayKey(pub PublicKey);
91
92#[derive(Debug, Encodable, Decodable)]
93pub struct LightningGatewayKeyPrefix;
94
95impl_db_record!(
96 key = LightningGatewayKey,
97 value = LightningGatewayRegistration,
98 db_prefix = DbKeyPrefix::LightningGateway,
99);
100impl_db_lookup!(
101 key = LightningGatewayKey,
102 query_prefix = LightningGatewayKeyPrefix
103);
104
105#[derive(Debug, Encodable, Decodable)]
108pub struct RecurringPaymentCodeKey {
109 pub derivation_idx: u64,
110}
111
112#[derive(Debug, Encodable, Decodable)]
113pub struct RecurringPaymentCodeKeyPrefix;
114
115impl_db_record!(
116 key = RecurringPaymentCodeKey,
117 value = RecurringPaymentCodeEntry,
118 db_prefix = DbKeyPrefix::RecurringPaymentKey,
119);
120
121impl_db_lookup!(
122 key = RecurringPaymentCodeKey,
123 query_prefix = RecurringPaymentCodeKeyPrefix
124);
125
126pub(crate) fn get_v1_migrated_state(
129 operation_id: OperationId,
130 cursor: &mut Cursor<&[u8]>,
131) -> Result<Option<(Vec<u8>, OperationId)>, DbMigrationError> {
132 #[derive(Debug, Clone, Decodable)]
133 pub struct LightningReceiveConfirmedInvoiceV0 {
134 invoice: Bolt11Invoice,
135 receiving_key: Keypair,
136 }
137
138 let decoders = ModuleDecoderRegistry::default();
139 let ln_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
140
141 if ln_sm_variant != 2 {
143 return Ok(None);
144 }
145
146 let _ln_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
147 let _operation_id = OperationId::consensus_decode_partial(cursor, &decoders)?;
148 let receive_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
149
150 let new = match receive_sm_variant {
151 0 => {
153 let _receive_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
154
155 let v0 = LightningReceiveSubmittedOfferV0::consensus_decode_partial(cursor, &decoders)?;
156
157 let new_offer = LightningReceiveSubmittedOffer {
158 offer_txid: v0.offer_txid,
159 invoice: v0.invoice,
160 receiving_key: ReceivingKey::Personal(v0.payment_keypair),
161 };
162 let new_recv = LightningReceiveStateMachine {
163 operation_id,
164 state: LightningReceiveStates::SubmittedOffer(new_offer),
165 };
166 LightningClientStateMachines::Receive(new_recv)
167 }
168 2 => {
170 let _receive_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
171 let confirmed_old =
172 LightningReceiveConfirmedInvoiceV0::consensus_decode_partial(cursor, &decoders)?;
173 let confirmed_new = LightningReceiveConfirmedInvoice {
174 invoice: confirmed_old.invoice,
175 receiving_key: ReceivingKey::Personal(confirmed_old.receiving_key),
176 };
177 LightningClientStateMachines::Receive(LightningReceiveStateMachine {
178 operation_id,
179 state: LightningReceiveStates::ConfirmedInvoice(confirmed_new),
180 })
181 }
182 _ => return Ok(None),
183 };
184
185 let bytes = new.consensus_encode_to_vec();
186 Ok(Some((bytes, operation_id)))
187}
188
189pub(crate) fn get_v2_migrated_state(
191 operation_id: OperationId,
192 cursor: &mut Cursor<&[u8]>,
193) -> Result<Option<(Vec<u8>, OperationId)>, DbMigrationError> {
194 let decoders = ModuleDecoderRegistry::default();
195 let ln_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
196
197 if ln_sm_variant != 2 {
199 return Ok(None);
200 }
201
202 let _ln_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
203 let _operation_id = OperationId::consensus_decode_partial(cursor, &decoders)?;
204 let receive_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
205 if receive_sm_variant != 5 {
206 return Ok(None);
207 }
208
209 let _receive_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
210 let old = LightningReceiveSubmittedOffer::consensus_decode_partial(cursor, &decoders)?;
211
212 let new_recv = LightningClientStateMachines::Receive(LightningReceiveStateMachine {
213 operation_id,
214 state: LightningReceiveStates::SubmittedOffer(old),
215 });
216
217 let bytes = new_recv.consensus_encode_to_vec();
218 Ok(Some((bytes, operation_id)))
219}
220
221pub(crate) fn get_v3_migrated_state(
224 operation_id: OperationId,
225 cursor: &mut Cursor<&[u8]>,
226) -> Result<Option<(Vec<u8>, OperationId)>, DbMigrationError> {
227 let decoders = ModuleDecoderRegistry::default();
228 let ln_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
229
230 if ln_sm_variant != 1 {
232 return Ok(None);
233 }
234
235 let _ln_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
236 let common = LightningPayCommon::consensus_decode_partial(cursor, &decoders)?;
237 let pay_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
238
239 let _pay_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
240
241 match pay_sm_variant {
243 2 => {
245 #[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
246 pub struct LightningPayFundedV0 {
247 pub payload: PayInvoicePayload,
248 pub gateway: LightningGateway,
249 pub timelock: u32,
250 }
251
252 let v0 = LightningPayFundedV0::consensus_decode_partial(cursor, &decoders)?;
253 let v1 = LightningPayFunded {
254 payload: v0.payload,
255 gateway: v0.gateway,
256 timelock: v0.timelock,
257 funding_time: fedimint_core::time::now(),
258 };
259
260 let new_pay = LightningPayStateMachine {
261 common,
262 state: LightningPayStates::Funded(v1),
263 };
264 let new_sm = LightningClientStateMachines::LightningPay(new_pay);
265 let bytes = new_sm.consensus_encode_to_vec();
266 Ok(Some((bytes, operation_id)))
267 }
268 5 => {
270 #[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
271 pub struct LightningPayRefundV0 {
272 txid: TransactionId,
273 out_points: Vec<OutPoint>,
274 }
275
276 let v0 = LightningPayRefundV0::consensus_decode_partial(cursor, &decoders)?;
277 let v1 = LightningPayRefund {
278 txid: v0.txid,
279 out_points: v0.out_points,
280 error_reason: "unknown error (database migration)".to_string(),
281 };
282 let new_pay = LightningPayStateMachine {
283 common,
284 state: LightningPayStates::Refund(v1),
285 };
286 let new_sm = LightningClientStateMachines::LightningPay(new_pay);
287 let bytes = new_sm.consensus_encode_to_vec();
288 Ok(Some((bytes, operation_id)))
289 }
290 _ => Ok(None),
291 }
292}
293
294#[cfg(test)]
295mod tests;