fedimint_wallet_common/
config.rs

1use std::collections::BTreeMap;
2
3use bitcoin::Network;
4use bitcoin::secp256k1::SecretKey;
5use fedimint_core::core::ModuleKind;
6use fedimint_core::encoding::btc::NetworkLegacyEncodingWrapper;
7use fedimint_core::encoding::{Decodable, Encodable};
8use fedimint_core::envs::BitcoinRpcConfig;
9use fedimint_core::module::serde_json;
10use fedimint_core::util::SafeUrl;
11use fedimint_core::{Feerate, PeerId, plugin_types_trait_impl_config};
12use miniscript::descriptor::{Wpkh, Wsh};
13use serde::{Deserialize, Serialize};
14
15use crate::envs::FM_PORT_ESPLORA_ENV;
16use crate::keys::CompressedPublicKey;
17use crate::{PegInDescriptor, WalletCommonInit};
18
19/// Helps against dust attacks where an attacker deposits UTXOs that, with
20/// higher fee levels, cannot be spent profitably.
21const DEFAULT_DEPOSIT_FEE_SATS: u64 = 1000;
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct WalletGenParams {
25    pub local: WalletGenParamsLocal,
26    pub consensus: WalletGenParamsConsensus,
27}
28
29impl WalletGenParams {
30    pub fn regtest(bitcoin_rpc: BitcoinRpcConfig) -> WalletGenParams {
31        WalletGenParams {
32            local: WalletGenParamsLocal { bitcoin_rpc },
33            consensus: WalletGenParamsConsensus {
34                network: Network::Regtest,
35                finality_delay: 10,
36                client_default_bitcoin_rpc: BitcoinRpcConfig {
37                    kind: "esplora".to_string(),
38                    url: SafeUrl::parse(&format!(
39                        "http://127.0.0.1:{}/",
40                        std::env::var(FM_PORT_ESPLORA_ENV).unwrap_or(String::from("50002"))
41                    ))
42                    .expect("Failed to parse default esplora server"),
43                },
44                fee_consensus: FeeConsensus::default(),
45            },
46        }
47    }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct WalletGenParamsLocal {
52    pub bitcoin_rpc: BitcoinRpcConfig,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct WalletGenParamsConsensus {
57    pub network: Network,
58    pub finality_delay: u32,
59    /// See [`WalletConfigConsensus::client_default_bitcoin_rpc`].
60    pub client_default_bitcoin_rpc: BitcoinRpcConfig,
61    /// Fees to be charged for deposits and withdraws _by the federation_ in
62    /// addition to any on-chain fees.
63    ///
64    /// Deposit fees in particular are a protection against dust attacks.
65    pub fee_consensus: FeeConsensus,
66}
67
68#[derive(Clone, Debug, Serialize, Deserialize)]
69pub struct WalletConfig {
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: NetworkLegacyEncodingWrapper,
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: NetworkLegacyEncodingWrapper,
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        client_default_bitcoin_rpc: BitcoinRpcConfig,
162        fee_consensus: FeeConsensus,
163    ) -> Self {
164        let peg_in_descriptor = if pubkeys.len() == 1 {
165            PegInDescriptor::Wpkh(
166                Wpkh::new(
167                    *pubkeys
168                        .values()
169                        .next()
170                        .expect("there is exactly one pub key"),
171                )
172                .expect("Our key type is always compressed"),
173            )
174        } else {
175            PegInDescriptor::Wsh(
176                Wsh::new_sortedmulti(threshold, pubkeys.values().copied().collect()).unwrap(),
177            )
178        };
179
180        Self {
181            private: WalletConfigPrivate { peg_in_key: sk },
182            consensus: WalletConfigConsensus {
183                network: NetworkLegacyEncodingWrapper(network),
184                peg_in_descriptor,
185                peer_peg_in_keys: pubkeys,
186                finality_delay,
187                default_fee: Feerate { sats_per_kvb: 1000 },
188                fee_consensus,
189                client_default_bitcoin_rpc,
190            },
191        }
192    }
193}
194
195plugin_types_trait_impl_config!(
196    WalletCommonInit,
197    WalletGenParams,
198    WalletGenParamsLocal,
199    WalletGenParamsConsensus,
200    WalletConfig,
201    WalletConfigPrivate,
202    WalletConfigConsensus,
203    WalletClientConfig
204);