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 local: WalletConfigLocal,
71    pub private: WalletConfigPrivate,
72    pub consensus: WalletConfigConsensus,
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
76pub struct WalletConfigLocal {
77    /// Configures which bitcoin RPC to use
78    pub bitcoin_rpc: BitcoinRpcConfig,
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct WalletConfigPrivate {
83    /// Secret key for signing bitcoin multisig transactions
84    pub peg_in_key: SecretKey,
85}
86
87#[derive(Clone, Debug, Serialize, Deserialize, Encodable, Decodable)]
88pub struct WalletConfigConsensus {
89    /// Bitcoin network (e.g. testnet, bitcoin)
90    pub network: NetworkLegacyEncodingWrapper,
91    /// The federations public peg-in-descriptor
92    pub peg_in_descriptor: PegInDescriptor,
93    /// The public keys for the bitcoin multisig
94    pub peer_peg_in_keys: BTreeMap<PeerId, CompressedPublicKey>,
95    /// How many bitcoin blocks to wait before considering a transaction
96    /// confirmed
97    pub finality_delay: u32,
98    /// If we cannot determine the feerate from our bitcoin node, default to
99    /// this
100    pub default_fee: Feerate,
101    /// Fees for bitcoin transactions
102    pub fee_consensus: FeeConsensus,
103    /// Points to a Bitcoin API that the client can use to interact with the
104    /// Bitcoin blockchain (mostly for deposits). *Eventually the backend should
105    /// become configurable locally and this should merely be a suggested
106    /// default by the federation.*
107    ///
108    /// **This is only used by the client, the RPC used by the server is defined
109    /// in [`WalletConfigLocal`].**
110    pub client_default_bitcoin_rpc: BitcoinRpcConfig,
111}
112
113#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
114pub struct WalletClientConfig {
115    /// The federations public peg-in-descriptor
116    pub peg_in_descriptor: PegInDescriptor,
117    /// The bitcoin network the client will use
118    pub network: NetworkLegacyEncodingWrapper,
119    /// Confirmations required for a peg in to be accepted by federation
120    pub finality_delay: u32,
121    pub fee_consensus: FeeConsensus,
122    /// Points to a Bitcoin API that the client can use to interact with the
123    /// Bitcoin blockchain (mostly for deposits). *Eventually the backend should
124    /// become configurable locally and this should merely be a suggested
125    /// default by the federation.*
126    pub default_bitcoin_rpc: BitcoinRpcConfig,
127}
128
129impl std::fmt::Display for WalletClientConfig {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        write!(
132            f,
133            "WalletClientConfig {}",
134            serde_json::to_string(self).map_err(|_e| std::fmt::Error)?
135        )
136    }
137}
138
139#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
140pub struct FeeConsensus {
141    pub peg_in_abs: fedimint_core::Amount,
142    pub peg_out_abs: fedimint_core::Amount,
143}
144
145impl Default for FeeConsensus {
146    fn default() -> Self {
147        Self {
148            peg_in_abs: fedimint_core::Amount::from_sats(DEFAULT_DEPOSIT_FEE_SATS),
149            peg_out_abs: fedimint_core::Amount::ZERO,
150        }
151    }
152}
153
154impl WalletConfig {
155    #[allow(clippy::too_many_arguments)]
156    pub fn new(
157        pubkeys: BTreeMap<PeerId, CompressedPublicKey>,
158        sk: SecretKey,
159        threshold: usize,
160        network: Network,
161        finality_delay: u32,
162        bitcoin_rpc: BitcoinRpcConfig,
163        client_default_bitcoin_rpc: BitcoinRpcConfig,
164        fee_consensus: FeeConsensus,
165    ) -> Self {
166        let peg_in_descriptor = if pubkeys.len() == 1 {
167            PegInDescriptor::Wpkh(
168                Wpkh::new(
169                    *pubkeys
170                        .values()
171                        .next()
172                        .expect("there is exactly one pub key"),
173                )
174                .expect("Our key type is always compressed"),
175            )
176        } else {
177            PegInDescriptor::Wsh(
178                Wsh::new_sortedmulti(threshold, pubkeys.values().copied().collect()).unwrap(),
179            )
180        };
181
182        Self {
183            local: WalletConfigLocal { bitcoin_rpc },
184            private: WalletConfigPrivate { peg_in_key: sk },
185            consensus: WalletConfigConsensus {
186                network: NetworkLegacyEncodingWrapper(network),
187                peg_in_descriptor,
188                peer_peg_in_keys: pubkeys,
189                finality_delay,
190                default_fee: Feerate { sats_per_kvb: 1000 },
191                fee_consensus,
192                client_default_bitcoin_rpc,
193            },
194        }
195    }
196}
197
198plugin_types_trait_impl_config!(
199    WalletCommonInit,
200    WalletGenParams,
201    WalletGenParamsLocal,
202    WalletGenParamsConsensus,
203    WalletConfig,
204    WalletConfigLocal,
205    WalletConfigPrivate,
206    WalletConfigConsensus,
207    WalletClientConfig
208);