1use std::io::Cursor;
2
3use fedimint_client_module::module::init::recovery::RecoveryFromHistoryCommon;
4use fedimint_client_module::module::{IdxRange, OutPointRange};
5use fedimint_core::core::OperationId;
6use fedimint_core::db::{DatabaseRecord, DatabaseTransaction, IDatabaseTransactionOpsCore};
7use fedimint_core::encoding::{Decodable, Encodable};
8use fedimint_core::module::registry::ModuleDecoderRegistry;
9use fedimint_core::{Amount, impl_db_lookup, impl_db_record};
10use fedimint_logging::LOG_CLIENT_MODULE_MINT;
11use fedimint_mint_common::Nonce;
12use serde::Serialize;
13use strum_macros::EnumIter;
14use tracing::debug;
15
16use crate::backup::recovery::MintRecoveryState;
17use crate::input::{MintInputCommon, MintInputStateMachine, MintInputStateMachineV0};
18use crate::oob::{MintOOBStateMachine, MintOOBStateMachineV0, MintOOBStates, MintOOBStatesV0};
19use crate::output::{MintOutputCommon, MintOutputStateMachine, MintOutputStateMachineV0};
20use crate::{MintClientStateMachines, NoteIndex, SpendableNoteUndecoded};
21
22#[repr(u8)]
23#[derive(Clone, EnumIter, Debug)]
24pub enum DbKeyPrefix {
25 Note = 0x20,
26 NextECashNoteIndex = 0x2a,
27 CancelledOOBSpend = 0x2b,
28 RecoveryState = 0x2c,
29 RecoveryFinalized = 0x2d,
30 ReusedNoteIndices = 0x2e,
31 RecoveryStateV2 = 0x2f,
32 ExternalReservedStart = 0xb0,
35 CoreInternalReservedStart = 0xd0,
38 CoreInternalReservedEnd = 0xff,
39}
40
41impl std::fmt::Display for DbKeyPrefix {
42 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43 write!(f, "{self:?}")
44 }
45}
46
47#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
48pub struct NoteKey {
49 pub amount: Amount,
50 pub nonce: Nonce,
51}
52
53#[derive(Debug, Clone, Encodable, Decodable)]
54pub struct NoteKeyPrefix;
55
56impl_db_record!(
57 key = NoteKey,
58 value = SpendableNoteUndecoded,
59 db_prefix = DbKeyPrefix::Note,
60);
61impl_db_lookup!(key = NoteKey, query_prefix = NoteKeyPrefix);
62
63#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
64pub struct NextECashNoteIndexKey(pub Amount);
65
66#[derive(Debug, Clone, Encodable, Decodable)]
67pub struct NextECashNoteIndexKeyPrefix;
68
69impl_db_record!(
70 key = NextECashNoteIndexKey,
71 value = u64,
72 db_prefix = DbKeyPrefix::NextECashNoteIndex,
73);
74impl_db_lookup!(
75 key = NextECashNoteIndexKey,
76 query_prefix = NextECashNoteIndexKeyPrefix
77);
78
79#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
80pub struct RecoveryStateKey;
81
82#[derive(Debug, Clone, Encodable, Decodable)]
83pub struct RestoreStateKeyPrefix;
84
85impl_db_record!(
86 key = RecoveryStateKey,
87 value = (MintRecoveryState, RecoveryFromHistoryCommon),
88 db_prefix = DbKeyPrefix::RecoveryState,
89);
90
91#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
92pub struct RecoveryFinalizedKey;
93
94#[derive(Debug, Clone, Encodable, Decodable)]
95pub struct RecoveryFinalizedKeyPrefix;
96
97impl_db_record!(
98 key = RecoveryFinalizedKey,
99 value = bool,
100 db_prefix = DbKeyPrefix::RecoveryFinalized,
101);
102
103#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
104pub struct ReusedNoteIndices;
105
106impl_db_record!(
107 key = ReusedNoteIndices,
108 value = Vec<(Amount, NoteIndex)>,
109 db_prefix = DbKeyPrefix::ReusedNoteIndices,
110);
111
112#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
113pub struct CancelledOOBSpendKey(pub OperationId);
114
115#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
116pub struct CancelledOOBSpendKeyPrefix;
117
118impl_db_record!(
119 key = CancelledOOBSpendKey,
120 value = (),
121 db_prefix = DbKeyPrefix::CancelledOOBSpend,
122 notify_on_modify = true,
123);
124
125impl_db_lookup!(
126 key = CancelledOOBSpendKey,
127 query_prefix = CancelledOOBSpendKeyPrefix,
128);
129
130#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
131pub struct RecoveryStateV2Key;
132
133impl_db_record!(
134 key = RecoveryStateV2Key,
135 value = crate::backup::recovery::RecoveryStateV2,
136 db_prefix = DbKeyPrefix::RecoveryStateV2,
137);
138
139pub async fn migrate_to_v1(
140 dbtx: &mut DatabaseTransaction<'_>,
141) -> anyhow::Result<Option<(Vec<(Vec<u8>, OperationId)>, Vec<(Vec<u8>, OperationId)>)>> {
142 dbtx.ensure_isolated().expect("Must be in our database");
143 if dbtx
147 .raw_remove_entry(&[RecoveryStateKey::DB_PREFIX])
148 .await
149 .expect("Raw operations only fail on low level errors")
150 .is_some()
151 {
152 debug!(target: LOG_CLIENT_MODULE_MINT, "Deleted previous recovery state");
153 }
154
155 Ok(None)
156}
157
158pub(crate) fn migrate_state_to_v2(
160 operation_id: OperationId,
161 cursor: &mut Cursor<&[u8]>,
162) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
163 let decoders = ModuleDecoderRegistry::default();
164
165 let mint_client_state_machine_variant = u16::consensus_decode_partial(cursor, &decoders)?;
166
167 let new_mint_state_machine = match mint_client_state_machine_variant {
168 0 => {
169 let _output_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
170 let old_state = MintOutputStateMachineV0::consensus_decode_partial(cursor, &decoders)?;
171
172 MintClientStateMachines::Output(MintOutputStateMachine {
173 common: MintOutputCommon {
174 operation_id: old_state.common.operation_id,
175 out_point_range: OutPointRange::new_single(
176 old_state.common.out_point.txid,
177 old_state.common.out_point.out_idx,
178 )
179 .expect("Can't possibly overflow"),
180 },
181 state: old_state.state,
182 })
183 }
184 1 => {
185 let _input_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
186 let old_state = MintInputStateMachineV0::consensus_decode_partial(cursor, &decoders)?;
187
188 MintClientStateMachines::Input(MintInputStateMachine {
189 common: MintInputCommon {
190 operation_id: old_state.common.operation_id,
191 out_point_range: OutPointRange::new(
192 old_state.common.txid,
193 IdxRange::new_single(old_state.common.input_idx)
194 .expect("Can't possibly overflow"),
195 ),
196 },
197 state: old_state.state,
198 })
199 }
200 2 => {
201 let _oob_sm_len = u16::consensus_decode_partial(cursor, &decoders)?;
202 let old_state = MintOOBStateMachineV0::consensus_decode_partial(cursor, &decoders)?;
203
204 let new_state = match old_state.state {
205 MintOOBStatesV0::Created(created) => MintOOBStates::Created(created),
206 MintOOBStatesV0::UserRefund(refund) => MintOOBStates::UserRefund(refund),
207 MintOOBStatesV0::TimeoutRefund(refund) => MintOOBStates::TimeoutRefund(refund),
208 };
209 MintClientStateMachines::OOB(MintOOBStateMachine {
210 operation_id: old_state.operation_id,
211 state: new_state,
212 })
213 }
214 _ => return Ok(None),
215 };
216 Ok(Some((
217 new_mint_state_machine.consensus_encode_to_vec(),
218 operation_id,
219 )))
220}