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