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