fedimint_dummy_client/
db.rs

1use std::io::Cursor;
2
3use fedimint_core::core::OperationId;
4use fedimint_core::db::{DatabaseTransaction, IDatabaseTransactionOpsCoreTyped};
5use fedimint_core::encoding::{Decodable, Encodable};
6use fedimint_core::module::registry::ModuleDecoderRegistry;
7use fedimint_core::{Amount, TransactionId, impl_db_record};
8use strum_macros::EnumIter;
9use tracing::warn;
10
11use crate::states::DummyStateMachine;
12
13#[repr(u8)]
14#[derive(Clone, Debug, EnumIter)]
15pub enum DbKeyPrefix {
16    ClientFunds = 0x04,
17    // Used to verify that 0x50 key can be written to, which used to conflict with
18    // `DatabaseVersionKeyV0`
19    ClientName = 0x50,
20    /// Prefixes between 0xb0..=0xcf shall all be considered allocated for
21    /// historical and future external use
22    ExternalReservedStart = 0xb0,
23    /// Prefixes between 0xd0..=0xff shall all be considered allocated for
24    /// historical and future internal use
25    CoreInternalReservedStart = 0xd0,
26    /// Prefixes between 0xd0..=0xff shall all be considered allocated for
27    /// historical and future internal use
28    CoreInternalReservedEnd = 0xff,
29}
30
31impl std::fmt::Display for DbKeyPrefix {
32    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
33        write!(f, "{self:?}")
34    }
35}
36
37#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
38pub struct DummyClientFundsKeyV0;
39
40impl_db_record!(
41    key = DummyClientFundsKeyV0,
42    value = (),
43    db_prefix = DbKeyPrefix::ClientFunds,
44);
45
46#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
47pub struct DummyClientFundsKeyV1;
48
49impl_db_record!(
50    key = DummyClientFundsKeyV1,
51    value = Amount,
52    db_prefix = DbKeyPrefix::ClientFunds,
53);
54
55#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
56pub struct DummyClientNameKey;
57
58impl_db_record!(
59    key = DummyClientNameKey,
60    value = String,
61    db_prefix = DbKeyPrefix::ClientName,
62);
63
64/// Migrates the database from version 0 to version 1 by
65/// removing `DummyClientFundsKeyV0` and inserting `DummyClientFundsKeyV1`.
66/// The new key/value pair has an `Amount` as the value.
67pub async fn migrate_to_v1(
68    dbtx: &mut DatabaseTransaction<'_>,
69) -> anyhow::Result<Option<(Vec<(Vec<u8>, OperationId)>, Vec<(Vec<u8>, OperationId)>)>> {
70    if dbtx.remove_entry(&DummyClientFundsKeyV0).await.is_some() {
71        // Since this is a dummy migration, we can insert any value for the client
72        // funds. Real modules should handle the funds properly.
73
74        dbtx.insert_new_entry(&DummyClientFundsKeyV1, &Amount::from_sats(1000))
75            .await;
76    } else {
77        warn!("Dummy client did not have client funds, skipping database migration");
78    }
79
80    Ok(None)
81}
82
83/// Maps all `Unreachable` states in the state machine to `OutputDone`
84pub(crate) fn get_v1_migrated_state(
85    operation_id: OperationId,
86    cursor: &mut Cursor<&[u8]>,
87) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
88    let decoders = ModuleDecoderRegistry::default();
89    let dummy_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
90
91    // We are only migrating the type of one of the variants, so we do nothing on
92    // other discriminants.
93    if dummy_sm_variant != 5 {
94        return Ok(None);
95    }
96
97    let _unreachable_state_length = u16::consensus_decode_partial(cursor, &decoders)?;
98
99    // Migrate `Unreachable` states to `OutputDone`
100    let unreachable = Unreachable::consensus_decode_partial(cursor, &decoders)?;
101    let new_state = DummyStateMachine::OutputDone(
102        unreachable.amount,
103        unreachable.txid,
104        unreachable.operation_id,
105    );
106    let bytes = new_state.consensus_encode_to_vec();
107    Ok(Some((bytes, operation_id)))
108}
109
110#[derive(Debug)]
111struct Unreachable {
112    operation_id: OperationId,
113    txid: TransactionId,
114    amount: Amount,
115}
116
117impl Decodable for Unreachable {
118    fn consensus_decode_partial<R: std::io::Read>(
119        reader: &mut R,
120        modules: &ModuleDecoderRegistry,
121    ) -> Result<Self, fedimint_core::encoding::DecodeError> {
122        let operation_id = OperationId::consensus_decode_partial(reader, modules)?;
123        let txid = TransactionId::consensus_decode_partial(reader, modules)?;
124        let amount = Amount::consensus_decode_partial(reader, modules)?;
125
126        Ok(Unreachable {
127            operation_id,
128            txid,
129            amount,
130        })
131    }
132}