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