fedimint_wallet_common/
config.rs

1use std::collections::BTreeMap;
2
3use bitcoin::secp256k1::SecretKey;
4use bitcoin::Network;
5use fedimint_core::core::ModuleKind;
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::envs::BitcoinRpcConfig;
8use fedimint_core::module::serde_json;
9use fedimint_core::util::SafeUrl;
10use fedimint_core::{plugin_types_trait_impl_config, Feerate, PeerId};
11use miniscript::descriptor::{Wpkh, Wsh};
12use serde::{Deserialize, Serialize};
13
14use crate::envs::FM_PORT_ESPLORA_ENV;
15use crate::keys::CompressedPublicKey;
16use crate::{PegInDescriptor, WalletCommonInit};
17
18/// Helps against dust attacks where an attacker deposits UTXOs that, with
19/// higher fee levels, cannot be spent profitably.
20const DEFAULT_DEPOSIT_FEE_SATS: u64 = 1000;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct WalletGenParams {
24    pub local: WalletGenParamsLocal,
25    pub consensus: WalletGenParamsConsensus,
26}
27
28impl WalletGenParams {
29    pub fn regtest(bitcoin_rpc: BitcoinRpcConfig) -> WalletGenParams {
30        WalletGenParams {
31            local: WalletGenParamsLocal { bitcoin_rpc },
32            consensus: WalletGenParamsConsensus {
33                network: Network::Regtest,
34                finality_delay: 10,
35                client_default_bitcoin_rpc: BitcoinRpcConfig {
36                    kind: "esplora".to_string(),
37                    url: SafeUrl::parse(&format!(
38                        "http://127.0.0.1:{}/",
39                        std::env::var(FM_PORT_ESPLORA_ENV).unwrap_or(String::from("50002"))
40                    ))
41                    .expect("Failed to parse default esplora server"),
42                },
43                fee_consensus: FeeConsensus::default(),
44            },
45        }
46    }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct WalletGenParamsLocal {
51    pub bitcoin_rpc: BitcoinRpcConfig,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct WalletGenParamsConsensus {
56    pub network: Network,
57    pub finality_delay: u32,
58    /// See [`WalletConfigConsensus::client_default_bitcoin_rpc`].
59    pub client_default_bitcoin_rpc: BitcoinRpcConfig,
60    /// Fees to be charged for deposits and withdraws _by the federation_ in
61    /// addition to any on-chain fees.
62    ///
63    /// Deposit fees in particular are a protection against dust attacks.
64    pub fee_consensus: FeeConsensus,
65}
66
67#[derive(Clone, Debug, Serialize, Deserialize)]
68pub struct WalletConfig {
69    pub local: WalletConfigLocal,
70    pub private: WalletConfigPrivate,
71    pub consensus: WalletConfigConsensus,
72}
73
74#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
75pub struct WalletConfigLocal {
76    /// Configures which bitcoin RPC to use
77    pub bitcoin_rpc: BitcoinRpcConfig,
78}
79
80#[derive(Clone, Debug, Serialize, Deserialize)]
81pub struct WalletConfigPrivate {
82    /// Secret key for signing bitcoin multisig transactions
83    pub peg_in_key: SecretKey,
84}
85
86#[derive(Clone, Debug, Serialize, Deserialize, Encodable, Decodable)]
87pub struct WalletConfigConsensus {
88    /// Bitcoin network (e.g. testnet, bitcoin)
89    pub network: Network,
90    /// The federations public peg-in-descriptor
91    pub peg_in_descriptor: PegInDescriptor,
92    /// The public keys for the bitcoin multisig
93    pub peer_peg_in_keys: BTreeMap<PeerId, CompressedPublicKey>,
94    /// How many bitcoin blocks to wait before considering a transaction
95    /// confirmed
96    pub finality_delay: u32,
97    /// If we cannot determine the feerate from our bitcoin node, default to
98    /// this
99    pub default_fee: Feerate,
100    /// Fees for bitcoin transactions
101    pub fee_consensus: FeeConsensus,
102    /// Points to a Bitcoin API that the client can use to interact with the
103    /// Bitcoin blockchain (mostly for deposits). *Eventually the backend should
104    /// become configurable locally and this should merely be a suggested
105    /// default by the federation.*
106    ///
107    /// **This is only used by the client, the RPC used by the server is defined
108    /// in [`WalletConfigLocal`].**
109    pub client_default_bitcoin_rpc: BitcoinRpcConfig,
110}
111
112#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
113pub struct WalletClientConfig {
114    /// The federations public peg-in-descriptor
115    pub peg_in_descriptor: PegInDescriptor,
116    /// The bitcoin network the client will use
117    pub network: Network,
118    /// Confirmations required for a peg in to be accepted by federation
119    pub finality_delay: u32,
120    pub fee_consensus: FeeConsensus,
121    /// Points to a Bitcoin API that the client can use to interact with the
122    /// Bitcoin blockchain (mostly for deposits). *Eventually the backend should
123    /// become configurable locally and this should merely be a suggested
124    /// default by the federation.*
125    pub default_bitcoin_rpc: BitcoinRpcConfig,
126}
127
128impl std::fmt::Display for WalletClientConfig {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        write!(
131            f,
132            "WalletClientConfig {}",
133            serde_json::to_string(self).map_err(|_e| std::fmt::Error)?
134        )
135    }
136}
137
138#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
139pub struct FeeConsensus {
140    pub peg_in_abs: fedimint_core::Amount,
141    pub peg_out_abs: fedimint_core::Amount,
142}
143
144impl Default for FeeConsensus {
145    fn default() -> Self {
146        Self {
147            peg_in_abs: fedimint_core::Amount::from_sats(DEFAULT_DEPOSIT_FEE_SATS),
148            peg_out_abs: fedimint_core::Amount::ZERO,
149        }
150    }
151}
152
153impl WalletConfig {
154    #[allow(clippy::too_many_arguments)]
155    pub fn new(
156        pubkeys: BTreeMap<PeerId, CompressedPublicKey>,
157        sk: SecretKey,
158        threshold: usize,
159        network: Network,
160        finality_delay: u32,
161        bitcoin_rpc: BitcoinRpcConfig,
162        client_default_bitcoin_rpc: BitcoinRpcConfig,
163        fee_consensus: FeeConsensus,
164    ) -> Self {
165        let peg_in_descriptor = if pubkeys.len() == 1 {
166            PegInDescriptor::Wpkh(
167                Wpkh::new(
168                    *pubkeys
169                        .values()
170                        .next()
171                        .expect("there is exactly one pub key"),
172                )
173                .expect("Our key type is always compressed"),
174            )
175        } else {
176            PegInDescriptor::Wsh(
177                Wsh::new_sortedmulti(threshold, pubkeys.values().copied().collect()).unwrap(),
178            )
179        };
180
181        Self {
182            local: WalletConfigLocal { bitcoin_rpc },
183            private: WalletConfigPrivate { peg_in_key: sk },
184            consensus: WalletConfigConsensus {
185                network,
186                peg_in_descriptor,
187                peer_peg_in_keys: pubkeys,
188                finality_delay,
189                default_fee: Feerate { sats_per_kvb: 1000 },
190                fee_consensus,
191                client_default_bitcoin_rpc,
192            },
193        }
194    }
195}
196
197plugin_types_trait_impl_config!(
198    WalletCommonInit,
199    WalletGenParams,
200    WalletGenParamsLocal,
201    WalletGenParamsConsensus,
202    WalletConfig,
203    WalletConfigLocal,
204    WalletConfigPrivate,
205    WalletConfigConsensus,
206    WalletClientConfig
207);