Skip to main content

fedimint_wallet_client/
client_db.rs

1use core::fmt;
2use std::ops;
3use std::str::FromStr;
4use std::time::SystemTime;
5
6use fedimint_client_module::module::init::recovery::RecoveryFromHistoryCommon;
7use fedimint_core::core::OperationId;
8use fedimint_core::encoding::{Decodable, Encodable};
9use fedimint_core::{TransactionId, impl_db_lookup, impl_db_record};
10use serde::{Deserialize, Serialize};
11use strum_macros::EnumIter;
12
13use crate::backup::WalletRecoveryState;
14
15#[derive(Clone, EnumIter, Debug)]
16pub enum DbKeyPrefix {
17    NextPegInTweakIndex = 0x2c,
18    PegInTweakIndex = 0x2d,
19    ClaimedPegIn = 0x2e,
20    RecoveryFinalized = 0x2f,
21    RecoveryState = 0x30,
22    SupportsSafeDeposit = 0x31,
23    PegInPoolCursor = 0x32,
24    /// Prefixes between 0xb0..=0xcf shall all be considered allocated for
25    /// historical and future external use
26    ExternalReservedStart = 0xb0,
27    /// Prefixes between 0xd0..=0xff shall all be considered allocated for
28    /// historical and future internal use
29    CoreInternalReservedStart = 0xd0,
30    /// Prefixes between 0xd0..=0xff shall all be considered allocated for
31    /// historical and future internal use
32    CoreInternalReservedEnd = 0xff,
33}
34
35impl std::fmt::Display for DbKeyPrefix {
36    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37        write!(f, "{self:?}")
38    }
39}
40
41/// An index of a deposit address
42///
43/// Under the hood it's similar to `ChildId`, but in a wallet module
44/// it's used often enough to deserve own newtype.
45#[derive(
46    Copy,
47    Clone,
48    Debug,
49    Encodable,
50    Decodable,
51    Serialize,
52    Deserialize,
53    Default,
54    PartialEq,
55    Eq,
56    PartialOrd,
57    Ord,
58)]
59pub struct TweakIdx(pub u64);
60
61impl fmt::Display for TweakIdx {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        f.write_fmt(format_args!("TweakIdx({})", self.0))
64    }
65}
66
67impl FromStr for TweakIdx {
68    type Err = <u64 as FromStr>::Err;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        Ok(Self(FromStr::from_str(s)?))
72    }
73}
74
75impl TweakIdx {
76    #[must_use]
77    pub fn next(self) -> Self {
78        Self(self.0 + 1)
79    }
80
81    #[must_use]
82    pub fn prev(self) -> Option<Self> {
83        self.0.checked_sub(1).map(Self)
84    }
85
86    #[must_use]
87    pub fn advance(self, i: u64) -> Self {
88        Self(self.0 + i)
89    }
90
91    pub fn saturating_sub(&self, rhs: TweakIdx) -> u64 {
92        self.0.saturating_sub(rhs.0)
93    }
94}
95
96impl ops::Sub for TweakIdx {
97    type Output = u64;
98
99    fn sub(self, rhs: Self) -> Self::Output {
100        self.0 - rhs.0
101    }
102}
103
104/// A counter tracking next index to use to derive a peg-in address
105#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
106pub struct NextPegInTweakIndexKey;
107
108impl_db_record!(
109    key = NextPegInTweakIndexKey,
110    value = TweakIdx,
111    db_prefix = DbKeyPrefix::NextPegInTweakIndex,
112);
113
114/// Peg in index that was already allocated and is being tracked for deposits to
115/// claim
116#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
117pub struct PegInTweakIndexKey(pub TweakIdx);
118
119#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
120pub struct PegInTweakIndexPrefix;
121
122#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
123pub struct PegInTweakIndexData {
124    /// [`OperationId`] corresponding to this peg-in address.
125    pub operation_id: OperationId,
126    /// Time the address was allocated (created)
127    pub creation_time: SystemTime,
128    /// Last time the client checked the address for pegins
129    pub last_check_time: Option<SystemTime>,
130    /// Next time client is going to checked the address for pegins
131    pub next_check_time: Option<SystemTime>,
132    /// All previous on chain outputs claimed for this peg-in address.
133    pub claimed: Vec<bitcoin::OutPoint>,
134}
135
136impl_db_record!(
137    key = PegInTweakIndexKey,
138    value = PegInTweakIndexData,
139    db_prefix = DbKeyPrefix::PegInTweakIndex,
140);
141
142impl_db_lookup!(
143    key = PegInTweakIndexKey,
144    query_prefix = PegInTweakIndexPrefix
145);
146
147#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
148pub struct ClaimedPegInKey {
149    pub peg_in_index: TweakIdx,
150    pub btc_out_point: bitcoin::OutPoint,
151}
152
153#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
154pub struct ClaimedPegInPrefix;
155
156#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
157pub struct ClaimedPegInData {
158    /// The Fedimint transaction id of the claim transaction. If there was no
159    /// claim transaction due to the deposit being smaller than the deposit fee
160    /// there will be no claim transaction and the transaction id will be all
161    /// zeros.
162    pub claim_txid: TransactionId,
163    pub change: Vec<fedimint_core::OutPoint>,
164}
165
166impl_db_record!(
167    key = ClaimedPegInKey,
168    value = ClaimedPegInData,
169    db_prefix = DbKeyPrefix::ClaimedPegIn,
170    notify_on_modify = true,
171);
172impl_db_lookup!(key = ClaimedPegInKey, query_prefix = ClaimedPegInPrefix);
173
174#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
175pub struct RecoveryFinalizedKey;
176
177#[derive(Debug, Clone, Encodable, Decodable)]
178pub struct RecoveryFinalizedKeyPrefix;
179
180impl_db_record!(
181    key = RecoveryFinalizedKey,
182    value = bool,
183    db_prefix = DbKeyPrefix::RecoveryFinalized,
184);
185
186#[derive(Debug, Clone, Encodable, Decodable, Serialize)]
187pub struct RecoveryStateKey;
188
189#[derive(Debug, Clone, Encodable, Decodable)]
190pub struct RestoreStateKeyPrefix;
191
192impl_db_record!(
193    key = RecoveryStateKey,
194    value = (WalletRecoveryState, RecoveryFromHistoryCommon),
195    db_prefix = DbKeyPrefix::RecoveryState,
196);
197
198#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
199pub struct SupportsSafeDepositKey;
200
201#[derive(Clone, Debug, Encodable, Decodable)]
202pub struct SupportsSafeDepositPrefix;
203
204impl_db_record!(
205    key = SupportsSafeDepositKey,
206    value = (),
207    db_prefix = DbKeyPrefix::SupportsSafeDeposit,
208);
209
210impl_db_lookup!(
211    key = SupportsSafeDepositKey,
212    query_prefix = SupportsSafeDepositPrefix
213);
214
215/// Round-robin cursor for
216/// [`crate::WalletClientModule::allocate_deposit_address_pooled`].
217///
218/// Stores the [`TweakIdx`] of the most recently reused unused address. The
219/// next reuse picks the smallest unused tweak with `tweak_idx > cursor`,
220/// wrapping back to the smallest unused tweak when none exists.
221#[derive(Clone, Debug, Encodable, Decodable, Serialize)]
222pub struct PegInPoolCursorKey;
223
224impl_db_record!(
225    key = PegInPoolCursorKey,
226    value = TweakIdx,
227    db_prefix = DbKeyPrefix::PegInPoolCursor,
228);