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