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