1use bitcoin::secp256k1::ecdsa::Signature;
2use bitcoin::{BlockHash, OutPoint, TxOut, Txid};
3use fedimint_core::db::IDatabaseTransactionOpsCoreTyped;
4use fedimint_core::encoding::{Decodable, Encodable};
5use fedimint_core::module::ModuleConsensusVersion;
6use fedimint_core::{PeerId, impl_db_lookup, impl_db_record};
7use fedimint_server_core::migration::{
8 ModuleHistoryItem, ServerModuleDbMigrationFnContext, ServerModuleDbMigrationFnContextExt as _,
9};
10use futures::StreamExt;
11use serde::Serialize;
12use strum_macros::EnumIter;
13
14use crate::common::{RecoveryItem, WalletInput};
15use crate::{
16 PEG_OUT_CHANGE_VOUT, PendingTransaction, SpendableUTXO, UnsignedTransaction, Wallet,
17 WalletOutputOutcome,
18};
19
20#[repr(u8)]
21#[derive(Clone, EnumIter, Debug)]
22pub enum DbKeyPrefix {
23 BlockHash = 0x30,
24 Utxo = 0x31,
25 BlockCountVote = 0x32,
26 FeeRateVote = 0x33,
27 UnsignedTransaction = 0x34,
28 PendingTransaction = 0x35,
29 PegOutTxSigCi = 0x36,
30 PegOutBitcoinOutPoint = 0x37,
31 PegOutNonce = 0x38,
32 ClaimedPegInOutpoint = 0x39,
33 ConsensusVersionVote = 0x40,
34 UnspentTxOut = 0x41,
35 ConsensusVersionVotingActivation = 0x42,
36 BlockHashByHeight = 0x43,
40 RecoveryItem = 0x44,
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(Clone, Debug, Encodable, Decodable, Serialize)]
50pub struct BlockHashKey(pub BlockHash);
51
52#[derive(Clone, Debug, Encodable, Decodable)]
53pub struct BlockHashKeyPrefix;
54
55impl_db_record!(
56 key = BlockHashKey,
57 value = (),
58 db_prefix = DbKeyPrefix::BlockHash,
59);
60impl_db_lookup!(key = BlockHashKey, query_prefix = BlockHashKeyPrefix);
61
62#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
65pub struct BlockHashByHeightKey(pub u32);
66
67#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
68pub struct BlockHashByHeightValue(pub BlockHash);
69
70#[derive(Clone, Debug, Encodable, Decodable)]
71pub struct BlockHashByHeightKeyPrefix;
72
73impl_db_record!(
74 key = BlockHashByHeightKey,
75 value = BlockHashByHeightValue,
76 db_prefix = DbKeyPrefix::BlockHashByHeight,
77);
78impl_db_lookup!(
79 key = BlockHashByHeightKey,
80 query_prefix = BlockHashByHeightKeyPrefix
81);
82
83#[derive(Clone, Debug, Eq, PartialEq, Encodable, Decodable, Serialize)]
84pub struct UTXOKey(pub bitcoin::OutPoint);
85
86#[derive(Clone, Debug, Encodable, Decodable)]
87pub struct UTXOPrefixKey;
88
89impl_db_record!(
90 key = UTXOKey,
91 value = SpendableUTXO,
92 db_prefix = DbKeyPrefix::Utxo,
93);
94impl_db_lookup!(key = UTXOKey, query_prefix = UTXOPrefixKey);
95
96#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
97pub struct UnsignedTransactionKey(pub Txid);
98
99#[derive(Clone, Debug, Encodable, Decodable)]
100pub struct UnsignedTransactionPrefixKey;
101
102impl_db_record!(
103 key = UnsignedTransactionKey,
104 value = UnsignedTransaction,
105 db_prefix = DbKeyPrefix::UnsignedTransaction,
106);
107impl_db_lookup!(
108 key = UnsignedTransactionKey,
109 query_prefix = UnsignedTransactionPrefixKey
110);
111
112#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
113pub struct PendingTransactionKey(pub Txid);
114
115#[derive(Clone, Debug, Encodable, Decodable)]
116pub struct PendingTransactionPrefixKey;
117
118impl_db_record!(
119 key = PendingTransactionKey,
120 value = PendingTransaction,
121 db_prefix = DbKeyPrefix::PendingTransaction,
122);
123impl_db_lookup!(
124 key = PendingTransactionKey,
125 query_prefix = PendingTransactionPrefixKey
126);
127
128#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
129pub struct PegOutTxSignatureCI(pub Txid);
130
131#[derive(Clone, Debug, Encodable, Decodable)]
132pub struct PegOutTxSignatureCIPrefix;
133
134impl_db_record!(
135 key = PegOutTxSignatureCI,
136 value = Vec<Signature>,
137 db_prefix = DbKeyPrefix::PegOutTxSigCi,
138);
139impl_db_lookup!(
140 key = PegOutTxSignatureCI,
141 query_prefix = PegOutTxSignatureCIPrefix
142);
143
144#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
145pub struct PegOutBitcoinTransaction(pub fedimint_core::OutPoint);
146
147#[derive(Clone, Debug, Encodable, Decodable)]
148pub struct PegOutBitcoinTransactionPrefix;
149
150impl_db_record!(
151 key = PegOutBitcoinTransaction,
152 value = WalletOutputOutcome,
153 db_prefix = DbKeyPrefix::PegOutBitcoinOutPoint,
154);
155
156impl_db_lookup!(
157 key = PegOutBitcoinTransaction,
158 query_prefix = PegOutBitcoinTransactionPrefix
159);
160
161#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
162pub struct BlockCountVoteKey(pub PeerId);
163
164#[derive(Clone, Debug, Encodable, Decodable)]
165pub struct BlockCountVotePrefix;
166
167impl_db_record!(
168 key = BlockCountVoteKey,
169 value = u32,
170 db_prefix = DbKeyPrefix::BlockCountVote
171);
172
173impl_db_lookup!(key = BlockCountVoteKey, query_prefix = BlockCountVotePrefix);
174
175#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
176pub struct FeeRateVoteKey(pub PeerId);
177
178#[derive(Clone, Debug, Encodable, Decodable)]
179pub struct FeeRateVotePrefix;
180
181impl_db_record!(
182 key = FeeRateVoteKey,
183 value = fedimint_core::Feerate,
184 db_prefix = DbKeyPrefix::FeeRateVote
185);
186
187impl_db_lookup!(key = FeeRateVoteKey, query_prefix = FeeRateVotePrefix);
188
189#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
190pub struct ConsensusVersionVoteKey(pub PeerId);
191
192#[derive(Clone, Debug, Encodable, Decodable)]
193pub struct ConsensusVersionVotePrefix;
194
195impl_db_record!(
196 key = ConsensusVersionVoteKey,
197 value = ModuleConsensusVersion,
198 db_prefix = DbKeyPrefix::ConsensusVersionVote
199);
200
201impl_db_lookup!(
202 key = ConsensusVersionVoteKey,
203 query_prefix = ConsensusVersionVotePrefix
204);
205
206#[derive(Clone, Debug, Encodable, Decodable)]
207pub struct PegOutNonceKey;
208
209impl_db_record!(
210 key = PegOutNonceKey,
211 value = u64,
212 db_prefix = DbKeyPrefix::PegOutNonce
213);
214
215#[derive(Clone, Debug, Eq, PartialEq, Encodable, Decodable, Serialize)]
216pub struct ClaimedPegInOutpointKey(pub OutPoint);
217
218#[derive(Clone, Debug, Encodable, Decodable)]
219pub struct ClaimedPegInOutpointPrefixKey;
220
221impl_db_record!(
222 key = ClaimedPegInOutpointKey,
223 value = (),
224 db_prefix = DbKeyPrefix::ClaimedPegInOutpoint,
225);
226impl_db_lookup!(
227 key = ClaimedPegInOutpointKey,
228 query_prefix = ClaimedPegInOutpointPrefixKey
229);
230
231pub async fn migrate_to_v1(
233 mut ctx: ServerModuleDbMigrationFnContext<'_, Wallet>,
234) -> Result<(), anyhow::Error> {
235 let outpoints = ctx
236 .get_typed_module_history_stream()
237 .await
238 .filter_map(|item| async {
239 match item {
240 ModuleHistoryItem::Input(input) => {
241 let outpoint = input
242 .maybe_v0_ref()
243 .expect("can only support V0 wallet inputs")
244 .0
245 .outpoint();
246
247 Some(outpoint)
248 }
249 ModuleHistoryItem::Output(_, _) | ModuleHistoryItem::ConsensusItem(_) => None,
250 }
251 })
252 .collect::<Vec<_>>()
253 .await;
254
255 let mut dbtx = ctx.dbtx();
256 for outpoint in outpoints {
257 dbtx.insert_new_entry(&ClaimedPegInOutpointKey(outpoint), &())
258 .await;
259 }
260
261 Ok(())
262}
263
264#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
265pub struct UnspentTxOutKey(pub bitcoin::OutPoint);
266
267#[derive(Clone, Debug, Encodable, Decodable)]
268pub struct UnspentTxOutPrefix;
269
270impl_db_record!(
271 key = UnspentTxOutKey,
272 value = TxOut,
273 db_prefix = DbKeyPrefix::UnspentTxOut,
274);
275impl_db_lookup!(key = UnspentTxOutKey, query_prefix = UnspentTxOutPrefix);
276
277#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
278pub struct ConsensusVersionVotingActivationKey;
279
280#[derive(Clone, Debug, Encodable, Decodable)]
281pub struct ConsensusVersionVotingActivationPrefix;
282
283impl_db_record!(
284 key = ConsensusVersionVotingActivationKey,
285 value = (),
286 db_prefix = DbKeyPrefix::ConsensusVersionVotingActivation,
287);
288impl_db_lookup!(
289 key = ConsensusVersionVotingActivationKey,
290 query_prefix = ConsensusVersionVotingActivationPrefix
291);
292
293#[derive(Debug, Clone, Copy, Encodable, Decodable, Serialize)]
294pub struct RecoveryItemKey(pub u64);
295
296#[derive(Debug, Encodable, Decodable)]
297pub struct RecoveryItemKeyPrefix;
298
299impl_db_record!(
300 key = RecoveryItemKey,
301 value = RecoveryItem,
302 db_prefix = DbKeyPrefix::RecoveryItem,
303);
304impl_db_lookup!(key = RecoveryItemKey, query_prefix = RecoveryItemKeyPrefix);
305
306pub async fn migrate_to_v2(
308 mut ctx: ServerModuleDbMigrationFnContext<'_, Wallet>,
309) -> Result<(), anyhow::Error> {
310 let mut recovery_items = Vec::new();
311 let mut stream = ctx.get_typed_module_history_stream().await;
312
313 while let Some(history_item) = stream.next().await {
314 if let ModuleHistoryItem::Input(input) = history_item {
315 let (outpoint, script) = match &input {
316 WalletInput::V0(input) => {
317 (input.0.outpoint(), input.tx_output().script_pubkey.clone())
318 }
319 WalletInput::V1(input) => (input.outpoint, input.tx_out.script_pubkey.clone()),
320 WalletInput::Default { .. } => continue,
321 };
322 recovery_items.push(RecoveryItem::Input { outpoint, script });
323 }
324 }
325
326 drop(stream);
327
328 for (index, item) in recovery_items.into_iter().enumerate() {
329 ctx.dbtx()
330 .insert_new_entry(&RecoveryItemKey(index as u64), &item)
331 .await;
332 }
333
334 Ok(())
335}
336
337pub async fn migrate_to_v3(
346 mut ctx: ServerModuleDbMigrationFnContext<'_, Wallet>,
347) -> Result<(), anyhow::Error> {
348 let mut dbtx = ctx.dbtx();
349
350 let change_outpoints = dbtx
351 .find_by_prefix(&PegOutBitcoinTransactionPrefix)
352 .await
353 .map(|(_, outcome)| {
354 let WalletOutputOutcome::V0(outcome) = outcome else {
355 return None;
358 };
359
360 Some(OutPoint {
361 txid: outcome.0,
362 vout: PEG_OUT_CHANGE_VOUT,
363 })
364 })
365 .collect::<Vec<_>>()
366 .await;
367
368 for outpoint in change_outpoints.into_iter().flatten() {
369 dbtx.insert_entry(&ClaimedPegInOutpointKey(outpoint), &())
370 .await;
371 }
372
373 Ok(())
374}