1use std::collections::{BTreeMap, BTreeSet};
2use std::str::FromStr;
3use std::time::SystemTime;
4
5use bitcoin::hashes::{Hash, sha256};
6use fedimint_core::config::FederationId;
7use fedimint_core::core::OperationId;
8use fedimint_core::db::{
9 Database, DatabaseTransaction, DatabaseVersion, DbMigrationError, GeneralDbMigrationFn,
10 GeneralDbMigrationFnContext, IDatabaseTransactionOpsCore, IDatabaseTransactionOpsCoreTyped,
11};
12use fedimint_core::encoding::btc::NetworkLegacyEncodingWrapper;
13use fedimint_core::encoding::{
14 Decodable, DecodeError, Encodable, decode_legacy_option_system_time_from_finite_reader,
15 encode_legacy_option_system_time, with_decoding_context,
16};
17use fedimint_core::invite_code::InviteCode;
18use fedimint_core::module::registry::ModuleDecoderRegistry;
19use fedimint_core::time::duration_since_epoch;
20use fedimint_core::{Amount, impl_db_lookup, impl_db_record, push_db_pair_items, secp256k1};
21use fedimint_gateway_common::envs::FM_GATEWAY_IROH_SECRET_KEY_OVERRIDE_ENV;
22use fedimint_gateway_common::{ConnectorType, FederationConfig, RegisteredProtocol};
23use fedimint_ln_common::serde_routing_fees;
24use fedimint_lnv2_common::contracts::{IncomingContract, PaymentImage};
25use fedimint_lnv2_common::gateway_api::{MAX_INVOICE_EXPIRY_SECS, PaymentFee};
26use futures::{FutureExt, StreamExt};
27use lightning_invoice::RoutingFees;
28use rand::Rng;
29use rand::rngs::OsRng;
30use secp256k1::{Keypair, Secp256k1};
31use serde::{Deserialize, Serialize};
32use strum::IntoEnumIterator;
33use strum_macros::EnumIter;
34
35pub trait GatewayDbExt {
36 fn get_client_database(&self, federation_id: &FederationId) -> Database;
37}
38
39impl GatewayDbExt for Database {
40 fn get_client_database(&self, federation_id: &FederationId) -> Database {
41 let mut prefix = vec![DbKeyPrefix::ClientDatabase as u8];
42 prefix.append(&mut federation_id.consensus_encode_to_vec());
43 self.with_prefix(prefix)
44 }
45}
46
47#[allow(async_fn_in_trait)]
48pub trait GatewayDbtxNcExt {
49 async fn save_federation_config(&mut self, config: &FederationConfig);
50 async fn load_federation_configs_v0(&mut self) -> BTreeMap<FederationId, FederationConfigV0>;
51 async fn load_federation_configs(&mut self) -> BTreeMap<FederationId, FederationConfig>;
52 async fn load_federation_config(
53 &mut self,
54 federation_id: FederationId,
55 ) -> Option<FederationConfig>;
56 async fn remove_federation_config(&mut self, federation_id: FederationId);
57
58 async fn load_or_create_gateway_keypair(&mut self, protocol: RegisteredProtocol) -> Keypair;
62
63 async fn save_new_preimage_authentication(
64 &mut self,
65 payment_hash: sha256::Hash,
66 preimage_auth: sha256::Hash,
67 );
68
69 async fn load_preimage_authentication(
70 &mut self,
71 payment_hash: sha256::Hash,
72 ) -> Option<sha256::Hash>;
73
74 async fn save_registered_incoming_contract(
77 &mut self,
78 federation_id: FederationId,
79 incoming_amount: Amount,
80 invoice_expires_at_secs: u64,
81 contract: IncomingContract,
82 ) -> Option<RegisteredIncomingContract>;
83
84 async fn load_registered_incoming_contract(
85 &mut self,
86 payment_image: PaymentImage,
87 ) -> Option<RegisteredIncomingContract>;
88
89 async fn delete_registered_incoming_contract(
92 &mut self,
93 payment_image: PaymentImage,
94 ) -> Option<RegisteredIncomingContract>;
95
96 async fn prune_registered_incoming_contracts(&mut self, expired_before_secs: u64) -> usize;
99
100 async fn claim_outgoing_payment_image(
106 &mut self,
107 payment_image: PaymentImage,
108 operation_id: OperationId,
109 ) -> OperationId;
110
111 async fn dump_database(
114 &mut self,
115 prefix_names: Vec<String>,
116 ) -> BTreeMap<String, Box<dyn erased_serde::Serialize + Send>>;
117
118 async fn load_or_create_iroh_key(&mut self) -> iroh::SecretKey;
121
122 async fn load_backup_records(&mut self) -> BTreeMap<FederationId, Option<SystemTime>>;
124
125 async fn load_backup_record(
127 &mut self,
128 federation_id: FederationId,
129 ) -> Option<Option<SystemTime>>;
130
131 async fn save_federation_backup_record(
133 &mut self,
134 federation_id: FederationId,
135 backup_time: Option<SystemTime>,
136 );
137}
138
139impl<Cap: Send> GatewayDbtxNcExt for DatabaseTransaction<'_, Cap> {
140 async fn save_federation_config(&mut self, config: &FederationConfig) {
141 let id = config.invite_code.federation_id();
142 self.insert_entry(&FederationConfigKey { id }, config).await;
143 }
144
145 async fn load_federation_configs_v0(&mut self) -> BTreeMap<FederationId, FederationConfigV0> {
146 self.find_by_prefix(&FederationConfigKeyPrefixV0)
147 .await
148 .map(|(key, config): (FederationConfigKeyV0, FederationConfigV0)| (key.id, config))
149 .collect::<BTreeMap<FederationId, FederationConfigV0>>()
150 .await
151 }
152
153 async fn load_federation_configs(&mut self) -> BTreeMap<FederationId, FederationConfig> {
154 self.find_by_prefix(&FederationConfigKeyPrefix)
155 .await
156 .map(|(key, config): (FederationConfigKey, FederationConfig)| (key.id, config))
157 .collect::<BTreeMap<FederationId, FederationConfig>>()
158 .await
159 }
160
161 async fn load_federation_config(
162 &mut self,
163 federation_id: FederationId,
164 ) -> Option<FederationConfig> {
165 self.get_value(&FederationConfigKey { id: federation_id })
166 .await
167 }
168
169 async fn remove_federation_config(&mut self, federation_id: FederationId) {
170 self.remove_entry(&FederationConfigKey { id: federation_id })
171 .await;
172 }
173
174 async fn load_or_create_gateway_keypair(&mut self, protocol: RegisteredProtocol) -> Keypair {
175 if let Some(key_pair) = self
176 .get_value(&GatewayPublicKey {
177 protocol: protocol.clone(),
178 })
179 .await
180 {
181 key_pair
182 } else {
183 let context = Secp256k1::new();
184 let (secret_key, _public_key) = context.generate_keypair(&mut OsRng);
185 let key_pair = Keypair::from_secret_key(&context, &secret_key);
186
187 self.insert_new_entry(&GatewayPublicKey { protocol }, &key_pair)
188 .await;
189 key_pair
190 }
191 }
192
193 async fn save_new_preimage_authentication(
194 &mut self,
195 payment_hash: sha256::Hash,
196 preimage_auth: sha256::Hash,
197 ) {
198 self.insert_new_entry(&PreimageAuthentication { payment_hash }, &preimage_auth)
199 .await;
200 }
201
202 async fn load_preimage_authentication(
203 &mut self,
204 payment_hash: sha256::Hash,
205 ) -> Option<sha256::Hash> {
206 self.get_value(&PreimageAuthentication { payment_hash })
207 .await
208 }
209
210 async fn save_registered_incoming_contract(
211 &mut self,
212 federation_id: FederationId,
213 incoming_amount: Amount,
214 invoice_expires_at_secs: u64,
215 contract: IncomingContract,
216 ) -> Option<RegisteredIncomingContract> {
217 self.insert_entry(
218 &RegisteredIncomingContractKey(contract.commitment.payment_image.clone()),
219 &RegisteredIncomingContract {
220 federation_id,
221 incoming_amount_msats: incoming_amount.msats,
222 invoice_expires_at_secs,
223 contract,
224 },
225 )
226 .await
227 }
228
229 async fn load_registered_incoming_contract(
230 &mut self,
231 payment_image: PaymentImage,
232 ) -> Option<RegisteredIncomingContract> {
233 self.get_value(&RegisteredIncomingContractKey(payment_image))
234 .await
235 }
236
237 async fn delete_registered_incoming_contract(
238 &mut self,
239 payment_image: PaymentImage,
240 ) -> Option<RegisteredIncomingContract> {
241 self.remove_entry(&RegisteredIncomingContractKey(payment_image))
242 .await
243 }
244
245 async fn prune_registered_incoming_contracts(&mut self, expired_before_secs: u64) -> usize {
246 let expired_payment_images = self
247 .find_by_prefix(&RegisteredIncomingContractKeyPrefix)
248 .await
249 .filter_map(
250 |(key, record): (RegisteredIncomingContractKey, RegisteredIncomingContract)| async move {
251 (record.invoice_expires_at_secs < expired_before_secs).then_some(key.0)
252 },
253 )
254 .collect::<Vec<_>>()
255 .await;
256
257 let num_expired = expired_payment_images.len();
258
259 for payment_image in expired_payment_images {
260 self.remove_entry(&RegisteredIncomingContractKey(payment_image))
261 .await;
262 }
263
264 num_expired
265 }
266
267 async fn claim_outgoing_payment_image(
268 &mut self,
269 payment_image: PaymentImage,
270 operation_id: OperationId,
271 ) -> OperationId {
272 if let Some(existing) = self
273 .get_value(&ClaimedOutgoingPaymentImageKey(payment_image.clone()))
274 .await
275 {
276 existing
277 } else {
278 self.insert_entry(
279 &ClaimedOutgoingPaymentImageKey(payment_image),
280 &operation_id,
281 )
282 .await;
283 operation_id
284 }
285 }
286
287 async fn dump_database(
288 &mut self,
289 prefix_names: Vec<String>,
290 ) -> BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> {
291 let mut gateway_items: BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> =
292 BTreeMap::new();
293 let filtered_prefixes = DbKeyPrefix::iter().filter(|f| {
294 prefix_names.is_empty() || prefix_names.contains(&f.to_string().to_lowercase())
295 });
296
297 for table in filtered_prefixes {
298 match table {
299 DbKeyPrefix::FederationConfig => {
300 push_db_pair_items!(
301 self,
302 FederationConfigKeyPrefix,
303 FederationConfigKey,
304 FederationConfig,
305 gateway_items,
306 "Federation Config"
307 );
308 }
309 DbKeyPrefix::GatewayPublicKey => {
310 push_db_pair_items!(
311 self,
312 GatewayPublicKeyPrefix,
313 GatewayPublicKey,
314 Keypair,
315 gateway_items,
316 "Gateway Public Keys"
317 );
318 }
319 _ => {}
320 }
321 }
322
323 gateway_items
324 }
325
326 async fn load_or_create_iroh_key(&mut self) -> iroh::SecretKey {
327 if let Some(iroh_sk) = self.get_value(&IrohKey).await {
328 iroh_sk
329 } else {
330 let iroh_sk = if let Ok(var) = std::env::var(FM_GATEWAY_IROH_SECRET_KEY_OVERRIDE_ENV) {
331 iroh::SecretKey::from_str(&var).expect("Invalid overridden iroh secret key")
332 } else {
333 iroh::SecretKey::generate(&mut OsRng)
334 };
335
336 self.insert_new_entry(&IrohKey, &iroh_sk).await;
337 iroh_sk
338 }
339 }
340
341 async fn load_backup_records(&mut self) -> BTreeMap<FederationId, Option<SystemTime>> {
342 self.find_by_prefix(&FederationBackupPrefix)
343 .await
344 .map(|(key, time): (FederationBackupKey, FederationBackupTime)| {
345 (key.federation_id, time.0)
346 })
347 .collect::<BTreeMap<FederationId, Option<SystemTime>>>()
348 .await
349 }
350
351 async fn load_backup_record(
352 &mut self,
353 federation_id: FederationId,
354 ) -> Option<Option<SystemTime>> {
355 self.get_value(&FederationBackupKey { federation_id })
356 .await
357 .map(|time| time.0)
358 }
359
360 async fn save_federation_backup_record(
361 &mut self,
362 federation_id: FederationId,
363 backup_time: Option<SystemTime>,
364 ) {
365 self.insert_entry(
366 &FederationBackupKey { federation_id },
367 &FederationBackupTime(backup_time),
368 )
369 .await;
370 }
371}
372
373#[repr(u8)]
374#[derive(Clone, EnumIter, Debug)]
375enum DbKeyPrefix {
376 FederationConfig = 0x04,
377 GatewayPublicKey = 0x06,
378 GatewayConfiguration = 0x07,
379 PreimageAuthentication = 0x08,
380 RegisteredIncomingContract = 0x09,
381 ClientDatabase = 0x10,
382 Iroh = 0x11,
383 FederationBackup = 0x12,
384 ClaimedOutgoingPaymentImage = 0x13,
385}
386
387impl std::fmt::Display for DbKeyPrefix {
388 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
389 write!(f, "{self:?}")
390 }
391}
392
393#[derive(Debug, Encodable, Decodable)]
394struct FederationConfigKeyPrefixV0;
395
396#[derive(Debug, Encodable, Decodable)]
397struct FederationConfigKeyPrefixV1;
398
399#[derive(Debug, Encodable, Decodable)]
400struct FederationConfigKeyPrefix;
401
402#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
403struct FederationConfigKeyV0 {
404 id: FederationId,
405}
406
407#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
408pub struct FederationConfigV0 {
409 pub invite_code: InviteCode,
410 pub federation_index: u64,
411 pub timelock_delta: u64,
412 #[serde(with = "serde_routing_fees")]
413 pub fees: RoutingFees,
414}
415
416#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
417struct FederationConfigKeyV1 {
418 id: FederationId,
419}
420
421#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
422pub struct FederationConfigV1 {
423 pub invite_code: InviteCode,
424 #[serde(alias = "mint_channel_id")]
427 pub federation_index: u64,
428 pub timelock_delta: u64,
429 #[serde(with = "serde_routing_fees")]
430 pub fees: RoutingFees,
431}
432
433#[derive(Debug, Encodable, Decodable)]
434struct FederationConfigKeyPrefixV2;
435
436#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
437struct FederationConfigKeyV2 {
438 id: FederationId,
439}
440
441#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
443pub struct FederationConfigV2 {
444 pub invite_code: InviteCode,
445 #[serde(alias = "mint_channel_id")]
446 pub federation_index: u64,
447 pub lightning_fee: PaymentFee,
448 pub transaction_fee: PaymentFee,
449 #[allow(deprecated)] pub _connector: ConnectorType,
451}
452
453#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
454struct FederationConfigKey {
455 id: FederationId,
456}
457
458impl_db_record!(
459 key = FederationConfigKeyV0,
460 value = FederationConfigV0,
461 db_prefix = DbKeyPrefix::FederationConfig,
462);
463
464impl_db_record!(
465 key = FederationConfigKeyV1,
466 value = FederationConfigV1,
467 db_prefix = DbKeyPrefix::FederationConfig,
468);
469
470impl_db_record!(
471 key = FederationConfigKeyV2,
472 value = FederationConfigV2,
473 db_prefix = DbKeyPrefix::FederationConfig,
474);
475
476impl_db_record!(
477 key = FederationConfigKey,
478 value = FederationConfig,
479 db_prefix = DbKeyPrefix::FederationConfig,
480);
481
482impl_db_lookup!(
483 key = FederationConfigKeyV0,
484 query_prefix = FederationConfigKeyPrefixV0
485);
486impl_db_lookup!(
487 key = FederationConfigKeyV1,
488 query_prefix = FederationConfigKeyPrefixV1
489);
490impl_db_lookup!(
491 key = FederationConfigKeyV2,
492 query_prefix = FederationConfigKeyPrefixV2
493);
494impl_db_lookup!(
495 key = FederationConfigKey,
496 query_prefix = FederationConfigKeyPrefix
497);
498
499#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
500struct GatewayPublicKeyV0;
501
502#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
503struct GatewayPublicKey {
504 protocol: RegisteredProtocol,
505}
506
507#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
508struct GatewayPublicKeyPrefix;
509
510impl_db_record!(
511 key = GatewayPublicKeyV0,
512 value = Keypair,
513 db_prefix = DbKeyPrefix::GatewayPublicKey,
514);
515
516impl_db_record!(
517 key = GatewayPublicKey,
518 value = Keypair,
519 db_prefix = DbKeyPrefix::GatewayPublicKey,
520);
521
522impl_db_lookup!(
523 key = GatewayPublicKey,
524 query_prefix = GatewayPublicKeyPrefix
525);
526
527#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
528struct GatewayConfigurationKeyV0;
529
530#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
531struct GatewayConfigurationV0 {
532 password: String,
533 num_route_hints: u32,
534 #[serde(with = "serde_routing_fees")]
535 routing_fees: RoutingFees,
536 network: NetworkLegacyEncodingWrapper,
537}
538
539#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
540pub struct GatewayConfigurationKeyV1;
541
542#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
543pub struct GatewayConfigurationV1 {
544 pub hashed_password: sha256::Hash,
545 pub num_route_hints: u32,
546 #[serde(with = "serde_routing_fees")]
547 pub routing_fees: RoutingFees,
548 pub network: NetworkLegacyEncodingWrapper,
549 pub password_salt: [u8; 16],
550}
551
552#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
553pub struct GatewayConfigurationKeyV2;
554
555#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
556pub struct GatewayConfigurationV2 {
557 pub num_route_hints: u32,
558 #[serde(with = "serde_routing_fees")]
559 pub routing_fees: RoutingFees,
560 pub network: NetworkLegacyEncodingWrapper,
561}
562
563impl_db_record!(
564 key = GatewayConfigurationKeyV0,
565 value = GatewayConfigurationV0,
566 db_prefix = DbKeyPrefix::GatewayConfiguration,
567);
568
569impl_db_record!(
570 key = GatewayConfigurationKeyV1,
571 value = GatewayConfigurationV1,
572 db_prefix = DbKeyPrefix::GatewayConfiguration,
573);
574
575impl_db_record!(
576 key = GatewayConfigurationKeyV2,
577 value = GatewayConfigurationV2,
578 db_prefix = DbKeyPrefix::GatewayConfiguration,
579);
580
581#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
582struct PreimageAuthentication {
583 payment_hash: sha256::Hash,
584}
585
586impl_db_record!(
587 key = PreimageAuthentication,
588 value = sha256::Hash,
589 db_prefix = DbKeyPrefix::PreimageAuthentication
590);
591
592#[allow(dead_code)] #[derive(Debug, Encodable, Decodable)]
594struct PreimageAuthenticationPrefix;
595
596impl_db_lookup!(
597 key = PreimageAuthentication,
598 query_prefix = PreimageAuthenticationPrefix
599);
600
601#[derive(Debug, Encodable, Decodable)]
602struct IrohKey;
603
604impl_db_record!(
605 key = IrohKey,
606 value = iroh::SecretKey,
607 db_prefix = DbKeyPrefix::Iroh
608);
609
610#[derive(Debug, Encodable, Decodable)]
611pub struct FederationBackupKey {
612 federation_id: FederationId,
613}
614
615#[derive(Debug, Encodable, Decodable)]
616pub struct FederationBackupPrefix;
617
618#[derive(Debug)]
619pub struct FederationBackupTime(Option<SystemTime>);
621
622impl Encodable for FederationBackupTime {
623 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
624 encode_legacy_option_system_time(&self.0, writer)
625 }
626}
627
628impl Decodable for FederationBackupTime {
629 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
630 decoder: &mut D,
631 modules: &ModuleDecoderRegistry,
632 ) -> Result<Self, DecodeError> {
633 Ok(Self(with_decoding_context(
634 decode_legacy_option_system_time_from_finite_reader(decoder, modules),
635 "Decoding tuple block FederationBackupTime field field_0",
636 )?))
637 }
638}
639
640impl_db_record!(
641 key = FederationBackupKey,
642 value = FederationBackupTime,
643 db_prefix = DbKeyPrefix::FederationBackup,
644);
645
646impl_db_lookup!(
647 key = FederationBackupKey,
648 query_prefix = FederationBackupPrefix,
649);
650
651pub fn get_gatewayd_database_migrations() -> BTreeMap<DatabaseVersion, GeneralDbMigrationFn> {
652 let mut migrations: BTreeMap<DatabaseVersion, GeneralDbMigrationFn> = BTreeMap::new();
653 migrations.insert(
654 DatabaseVersion(0),
655 Box::new(|ctx| migrate_to_v1(ctx).boxed()),
656 );
657 migrations.insert(
658 DatabaseVersion(1),
659 Box::new(|ctx| migrate_to_v2(ctx).boxed()),
660 );
661 migrations.insert(
662 DatabaseVersion(2),
663 Box::new(|ctx| migrate_to_v3(ctx).boxed()),
664 );
665 migrations.insert(
666 DatabaseVersion(3),
667 Box::new(|ctx| migrate_to_v4(ctx).boxed()),
668 );
669 migrations.insert(
670 DatabaseVersion(4),
671 Box::new(|ctx| migrate_to_v5(ctx).boxed()),
672 );
673 migrations.insert(
674 DatabaseVersion(5),
675 Box::new(|ctx| migrate_to_v6(ctx).boxed()),
676 );
677 migrations.insert(
678 DatabaseVersion(6),
679 Box::new(|ctx| migrate_to_v7(ctx).boxed()),
680 );
681 migrations.insert(
682 DatabaseVersion(7),
683 Box::new(|ctx| migrate_to_v8(ctx).boxed()),
684 );
685 migrations.insert(
686 DatabaseVersion(8),
687 Box::new(|ctx| migrate_to_v9(ctx).boxed()),
688 );
689 migrations
690}
691
692async fn migrate_to_v1(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
693 fn hash_password(plaintext_password: &str, salt: [u8; 16]) -> sha256::Hash {
696 let mut bytes = Vec::new();
697 bytes.append(&mut plaintext_password.consensus_encode_to_vec());
698 bytes.append(&mut salt.consensus_encode_to_vec());
699 sha256::Hash::hash(&bytes)
700 }
701
702 let mut dbtx = ctx.dbtx();
703
704 if let Some(old_gateway_config) = dbtx.remove_entry(&GatewayConfigurationKeyV0).await {
706 let password_salt: [u8; 16] = rand::thread_rng().r#gen();
707 let hashed_password = hash_password(&old_gateway_config.password, password_salt);
708 let new_gateway_config = GatewayConfigurationV1 {
709 hashed_password,
710 num_route_hints: old_gateway_config.num_route_hints,
711 routing_fees: old_gateway_config.routing_fees,
712 network: old_gateway_config.network,
713 password_salt,
714 };
715 dbtx.insert_entry(&GatewayConfigurationKeyV1, &new_gateway_config)
716 .await;
717 }
718
719 Ok(())
720}
721
722async fn migrate_to_v2(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
723 let mut dbtx = ctx.dbtx();
724
725 for (old_federation_id, _old_federation_config) in dbtx.load_federation_configs_v0().await {
727 if let Some(old_federation_config) = dbtx
728 .remove_entry(&FederationConfigKeyV0 {
729 id: old_federation_id,
730 })
731 .await
732 {
733 let new_federation_config = FederationConfigV1 {
734 invite_code: old_federation_config.invite_code,
735 federation_index: old_federation_config.federation_index,
736 timelock_delta: old_federation_config.timelock_delta,
737 fees: old_federation_config.fees,
738 };
739 let new_federation_key = FederationConfigKeyV1 {
740 id: old_federation_id,
741 };
742 dbtx.insert_entry(&new_federation_key, &new_federation_config)
743 .await;
744 }
745 }
746 Ok(())
747}
748
749async fn migrate_to_v3(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
750 let mut dbtx = ctx.dbtx();
751
752 if let Some(old_gateway_config) = dbtx.remove_entry(&GatewayConfigurationKeyV1).await {
754 let new_gateway_config = GatewayConfigurationV2 {
755 num_route_hints: old_gateway_config.num_route_hints,
756 routing_fees: old_gateway_config.routing_fees,
757 network: old_gateway_config.network,
758 };
759 dbtx.insert_entry(&GatewayConfigurationKeyV2, &new_gateway_config)
760 .await;
761 }
762
763 Ok(())
764}
765
766async fn migrate_to_v4(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
767 let mut dbtx = ctx.dbtx();
768
769 dbtx.remove_entry(&GatewayConfigurationKeyV2).await;
770
771 let configs = dbtx
772 .find_by_prefix(&FederationConfigKeyPrefixV1)
773 .await
774 .collect::<Vec<_>>()
775 .await;
776 for (fed_id, _old_config) in configs {
777 if let Some(old_federation_config) = dbtx.remove_entry(&fed_id).await {
778 let new_fed_config = FederationConfigV2 {
779 invite_code: old_federation_config.invite_code,
780 federation_index: old_federation_config.federation_index,
781 lightning_fee: old_federation_config.fees.into(),
782 transaction_fee: PaymentFee::TRANSACTION_FEE_DEFAULT,
783 _connector: ConnectorType::Tcp,
785 };
786 let new_key = FederationConfigKeyV2 { id: fed_id.id };
787 dbtx.insert_new_entry(&new_key, &new_fed_config).await;
788 }
789 }
790 Ok(())
791}
792
793async fn migrate_to_v5(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
798 let mut dbtx = ctx.dbtx();
799 migrate_federation_configs(&mut dbtx).await
800}
801
802async fn migrate_federation_configs(
803 dbtx: &mut DatabaseTransaction<'_>,
804) -> Result<(), DbMigrationError> {
805 let problem_entries = dbtx
818 .raw_find_by_prefix(&[0x04])
819 .await?
820 .collect::<BTreeMap<_, _>>()
821 .await;
822 for (mut problem_key, value) in problem_entries {
823 if problem_key.len() == 33
827 && let Ok(federation_id) = FederationId::consensus_decode_whole(
828 &problem_key[1..33],
829 &ModuleDecoderRegistry::default(),
830 )
831 && let Ok(federation_config) = FederationConfigV2::consensus_decode_whole(
832 &value,
833 &ModuleDecoderRegistry::default(),
834 )
835 && federation_id == federation_config.invite_code.federation_id()
836 {
837 continue;
838 }
839
840 dbtx.raw_remove_entry(&problem_key).await?;
841 let mut new_key = vec![DbKeyPrefix::ClientDatabase as u8];
842 new_key.append(&mut problem_key);
843 dbtx.raw_insert_bytes(&new_key, &value).await?;
844 }
845
846 let fed_ids = dbtx
849 .find_by_prefix(&FederationConfigKeyPrefixV2)
850 .await
851 .collect::<BTreeMap<_, _>>()
852 .await;
853 for fed_id in fed_ids.keys() {
854 let federation_id_bytes = fed_id.id.consensus_encode_to_vec();
855 let isolated_entries = dbtx
856 .raw_find_by_prefix(&federation_id_bytes)
857 .await?
858 .collect::<BTreeMap<_, _>>()
859 .await;
860 for (mut key, value) in isolated_entries {
861 dbtx.raw_remove_entry(&key).await?;
862 let mut new_key = vec![DbKeyPrefix::ClientDatabase as u8];
863 new_key.append(&mut key);
864 dbtx.raw_insert_bytes(&new_key, &value).await?;
865 }
866 }
867
868 Ok(())
869}
870
871async fn migrate_to_v6(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
872 let mut dbtx = ctx.dbtx();
873
874 let configs = dbtx
876 .find_by_prefix(&FederationConfigKeyPrefixV2)
877 .await
878 .collect::<Vec<_>>()
879 .await;
880 for (fed_id, _) in configs {
881 dbtx.insert_new_entry(
882 &FederationBackupKey {
883 federation_id: fed_id.id,
884 },
885 &FederationBackupTime(None),
886 )
887 .await;
888 }
889 Ok(())
890}
891
892async fn migrate_to_v7(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
893 let mut dbtx = ctx.dbtx();
894
895 let gateway_keypair = dbtx.remove_entry(&GatewayPublicKeyV0).await;
896 if let Some(gateway_keypair) = gateway_keypair {
897 dbtx.insert_new_entry(
898 &GatewayPublicKey {
899 protocol: RegisteredProtocol::Http,
900 },
901 &gateway_keypair,
902 )
903 .await;
904 }
905
906 Ok(())
907}
908
909async fn migrate_to_v9(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
913 let mut dbtx = ctx.dbtx();
914 migrate_federation_configs_payment_policies(&mut dbtx).await
915}
916
917async fn migrate_federation_configs_payment_policies(
918 dbtx: &mut DatabaseTransaction<'_>,
919) -> Result<(), DbMigrationError> {
920 let configs = dbtx
921 .find_by_prefix(&FederationConfigKeyPrefixV2)
922 .await
923 .collect::<Vec<_>>()
924 .await;
925
926 for (key, config) in configs {
927 dbtx.remove_entry(&key).await;
928 dbtx.insert_new_entry(
929 &FederationConfigKey { id: key.id },
930 &FederationConfig {
931 invite_code: config.invite_code,
932 federation_index: config.federation_index,
933 lightning_fee: config.lightning_fee,
934 transaction_fee: config.transaction_fee,
935 payment_policies: BTreeSet::new(),
936 _connector: config._connector,
937 },
938 )
939 .await;
940 }
941
942 Ok(())
943}
944
945async fn migrate_to_v8(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), DbMigrationError> {
948 let mut dbtx = ctx.dbtx();
949 migrate_registered_incoming_contracts(&mut dbtx).await
950}
951
952async fn migrate_registered_incoming_contracts(
953 dbtx: &mut DatabaseTransaction<'_>,
954) -> Result<(), DbMigrationError> {
955 let backfilled_expiry = duration_since_epoch()
959 .as_secs()
960 .saturating_add(u64::from(MAX_INVOICE_EXPIRY_SECS));
961
962 let contracts = dbtx
963 .find_by_prefix(&RegisteredIncomingContractKeyPrefixV0)
964 .await
965 .collect::<Vec<_>>()
966 .await;
967
968 for (key, record) in contracts {
969 dbtx.remove_entry(&key).await;
970 dbtx.insert_new_entry(
971 &RegisteredIncomingContractKey(key.0),
972 &RegisteredIncomingContract {
973 federation_id: record.federation_id,
974 incoming_amount_msats: record.incoming_amount_msats,
975 invoice_expires_at_secs: backfilled_expiry,
976 contract: record.contract,
977 },
978 )
979 .await;
980 }
981
982 Ok(())
983}
984
985#[derive(Debug, Encodable, Decodable)]
986struct RegisteredIncomingContractKey(pub PaymentImage);
987
988#[derive(Debug, Encodable, Decodable)]
989struct RegisteredIncomingContractKeyPrefix;
990
991#[derive(Debug, Encodable, Decodable)]
992pub struct RegisteredIncomingContract {
993 pub federation_id: FederationId,
994 pub incoming_amount_msats: u64,
996 pub invoice_expires_at_secs: u64,
1000 pub contract: IncomingContract,
1001}
1002
1003impl_db_record!(
1004 key = RegisteredIncomingContractKey,
1005 value = RegisteredIncomingContract,
1006 db_prefix = DbKeyPrefix::RegisteredIncomingContract,
1007);
1008
1009impl_db_lookup!(
1010 key = RegisteredIncomingContractKey,
1011 query_prefix = RegisteredIncomingContractKeyPrefix
1012);
1013
1014#[derive(Debug, Encodable, Decodable)]
1015struct RegisteredIncomingContractKeyV0(pub PaymentImage);
1016
1017#[derive(Debug, Encodable, Decodable)]
1018struct RegisteredIncomingContractKeyPrefixV0;
1019
1020#[derive(Debug, Encodable, Decodable)]
1021pub struct RegisteredIncomingContractV0 {
1022 pub federation_id: FederationId,
1023 pub incoming_amount_msats: u64,
1024 pub contract: IncomingContract,
1025}
1026
1027impl_db_record!(
1028 key = RegisteredIncomingContractKeyV0,
1029 value = RegisteredIncomingContractV0,
1030 db_prefix = DbKeyPrefix::RegisteredIncomingContract,
1031);
1032
1033impl_db_lookup!(
1034 key = RegisteredIncomingContractKeyV0,
1035 query_prefix = RegisteredIncomingContractKeyPrefixV0
1036);
1037
1038#[derive(Debug, Encodable, Decodable)]
1044struct ClaimedOutgoingPaymentImageKey(pub PaymentImage);
1045
1046impl_db_record!(
1047 key = ClaimedOutgoingPaymentImageKey,
1048 value = OperationId,
1049 db_prefix = DbKeyPrefix::ClaimedOutgoingPaymentImage,
1050);
1051
1052#[cfg(test)]
1053mod migration_tests;