fedimint_mint_client/
backup.rs1use fedimint_client_module::module::recovery::{DynModuleBackup, ModuleBackup};
2use fedimint_core::core::{IntoDynInstance, ModuleInstanceId, ModuleKind};
3use fedimint_core::db::DatabaseTransaction;
4use fedimint_core::encoding::{Decodable, Encodable};
5use fedimint_core::{Amount, OutPoint, Tiered, TieredMulti};
6use fedimint_mint_common::KIND;
7use serde::{Deserialize, Serialize};
8
9use super::MintClientModule;
10use crate::error::PrepareEcashBackupError;
11use crate::output::{MintOutputStateMachine, NoteIssuanceRequest};
12use crate::{MintClientStateMachines, NoteIndex, SpendableNote};
13
14pub mod recovery;
15
16#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Encodable, Decodable)]
17pub enum EcashBackup {
18 V0(EcashBackupV0),
19 #[encodable_default]
20 Default {
21 variant: u64,
22 bytes: Vec<u8>,
23 },
24}
25
26impl EcashBackup {
27 pub fn new_v0(
28 spendable_notes: TieredMulti<SpendableNote>,
29 pending_notes: Vec<(OutPoint, Amount, NoteIssuanceRequest)>,
30 session_count: u64,
31 next_note_idx: Tiered<NoteIndex>,
32 ) -> EcashBackup {
33 EcashBackup::V0(EcashBackupV0 {
34 spendable_notes,
35 pending_notes,
36 session_count,
37 next_note_idx,
38 })
39 }
40}
41
42#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug, Encodable, Decodable)]
47pub struct EcashBackupV0 {
48 spendable_notes: TieredMulti<SpendableNote>,
49 pending_notes: Vec<(OutPoint, Amount, NoteIssuanceRequest)>,
50 session_count: u64,
51 next_note_idx: Tiered<NoteIndex>,
52}
53
54impl EcashBackupV0 {
55 pub fn new_empty() -> Self {
57 Self {
58 spendable_notes: TieredMulti::default(),
59 pending_notes: vec![],
60 session_count: 0,
61 next_note_idx: Tiered::default(),
62 }
63 }
64}
65
66impl ModuleBackup for EcashBackup {
67 const KIND: Option<ModuleKind> = Some(KIND);
68}
69
70impl IntoDynInstance for EcashBackup {
71 type DynType = DynModuleBackup;
72
73 fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
74 DynModuleBackup::from_typed(instance_id, self)
75 }
76}
77
78impl MintClientModule {
79 pub async fn prepare_plaintext_ecash_backup(
80 &self,
81 dbtx: &mut DatabaseTransaction<'_>,
82 ) -> Result<EcashBackup, PrepareEcashBackupError> {
83 let session_count = self.client_ctx.global_api().session_count().await?;
85
86 let notes = Self::get_all_spendable_notes(dbtx).await;
87
88 let pending_notes: Vec<(OutPoint, Amount, NoteIssuanceRequest)> = self
89 .client_ctx
90 .get_own_active_states()
91 .await
92 .into_iter()
93 .filter_map(|(state, _active_state)| match state {
94 MintClientStateMachines::Output(MintOutputStateMachine {
95 common,
96 state: crate::output::MintOutputStates::Created(created_state),
97 }) => Some(vec![(
98 OutPoint {
99 txid: common.out_point_range.txid(),
100 out_idx: common.out_point_range.start_idx(),
102 },
103 created_state.amount,
104 created_state.issuance_request,
105 )]),
106 MintClientStateMachines::Output(MintOutputStateMachine {
107 common,
108 state: crate::output::MintOutputStates::CreatedMulti(created_state),
109 }) => Some(
110 common
111 .out_point_range
112 .into_iter()
113 .map(|out_point| {
114 let issuance_request = created_state
115 .issuance_requests
116 .get(&out_point.out_idx)
117 .expect("Must have corresponding out_idx");
118 (out_point, issuance_request.0, issuance_request.1)
119 })
120 .collect(),
121 ),
122 _ => None,
123 })
124 .flatten()
125 .collect::<Vec<_>>();
126
127 let mut idxes = vec![];
128 for &amount in self.cfg.tbs_pks.tiers() {
129 idxes.push((amount, self.get_next_note_index(dbtx, amount).await));
130 }
131 let next_note_idx = Tiered::from_iter(idxes);
132
133 Ok(EcashBackup::new_v0(
134 notes
135 .into_iter_items()
136 .map(|(amt, spendable_note)| Ok((amt, spendable_note.decode()?)))
137 .collect::<Result<TieredMulti<_>, PrepareEcashBackupError>>()?,
138 pending_notes,
139 session_count,
140 next_note_idx,
141 ))
142 }
143}