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