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