1use std::collections::BTreeMap;
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, 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::{Amount, impl_db_lookup, impl_db_record, push_db_pair_items, secp256k1};
20use fedimint_gateway_common::envs::FM_GATEWAY_IROH_SECRET_KEY_OVERRIDE_ENV;
21use fedimint_gateway_common::{ConnectorType, FederationConfig, RegisteredProtocol};
22use fedimint_ln_common::serde_routing_fees;
23use fedimint_lnv2_common::contracts::{IncomingContract, PaymentImage};
24use fedimint_lnv2_common::gateway_api::PaymentFee;
25use futures::{FutureExt, StreamExt};
26use lightning_invoice::RoutingFees;
27use rand::Rng;
28use rand::rngs::OsRng;
29use secp256k1::{Keypair, Secp256k1};
30use serde::{Deserialize, Serialize};
31use strum::IntoEnumIterator;
32use strum_macros::EnumIter;
33
34pub trait GatewayDbExt {
35 fn get_client_database(&self, federation_id: &FederationId) -> Database;
36}
37
38impl GatewayDbExt for Database {
39 fn get_client_database(&self, federation_id: &FederationId) -> Database {
40 let mut prefix = vec![DbKeyPrefix::ClientDatabase as u8];
41 prefix.append(&mut federation_id.consensus_encode_to_vec());
42 self.with_prefix(prefix)
43 }
44}
45
46#[allow(async_fn_in_trait)]
47pub trait GatewayDbtxNcExt {
48 async fn save_federation_config(&mut self, config: &FederationConfig);
49 async fn load_federation_configs_v0(&mut self) -> BTreeMap<FederationId, FederationConfigV0>;
50 async fn load_federation_configs(&mut self) -> BTreeMap<FederationId, FederationConfig>;
51 async fn load_federation_config(
52 &mut self,
53 federation_id: FederationId,
54 ) -> Option<FederationConfig>;
55 async fn remove_federation_config(&mut self, federation_id: FederationId);
56
57 async fn load_or_create_gateway_keypair(&mut self, protocol: RegisteredProtocol) -> Keypair;
61
62 async fn save_new_preimage_authentication(
63 &mut self,
64 payment_hash: sha256::Hash,
65 preimage_auth: sha256::Hash,
66 );
67
68 async fn load_preimage_authentication(
69 &mut self,
70 payment_hash: sha256::Hash,
71 ) -> Option<sha256::Hash>;
72
73 async fn save_registered_incoming_contract(
76 &mut self,
77 federation_id: FederationId,
78 incoming_amount: Amount,
79 contract: IncomingContract,
80 ) -> Option<RegisteredIncomingContract>;
81
82 async fn load_registered_incoming_contract(
83 &mut self,
84 payment_image: PaymentImage,
85 ) -> Option<RegisteredIncomingContract>;
86
87 async fn claim_outgoing_payment_image(
93 &mut self,
94 payment_image: PaymentImage,
95 operation_id: OperationId,
96 ) -> OperationId;
97
98 async fn dump_database(
101 &mut self,
102 prefix_names: Vec<String>,
103 ) -> BTreeMap<String, Box<dyn erased_serde::Serialize + Send>>;
104
105 async fn load_or_create_iroh_key(&mut self) -> iroh::SecretKey;
108
109 async fn load_backup_records(&mut self) -> BTreeMap<FederationId, Option<SystemTime>>;
111
112 async fn load_backup_record(
114 &mut self,
115 federation_id: FederationId,
116 ) -> Option<Option<SystemTime>>;
117
118 async fn save_federation_backup_record(
120 &mut self,
121 federation_id: FederationId,
122 backup_time: Option<SystemTime>,
123 );
124}
125
126impl<Cap: Send> GatewayDbtxNcExt for DatabaseTransaction<'_, Cap> {
127 async fn save_federation_config(&mut self, config: &FederationConfig) {
128 let id = config.invite_code.federation_id();
129 self.insert_entry(&FederationConfigKey { id }, config).await;
130 }
131
132 async fn load_federation_configs_v0(&mut self) -> BTreeMap<FederationId, FederationConfigV0> {
133 self.find_by_prefix(&FederationConfigKeyPrefixV0)
134 .await
135 .map(|(key, config): (FederationConfigKeyV0, FederationConfigV0)| (key.id, config))
136 .collect::<BTreeMap<FederationId, FederationConfigV0>>()
137 .await
138 }
139
140 async fn load_federation_configs(&mut self) -> BTreeMap<FederationId, FederationConfig> {
141 self.find_by_prefix(&FederationConfigKeyPrefix)
142 .await
143 .map(|(key, config): (FederationConfigKey, FederationConfig)| (key.id, config))
144 .collect::<BTreeMap<FederationId, FederationConfig>>()
145 .await
146 }
147
148 async fn load_federation_config(
149 &mut self,
150 federation_id: FederationId,
151 ) -> Option<FederationConfig> {
152 self.get_value(&FederationConfigKey { id: federation_id })
153 .await
154 }
155
156 async fn remove_federation_config(&mut self, federation_id: FederationId) {
157 self.remove_entry(&FederationConfigKey { id: federation_id })
158 .await;
159 }
160
161 async fn load_or_create_gateway_keypair(&mut self, protocol: RegisteredProtocol) -> Keypair {
162 if let Some(key_pair) = self
163 .get_value(&GatewayPublicKey {
164 protocol: protocol.clone(),
165 })
166 .await
167 {
168 key_pair
169 } else {
170 let context = Secp256k1::new();
171 let (secret_key, _public_key) = context.generate_keypair(&mut OsRng);
172 let key_pair = Keypair::from_secret_key(&context, &secret_key);
173
174 self.insert_new_entry(&GatewayPublicKey { protocol }, &key_pair)
175 .await;
176 key_pair
177 }
178 }
179
180 async fn save_new_preimage_authentication(
181 &mut self,
182 payment_hash: sha256::Hash,
183 preimage_auth: sha256::Hash,
184 ) {
185 self.insert_new_entry(&PreimageAuthentication { payment_hash }, &preimage_auth)
186 .await;
187 }
188
189 async fn load_preimage_authentication(
190 &mut self,
191 payment_hash: sha256::Hash,
192 ) -> Option<sha256::Hash> {
193 self.get_value(&PreimageAuthentication { payment_hash })
194 .await
195 }
196
197 async fn save_registered_incoming_contract(
198 &mut self,
199 federation_id: FederationId,
200 incoming_amount: Amount,
201 contract: IncomingContract,
202 ) -> Option<RegisteredIncomingContract> {
203 self.insert_entry(
204 &RegisteredIncomingContractKey(contract.commitment.payment_image.clone()),
205 &RegisteredIncomingContract {
206 federation_id,
207 incoming_amount_msats: incoming_amount.msats,
208 contract,
209 },
210 )
211 .await
212 }
213
214 async fn load_registered_incoming_contract(
215 &mut self,
216 payment_image: PaymentImage,
217 ) -> Option<RegisteredIncomingContract> {
218 self.get_value(&RegisteredIncomingContractKey(payment_image))
219 .await
220 }
221
222 async fn claim_outgoing_payment_image(
223 &mut self,
224 payment_image: PaymentImage,
225 operation_id: OperationId,
226 ) -> OperationId {
227 if let Some(existing) = self
228 .get_value(&ClaimedOutgoingPaymentImageKey(payment_image.clone()))
229 .await
230 {
231 existing
232 } else {
233 self.insert_entry(
234 &ClaimedOutgoingPaymentImageKey(payment_image),
235 &operation_id,
236 )
237 .await;
238 operation_id
239 }
240 }
241
242 async fn dump_database(
243 &mut self,
244 prefix_names: Vec<String>,
245 ) -> BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> {
246 let mut gateway_items: BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> =
247 BTreeMap::new();
248 let filtered_prefixes = DbKeyPrefix::iter().filter(|f| {
249 prefix_names.is_empty() || prefix_names.contains(&f.to_string().to_lowercase())
250 });
251
252 for table in filtered_prefixes {
253 match table {
254 DbKeyPrefix::FederationConfig => {
255 push_db_pair_items!(
256 self,
257 FederationConfigKeyPrefix,
258 FederationConfigKey,
259 FederationConfig,
260 gateway_items,
261 "Federation Config"
262 );
263 }
264 DbKeyPrefix::GatewayPublicKey => {
265 push_db_pair_items!(
266 self,
267 GatewayPublicKeyPrefix,
268 GatewayPublicKey,
269 Keypair,
270 gateway_items,
271 "Gateway Public Keys"
272 );
273 }
274 _ => {}
275 }
276 }
277
278 gateway_items
279 }
280
281 async fn load_or_create_iroh_key(&mut self) -> iroh::SecretKey {
282 if let Some(iroh_sk) = self.get_value(&IrohKey).await {
283 iroh_sk
284 } else {
285 let iroh_sk = if let Ok(var) = std::env::var(FM_GATEWAY_IROH_SECRET_KEY_OVERRIDE_ENV) {
286 iroh::SecretKey::from_str(&var).expect("Invalid overridden iroh secret key")
287 } else {
288 iroh::SecretKey::generate(&mut OsRng)
289 };
290
291 self.insert_new_entry(&IrohKey, &iroh_sk).await;
292 iroh_sk
293 }
294 }
295
296 async fn load_backup_records(&mut self) -> BTreeMap<FederationId, Option<SystemTime>> {
297 self.find_by_prefix(&FederationBackupPrefix)
298 .await
299 .map(|(key, time): (FederationBackupKey, FederationBackupTime)| {
300 (key.federation_id, time.0)
301 })
302 .collect::<BTreeMap<FederationId, Option<SystemTime>>>()
303 .await
304 }
305
306 async fn load_backup_record(
307 &mut self,
308 federation_id: FederationId,
309 ) -> Option<Option<SystemTime>> {
310 self.get_value(&FederationBackupKey { federation_id })
311 .await
312 .map(|time| time.0)
313 }
314
315 async fn save_federation_backup_record(
316 &mut self,
317 federation_id: FederationId,
318 backup_time: Option<SystemTime>,
319 ) {
320 self.insert_entry(
321 &FederationBackupKey { federation_id },
322 &FederationBackupTime(backup_time),
323 )
324 .await;
325 }
326}
327
328#[repr(u8)]
329#[derive(Clone, EnumIter, Debug)]
330enum DbKeyPrefix {
331 FederationConfig = 0x04,
332 GatewayPublicKey = 0x06,
333 GatewayConfiguration = 0x07,
334 PreimageAuthentication = 0x08,
335 RegisteredIncomingContract = 0x09,
336 ClientDatabase = 0x10,
337 Iroh = 0x11,
338 FederationBackup = 0x12,
339 ClaimedOutgoingPaymentImage = 0x13,
340}
341
342impl std::fmt::Display for DbKeyPrefix {
343 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
344 write!(f, "{self:?}")
345 }
346}
347
348#[derive(Debug, Encodable, Decodable)]
349struct FederationConfigKeyPrefixV0;
350
351#[derive(Debug, Encodable, Decodable)]
352struct FederationConfigKeyPrefixV1;
353
354#[derive(Debug, Encodable, Decodable)]
355struct FederationConfigKeyPrefix;
356
357#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
358struct FederationConfigKeyV0 {
359 id: FederationId,
360}
361
362#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
363pub struct FederationConfigV0 {
364 pub invite_code: InviteCode,
365 pub federation_index: u64,
366 pub timelock_delta: u64,
367 #[serde(with = "serde_routing_fees")]
368 pub fees: RoutingFees,
369}
370
371#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
372struct FederationConfigKeyV1 {
373 id: FederationId,
374}
375
376#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
377pub struct FederationConfigV1 {
378 pub invite_code: InviteCode,
379 #[serde(alias = "mint_channel_id")]
382 pub federation_index: u64,
383 pub timelock_delta: u64,
384 #[serde(with = "serde_routing_fees")]
385 pub fees: RoutingFees,
386}
387
388#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash, Ord, PartialOrd)]
389struct FederationConfigKey {
390 id: FederationId,
391}
392
393impl_db_record!(
394 key = FederationConfigKeyV0,
395 value = FederationConfigV0,
396 db_prefix = DbKeyPrefix::FederationConfig,
397);
398
399impl_db_record!(
400 key = FederationConfigKeyV1,
401 value = FederationConfigV1,
402 db_prefix = DbKeyPrefix::FederationConfig,
403);
404
405impl_db_record!(
406 key = FederationConfigKey,
407 value = FederationConfig,
408 db_prefix = DbKeyPrefix::FederationConfig,
409);
410
411impl_db_lookup!(
412 key = FederationConfigKeyV0,
413 query_prefix = FederationConfigKeyPrefixV0
414);
415impl_db_lookup!(
416 key = FederationConfigKeyV1,
417 query_prefix = FederationConfigKeyPrefixV1
418);
419impl_db_lookup!(
420 key = FederationConfigKey,
421 query_prefix = FederationConfigKeyPrefix
422);
423
424#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
425struct GatewayPublicKeyV0;
426
427#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
428struct GatewayPublicKey {
429 protocol: RegisteredProtocol,
430}
431
432#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
433struct GatewayPublicKeyPrefix;
434
435impl_db_record!(
436 key = GatewayPublicKeyV0,
437 value = Keypair,
438 db_prefix = DbKeyPrefix::GatewayPublicKey,
439);
440
441impl_db_record!(
442 key = GatewayPublicKey,
443 value = Keypair,
444 db_prefix = DbKeyPrefix::GatewayPublicKey,
445);
446
447impl_db_lookup!(
448 key = GatewayPublicKey,
449 query_prefix = GatewayPublicKeyPrefix
450);
451
452#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
453struct GatewayConfigurationKeyV0;
454
455#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
456struct GatewayConfigurationV0 {
457 password: String,
458 num_route_hints: u32,
459 #[serde(with = "serde_routing_fees")]
460 routing_fees: RoutingFees,
461 network: NetworkLegacyEncodingWrapper,
462}
463
464#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
465pub struct GatewayConfigurationKeyV1;
466
467#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
468pub struct GatewayConfigurationV1 {
469 pub hashed_password: sha256::Hash,
470 pub num_route_hints: u32,
471 #[serde(with = "serde_routing_fees")]
472 pub routing_fees: RoutingFees,
473 pub network: NetworkLegacyEncodingWrapper,
474 pub password_salt: [u8; 16],
475}
476
477#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
478pub struct GatewayConfigurationKeyV2;
479
480#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable, Serialize, Deserialize)]
481pub struct GatewayConfigurationV2 {
482 pub num_route_hints: u32,
483 #[serde(with = "serde_routing_fees")]
484 pub routing_fees: RoutingFees,
485 pub network: NetworkLegacyEncodingWrapper,
486}
487
488impl_db_record!(
489 key = GatewayConfigurationKeyV0,
490 value = GatewayConfigurationV0,
491 db_prefix = DbKeyPrefix::GatewayConfiguration,
492);
493
494impl_db_record!(
495 key = GatewayConfigurationKeyV1,
496 value = GatewayConfigurationV1,
497 db_prefix = DbKeyPrefix::GatewayConfiguration,
498);
499
500impl_db_record!(
501 key = GatewayConfigurationKeyV2,
502 value = GatewayConfigurationV2,
503 db_prefix = DbKeyPrefix::GatewayConfiguration,
504);
505
506#[derive(Debug, Clone, Eq, PartialEq, Encodable, Decodable)]
507struct PreimageAuthentication {
508 payment_hash: sha256::Hash,
509}
510
511impl_db_record!(
512 key = PreimageAuthentication,
513 value = sha256::Hash,
514 db_prefix = DbKeyPrefix::PreimageAuthentication
515);
516
517#[allow(dead_code)] #[derive(Debug, Encodable, Decodable)]
519struct PreimageAuthenticationPrefix;
520
521impl_db_lookup!(
522 key = PreimageAuthentication,
523 query_prefix = PreimageAuthenticationPrefix
524);
525
526#[derive(Debug, Encodable, Decodable)]
527struct IrohKey;
528
529impl_db_record!(
530 key = IrohKey,
531 value = iroh::SecretKey,
532 db_prefix = DbKeyPrefix::Iroh
533);
534
535#[derive(Debug, Encodable, Decodable)]
536pub struct FederationBackupKey {
537 federation_id: FederationId,
538}
539
540#[derive(Debug, Encodable, Decodable)]
541pub struct FederationBackupPrefix;
542
543#[derive(Debug)]
544pub struct FederationBackupTime(Option<SystemTime>);
546
547impl Encodable for FederationBackupTime {
548 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
549 encode_legacy_option_system_time(&self.0, writer)
550 }
551}
552
553impl Decodable for FederationBackupTime {
554 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
555 decoder: &mut D,
556 modules: &ModuleDecoderRegistry,
557 ) -> Result<Self, DecodeError> {
558 Ok(Self(with_decoding_context(
559 decode_legacy_option_system_time_from_finite_reader(decoder, modules),
560 "Decoding tuple block FederationBackupTime field field_0",
561 )?))
562 }
563}
564
565impl_db_record!(
566 key = FederationBackupKey,
567 value = FederationBackupTime,
568 db_prefix = DbKeyPrefix::FederationBackup,
569);
570
571impl_db_lookup!(
572 key = FederationBackupKey,
573 query_prefix = FederationBackupPrefix,
574);
575
576pub fn get_gatewayd_database_migrations() -> BTreeMap<DatabaseVersion, GeneralDbMigrationFn> {
577 let mut migrations: BTreeMap<DatabaseVersion, GeneralDbMigrationFn> = BTreeMap::new();
578 migrations.insert(
579 DatabaseVersion(0),
580 Box::new(|ctx| migrate_to_v1(ctx).boxed()),
581 );
582 migrations.insert(
583 DatabaseVersion(1),
584 Box::new(|ctx| migrate_to_v2(ctx).boxed()),
585 );
586 migrations.insert(
587 DatabaseVersion(2),
588 Box::new(|ctx| migrate_to_v3(ctx).boxed()),
589 );
590 migrations.insert(
591 DatabaseVersion(3),
592 Box::new(|ctx| migrate_to_v4(ctx).boxed()),
593 );
594 migrations.insert(
595 DatabaseVersion(4),
596 Box::new(|ctx| migrate_to_v5(ctx).boxed()),
597 );
598 migrations.insert(
599 DatabaseVersion(5),
600 Box::new(|ctx| migrate_to_v6(ctx).boxed()),
601 );
602 migrations.insert(
603 DatabaseVersion(6),
604 Box::new(|ctx| migrate_to_v7(ctx).boxed()),
605 );
606 migrations
607}
608
609async fn migrate_to_v1(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
610 fn hash_password(plaintext_password: &str, salt: [u8; 16]) -> sha256::Hash {
613 let mut bytes = Vec::new();
614 bytes.append(&mut plaintext_password.consensus_encode_to_vec());
615 bytes.append(&mut salt.consensus_encode_to_vec());
616 sha256::Hash::hash(&bytes)
617 }
618
619 let mut dbtx = ctx.dbtx();
620
621 if let Some(old_gateway_config) = dbtx.remove_entry(&GatewayConfigurationKeyV0).await {
623 let password_salt: [u8; 16] = rand::thread_rng().r#gen();
624 let hashed_password = hash_password(&old_gateway_config.password, password_salt);
625 let new_gateway_config = GatewayConfigurationV1 {
626 hashed_password,
627 num_route_hints: old_gateway_config.num_route_hints,
628 routing_fees: old_gateway_config.routing_fees,
629 network: old_gateway_config.network,
630 password_salt,
631 };
632 dbtx.insert_entry(&GatewayConfigurationKeyV1, &new_gateway_config)
633 .await;
634 }
635
636 Ok(())
637}
638
639async fn migrate_to_v2(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
640 let mut dbtx = ctx.dbtx();
641
642 for (old_federation_id, _old_federation_config) in dbtx.load_federation_configs_v0().await {
644 if let Some(old_federation_config) = dbtx
645 .remove_entry(&FederationConfigKeyV0 {
646 id: old_federation_id,
647 })
648 .await
649 {
650 let new_federation_config = FederationConfigV1 {
651 invite_code: old_federation_config.invite_code,
652 federation_index: old_federation_config.federation_index,
653 timelock_delta: old_federation_config.timelock_delta,
654 fees: old_federation_config.fees,
655 };
656 let new_federation_key = FederationConfigKeyV1 {
657 id: old_federation_id,
658 };
659 dbtx.insert_entry(&new_federation_key, &new_federation_config)
660 .await;
661 }
662 }
663 Ok(())
664}
665
666async fn migrate_to_v3(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
667 let mut dbtx = ctx.dbtx();
668
669 if let Some(old_gateway_config) = dbtx.remove_entry(&GatewayConfigurationKeyV1).await {
671 let new_gateway_config = GatewayConfigurationV2 {
672 num_route_hints: old_gateway_config.num_route_hints,
673 routing_fees: old_gateway_config.routing_fees,
674 network: old_gateway_config.network,
675 };
676 dbtx.insert_entry(&GatewayConfigurationKeyV2, &new_gateway_config)
677 .await;
678 }
679
680 Ok(())
681}
682
683async fn migrate_to_v4(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
684 let mut dbtx = ctx.dbtx();
685
686 dbtx.remove_entry(&GatewayConfigurationKeyV2).await;
687
688 let configs = dbtx
689 .find_by_prefix(&FederationConfigKeyPrefixV1)
690 .await
691 .collect::<Vec<_>>()
692 .await;
693 for (fed_id, _old_config) in configs {
694 if let Some(old_federation_config) = dbtx.remove_entry(&fed_id).await {
695 let new_fed_config = FederationConfig {
696 invite_code: old_federation_config.invite_code,
697 federation_index: old_federation_config.federation_index,
698 lightning_fee: old_federation_config.fees.into(),
699 transaction_fee: PaymentFee::TRANSACTION_FEE_DEFAULT,
700 _connector: ConnectorType::Tcp,
702 };
703 let new_key = FederationConfigKey { id: fed_id.id };
704 dbtx.insert_new_entry(&new_key, &new_fed_config).await;
705 }
706 }
707 Ok(())
708}
709
710async fn migrate_to_v5(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
715 let mut dbtx = ctx.dbtx();
716 migrate_federation_configs(&mut dbtx).await
717}
718
719async fn migrate_federation_configs(
720 dbtx: &mut DatabaseTransaction<'_>,
721) -> Result<(), anyhow::Error> {
722 let problem_entries = dbtx
731 .raw_find_by_prefix(&[0x04])
732 .await?
733 .collect::<BTreeMap<_, _>>()
734 .await;
735 for (mut problem_key, value) in problem_entries {
736 if problem_key.len() == 33
740 && let Ok(federation_id) = FederationId::consensus_decode_whole(
741 &problem_key[1..33],
742 &ModuleDecoderRegistry::default(),
743 )
744 && let Ok(federation_config) =
745 FederationConfig::consensus_decode_whole(&value, &ModuleDecoderRegistry::default())
746 && federation_id == federation_config.invite_code.federation_id()
747 {
748 continue;
749 }
750
751 dbtx.raw_remove_entry(&problem_key).await?;
752 let mut new_key = vec![DbKeyPrefix::ClientDatabase as u8];
753 new_key.append(&mut problem_key);
754 dbtx.raw_insert_bytes(&new_key, &value).await?;
755 }
756
757 let fed_ids = dbtx
760 .find_by_prefix(&FederationConfigKeyPrefix)
761 .await
762 .collect::<BTreeMap<_, _>>()
763 .await;
764 for fed_id in fed_ids.keys() {
765 let federation_id_bytes = fed_id.id.consensus_encode_to_vec();
766 let isolated_entries = dbtx
767 .raw_find_by_prefix(&federation_id_bytes)
768 .await?
769 .collect::<BTreeMap<_, _>>()
770 .await;
771 for (mut key, value) in isolated_entries {
772 dbtx.raw_remove_entry(&key).await?;
773 let mut new_key = vec![DbKeyPrefix::ClientDatabase as u8];
774 new_key.append(&mut key);
775 dbtx.raw_insert_bytes(&new_key, &value).await?;
776 }
777 }
778
779 Ok(())
780}
781
782async fn migrate_to_v6(mut ctx: GeneralDbMigrationFnContext<'_>) -> Result<(), anyhow::Error> {
783 let mut dbtx = ctx.dbtx();
784
785 let configs = dbtx
786 .find_by_prefix(&FederationConfigKeyPrefix)
787 .await
788 .collect::<Vec<_>>()
789 .await;
790 for (fed_id, _) in configs {
791 dbtx.insert_new_entry(
792 &FederationBackupKey {
793 federation_id: fed_id.id,
794 },
795 &FederationBackupTime(None),
796 )
797 .await;
798 }
799 Ok(())
800}
801
802async fn migrate_to_v7(mut ctx: GeneralDbMigrationFnContext<'_>) -> anyhow::Result<()> {
803 let mut dbtx = ctx.dbtx();
804
805 let gateway_keypair = dbtx.remove_entry(&GatewayPublicKeyV0).await;
806 if let Some(gateway_keypair) = gateway_keypair {
807 dbtx.insert_new_entry(
808 &GatewayPublicKey {
809 protocol: RegisteredProtocol::Http,
810 },
811 &gateway_keypair,
812 )
813 .await;
814 }
815
816 Ok(())
817}
818
819#[derive(Debug, Encodable, Decodable)]
820struct RegisteredIncomingContractKey(pub PaymentImage);
821
822#[derive(Debug, Encodable, Decodable)]
823pub struct RegisteredIncomingContract {
824 pub federation_id: FederationId,
825 pub incoming_amount_msats: u64,
827 pub contract: IncomingContract,
828}
829
830impl_db_record!(
831 key = RegisteredIncomingContractKey,
832 value = RegisteredIncomingContract,
833 db_prefix = DbKeyPrefix::RegisteredIncomingContract,
834);
835
836#[derive(Debug, Encodable, Decodable)]
842struct ClaimedOutgoingPaymentImageKey(pub PaymentImage);
843
844impl_db_record!(
845 key = ClaimedOutgoingPaymentImageKey,
846 value = OperationId,
847 db_prefix = DbKeyPrefix::ClaimedOutgoingPaymentImage,
848);
849
850#[cfg(test)]
851mod migration_tests;