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