fedimint_walletv2_common/
config.rs1use std::collections::BTreeMap;
2
3use bitcoin::Network;
4use bitcoin::hashes::{Hash, sha256};
5use fedimint_core::core::ModuleKind;
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::{Amount, PeerId, plugin_types_trait_impl_config, weight_to_vbytes};
8use secp256k1::{PublicKey, SecretKey};
9use serde::{Deserialize, Serialize};
10
11use crate::{WalletCommonInit, descriptor};
12
13plugin_types_trait_impl_config!(
14 WalletCommonInit,
15 WalletConfig,
16 WalletConfigPrivate,
17 WalletConfigConsensus,
18 WalletClientConfig
19);
20
21#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct WalletConfig {
23 pub private: WalletConfigPrivate,
24 pub consensus: WalletConfigConsensus,
25}
26
27#[derive(Clone, Debug, Serialize, Deserialize)]
28pub struct WalletConfigPrivate {
29 pub bitcoin_sk: SecretKey,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize, Encodable, Decodable)]
33pub struct WalletConfigConsensus {
34 pub bitcoin_pks: BTreeMap<PeerId, PublicKey>,
36 pub send_tx_vbytes: u64,
38 pub receive_tx_vbytes: u64,
40 pub min_feerate: u64,
43 pub dust_limit: bitcoin::Amount,
45 pub fee_consensus: FeeConsensus,
47 pub network: Network,
49}
50
51impl WalletConfigConsensus {
52 pub fn new(
76 bitcoin_pks: BTreeMap<PeerId, PublicKey>,
77 fee_consensus: FeeConsensus,
78 network: Network,
79 ) -> Self {
80 let tx_overhead_weight = 4 * 4 + 1 + 1 + 4 + 4 + 4 * 4; let change_witness_weight = descriptor(&bitcoin_pks, &sha256::Hash::all_zeros())
88 .max_weight_to_satisfy()
89 .expect("Cannot satisfy the change descriptor.")
90 .to_wu();
91
92 let change_input_weight = 32 * 4 + 4 * 4 + 4 + 4 * 4 + change_witness_weight;
97
98 let change_output_weight = 8 * 4 + 4 + 34 * 4; let destination_output_weight = 8 * 4 + 4 + 34 * 4; Self {
107 bitcoin_pks,
108 send_tx_vbytes: weight_to_vbytes(
109 tx_overhead_weight
110 + change_input_weight
111 + change_output_weight
112 + destination_output_weight,
113 ),
114 receive_tx_vbytes: weight_to_vbytes(
115 tx_overhead_weight
116 + change_input_weight
117 + change_input_weight
118 + change_output_weight,
119 ),
120 min_feerate: 1000,
121 dust_limit: bitcoin::Amount::from_sat(10_000),
122 fee_consensus,
123 network,
124 }
125 }
126}
127
128#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
129pub struct FeeConsensus {
130 pub base: Amount,
131 pub parts_per_million: u64,
132}
133
134impl FeeConsensus {
135 pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
146 anyhow::ensure!(
147 parts_per_million <= 10_000,
148 "Relative fee over ten thousand parts per million is excessive"
149 );
150
151 Ok(Self {
152 base: Amount::from_sats(100),
153 parts_per_million,
154 })
155 }
156
157 pub fn fee(&self, amount: Amount) -> Amount {
158 Amount::from_msats(self.fee_msats(amount.msats))
159 }
160
161 fn fee_msats(&self, msats: u64) -> u64 {
162 msats
163 .saturating_mul(self.parts_per_million)
164 .saturating_div(1_000_000)
165 .checked_add(self.base.msats)
166 .expect("The division creates sufficient headroom to add the base fee")
167 }
168}
169
170#[test]
171fn test_fee_consensus() {
172 let fee_consensus = FeeConsensus::new(10_000).expect("Relative fee is within range");
173
174 assert_eq!(
175 fee_consensus.fee(Amount::from_msats(99)),
176 Amount::from_sats(100)
177 );
178
179 assert_eq!(
180 fee_consensus.fee(Amount::from_sats(1)),
181 Amount::from_msats(10) + Amount::from_sats(100)
182 );
183
184 assert_eq!(
185 fee_consensus.fee(Amount::from_sats(1000)),
186 Amount::from_sats(10) + Amount::from_sats(100)
187 );
188
189 assert_eq!(
190 fee_consensus.fee(Amount::from_bitcoins(1)),
191 Amount::from_sats(1_000_000) + Amount::from_sats(100)
192 );
193
194 assert_eq!(
195 fee_consensus.fee(Amount::from_bitcoins(10_000)),
196 Amount::from_bitcoins(100) + Amount::from_sats(100)
197 );
198}
199
200#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
201pub struct WalletClientConfig {
202 pub bitcoin_pks: BTreeMap<PeerId, PublicKey>,
204 pub dust_limit: bitcoin::Amount,
206 pub fee_consensus: FeeConsensus,
208 pub network: Network,
210}
211
212impl std::fmt::Display for WalletClientConfig {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 write!(f, "WalletClientConfig {self:?}")
215 }
216}