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::AmountUnit;
7use fedimint_core::module::registry::ModuleDecoderRegistry;
8use fedimint_core::{Amount, TransactionId, impl_db_lookup, impl_db_record};
9use strum_macros::EnumIter;
10use tracing::warn;
11
12use crate::states::{DummyStateMachine, DummyStateMachineV1};
13
14#[repr(u8)]
15#[derive(Clone, Debug, EnumIter)]
16pub enum DbKeyPrefix {
17 ClientFunds = 0x04,
18 ClientName = 0x50,
21 ExternalReservedStart = 0xb0,
24 CoreInternalReservedStart = 0xd0,
27 CoreInternalReservedEnd = 0xff,
30}
31
32impl std::fmt::Display for DbKeyPrefix {
33 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
34 write!(f, "{self:?}")
35 }
36}
37
38#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
39pub struct DummyClientFundsKeyV0;
40
41impl_db_record!(
42 key = DummyClientFundsKeyV0,
43 value = (),
44 db_prefix = DbKeyPrefix::ClientFunds,
45);
46
47#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
48pub struct DummyClientFundsKeyV1;
49
50impl_db_record!(
51 key = DummyClientFundsKeyV1,
52 value = Amount,
53 db_prefix = DbKeyPrefix::ClientFunds,
54);
55
56#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
57pub struct DummyClientFundsKey {
58 pub unit: AmountUnit,
59}
60
61impl_db_record!(
62 key = DummyClientFundsKey,
63 value = Amount,
64 db_prefix = DbKeyPrefix::ClientFunds,
65);
66
67#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
68pub struct DummyClientFundsKeyV2PrefixAll;
69
70impl_db_lookup!(
71 key = DummyClientFundsKey,
72 query_prefix = DummyClientFundsKeyV2PrefixAll,
73);
74
75#[derive(Debug, Clone, Encodable, Decodable, Eq, PartialEq, Hash)]
76pub struct DummyClientNameKey;
77
78impl_db_record!(
79 key = DummyClientNameKey,
80 value = String,
81 db_prefix = DbKeyPrefix::ClientName,
82);
83
84pub async fn migrate_to_v1(
88 dbtx: &mut DatabaseTransaction<'_>,
89) -> anyhow::Result<Option<(Vec<(Vec<u8>, OperationId)>, Vec<(Vec<u8>, OperationId)>)>> {
90 if dbtx.remove_entry(&DummyClientFundsKeyV0).await.is_some() {
91 dbtx.insert_new_entry(&DummyClientFundsKeyV1, &Amount::from_sats(1000))
95 .await;
96 } else {
97 warn!("Dummy client did not have client funds, skipping database migration");
98 }
99
100 Ok(None)
101}
102
103pub(crate) fn get_v1_migrated_state(
105 operation_id: OperationId,
106 cursor: &mut Cursor<&[u8]>,
107) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
108 let decoders = ModuleDecoderRegistry::default();
109 let dummy_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
110
111 if dummy_sm_variant != 5 {
114 return Ok(None);
115 }
116
117 let _unreachable_state_length = u16::consensus_decode_partial(cursor, &decoders)?;
118
119 let unreachable = Unreachable::consensus_decode_partial(cursor, &decoders)?;
121 let new_state = DummyStateMachineV1::OutputDone(
122 unreachable.amount,
123 unreachable.txid,
124 unreachable.operation_id,
125 );
126 let bytes = new_state.consensus_encode_to_vec();
127 Ok(Some((bytes, operation_id)))
128}
129
130pub(crate) fn get_v2_migrated_state(
132 operation_id: OperationId,
133 cursor: &mut Cursor<&[u8]>,
134) -> anyhow::Result<Option<(Vec<u8>, OperationId)>> {
135 let decoders = ModuleDecoderRegistry::default();
136 let dummy_sm_variant = u16::consensus_decode_partial(cursor, &decoders)?;
137
138 let _state_length = u16::consensus_decode_partial(cursor, &decoders)?;
139
140 match dummy_sm_variant {
141 0 | 1 | 3 => {}
142 _ => {
143 return Ok(None);
144 }
145 }
146
147 let (amount, txid, op_id) =
148 <(Amount, TransactionId, OperationId)>::consensus_decode_partial(cursor, &decoders)?;
149
150 let bytes = match dummy_sm_variant {
154 0 => DummyStateMachine::Input(amount, AmountUnit::BITCOIN, txid, op_id)
155 .consensus_encode_to_vec(),
156 1 => DummyStateMachine::Output(amount, AmountUnit::BITCOIN, txid, op_id)
157 .consensus_encode_to_vec(),
158 3 => DummyStateMachine::OutputDone(amount, AmountUnit::BITCOIN, txid, op_id)
159 .consensus_encode_to_vec(),
160 _ => unreachable!(),
161 };
162
163 debug_assert!(DummyStateMachine::consensus_decode_whole(&bytes, &decoders).is_ok());
164
165 Ok(Some((bytes, operation_id)))
166}
167
168#[derive(Debug)]
169struct Unreachable {
170 operation_id: OperationId,
171 txid: TransactionId,
172 amount: Amount,
173}
174
175impl Decodable for Unreachable {
176 fn consensus_decode_partial<R: std::io::Read>(
177 reader: &mut R,
178 modules: &ModuleDecoderRegistry,
179 ) -> Result<Self, fedimint_core::encoding::DecodeError> {
180 let operation_id = OperationId::consensus_decode_partial(reader, modules)?;
181 let txid = TransactionId::consensus_decode_partial(reader, modules)?;
182 let amount = Amount::consensus_decode_partial(reader, modules)?;
183
184 Ok(Unreachable {
185 operation_id,
186 txid,
187 amount,
188 })
189 }
190}