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