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 descriptor: WalletDescriptor,
38 pub send_tx_vbytes: u64,
40 pub receive_tx_vbytes: u64,
42 pub feerate_base: u64,
45 pub dust_limit: bitcoin::Amount,
47 pub fee_consensus: FeeConsensus,
49 pub network: Network,
51}
52
53impl WalletConfigConsensus {
54 pub fn new(
78 bitcoin_pks: BTreeMap<PeerId, PublicKey>,
79 fee_consensus: FeeConsensus,
80 network: Network,
81 ) -> Self {
82 let tx_overhead_weight = 4 * 4 + 1 + 1 + 4 + 4 + 4 * 4; let change_witness_weight = descriptor(&bitcoin_pks, &sha256::Hash::all_zeros())
90 .max_weight_to_satisfy()
91 .expect("Cannot satisfy the change descriptor.")
92 .to_wu();
93
94 let change_input_weight = 32 * 4 + 4 * 4 + 4 + 4 * 4 + change_witness_weight;
99
100 let change_output_weight = 8 * 4 + 4 + 34 * 4; let destination_output_weight = 8 * 4 + 4 + 34 * 4; Self {
109 bitcoin_pks,
110 descriptor: WalletDescriptor::Wsh,
111 send_tx_vbytes: weight_to_vbytes(
112 tx_overhead_weight
113 + change_input_weight
114 + change_output_weight
115 + destination_output_weight,
116 ),
117 receive_tx_vbytes: weight_to_vbytes(
118 tx_overhead_weight
119 + change_input_weight
120 + change_input_weight
121 + change_output_weight,
122 ),
123 feerate_base: 250,
128 dust_limit: bitcoin::Amount::from_sat(10_000),
129 fee_consensus,
130 network,
131 }
132 }
133}
134
135#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
136pub struct FeeConsensus {
137 pub base: Amount,
138 pub parts_per_million: u64,
139}
140
141impl FeeConsensus {
142 pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
153 anyhow::ensure!(
154 parts_per_million <= 10_000,
155 "Relative fee over ten thousand parts per million is excessive"
156 );
157
158 Ok(Self {
159 base: Amount::from_sats(100),
160 parts_per_million,
161 })
162 }
163
164 pub fn fee(&self, amount: Amount) -> Amount {
165 Amount::from_msats(self.fee_msats(amount.msats))
166 }
167
168 fn fee_msats(&self, msats: u64) -> u64 {
169 msats
170 .saturating_mul(self.parts_per_million)
171 .saturating_div(1_000_000)
172 .checked_add(self.base.msats)
173 .expect("The division creates sufficient headroom to add the base fee")
174 }
175}
176
177#[test]
178fn test_fee_consensus() {
179 let fee_consensus = FeeConsensus::new(10_000).expect("Relative fee is within range");
180
181 assert_eq!(
182 fee_consensus.fee(Amount::from_msats(99)),
183 Amount::from_sats(100)
184 );
185
186 assert_eq!(
187 fee_consensus.fee(Amount::from_sats(1)),
188 Amount::from_msats(10) + Amount::from_sats(100)
189 );
190
191 assert_eq!(
192 fee_consensus.fee(Amount::from_sats(1000)),
193 Amount::from_sats(10) + Amount::from_sats(100)
194 );
195
196 assert_eq!(
197 fee_consensus.fee(Amount::from_bitcoins(1)),
198 Amount::from_sats(1_000_000) + Amount::from_sats(100)
199 );
200
201 assert_eq!(
202 fee_consensus.fee(Amount::from_bitcoins(10_000)),
203 Amount::from_bitcoins(100) + Amount::from_sats(100)
204 );
205}
206
207#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
210pub enum WalletDescriptor {
211 Wsh,
212}
213
214#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
215pub struct WalletClientConfig {
216 pub bitcoin_pks: BTreeMap<PeerId, PublicKey>,
218 pub descriptor: WalletDescriptor,
220 pub send_tx_vbytes: u64,
222 pub receive_tx_vbytes: u64,
224 pub feerate_base: u64,
227 pub dust_limit: bitcoin::Amount,
229 pub fee_consensus: FeeConsensus,
231 pub network: Network,
233}
234
235impl std::fmt::Display for WalletClientConfig {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 write!(f, "WalletClientConfig {self:?}")
238 }
239}