fedimint_mint_common/
config.rs1use std::collections::BTreeMap;
2
3use fedimint_core::core::ModuleKind;
4use fedimint_core::encoding::{Decodable, Encodable};
5use fedimint_core::module::serde_json;
6use fedimint_core::{Amount, PeerId, Tiered, plugin_types_trait_impl_config};
7use serde::{Deserialize, Serialize};
8use tbs::{AggregatePublicKey, PublicKeyShare};
9
10use crate::MintCommonInit;
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct MintConfig {
14 pub private: MintConfigPrivate,
15 pub consensus: MintConfigConsensus,
16}
17
18#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
19pub struct MintConfigLocal;
20
21#[derive(Clone, Debug, Serialize, Deserialize, Encodable, Decodable)]
22pub struct MintConfigConsensus {
23 pub peer_tbs_pks: BTreeMap<PeerId, Tiered<PublicKeyShare>>,
26 pub fee_consensus: FeeConsensus,
28 pub max_notes_per_denomination: u16,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
33pub struct MintConfigPrivate {
34 pub tbs_sks: Tiered<tbs::SecretKeyShare>,
36}
37
38#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable, Hash)]
39pub struct MintClientConfig {
40 pub tbs_pks: Tiered<AggregatePublicKey>,
41 pub fee_consensus: FeeConsensus,
42 pub peer_tbs_pks: BTreeMap<PeerId, Tiered<tbs::PublicKeyShare>>,
43 pub max_notes_per_denomination: u16,
44}
45
46impl std::fmt::Display for MintClientConfig {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(
49 f,
50 "MintClientConfig {}",
51 serde_json::to_string(self).map_err(|_e| std::fmt::Error)?
52 )
53 }
54}
55
56plugin_types_trait_impl_config!(
58 MintCommonInit,
59 MintConfig,
60 MintConfigPrivate,
61 MintConfigConsensus,
62 MintClientConfig
63);
64
65#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
66pub struct FeeConsensus {
67 base: Amount,
68 parts_per_million: u64,
69}
70
71impl FeeConsensus {
72 pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
83 anyhow::ensure!(
84 parts_per_million <= 1_000,
85 "Relative fee over one thousand parts per million is excessive"
86 );
87
88 Ok(Self {
89 base: Amount::from_msats(100),
90 parts_per_million,
91 })
92 }
93
94 pub fn zero() -> Self {
95 Self {
96 base: Amount::ZERO,
97 parts_per_million: 0,
98 }
99 }
100
101 pub fn fee(&self, amount: Amount) -> Amount {
102 Amount::from_msats(self.fee_msats(amount.msats))
103 }
104
105 fn fee_msats(&self, msats: u64) -> u64 {
106 msats
107 .saturating_mul(self.parts_per_million)
108 .saturating_div(1_000_000)
109 .checked_add(self.base.msats)
110 .expect("The division creates sufficient headroom to add the base fee")
111 }
112}
113
114#[test]
115fn test_fee_consensus() {
116 let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
117
118 assert_eq!(
119 fee_consensus.fee(Amount::from_msats(999)),
120 Amount::from_msats(100)
121 );
122
123 assert_eq!(
124 fee_consensus.fee(Amount::from_sats(1)),
125 Amount::from_msats(100) + Amount::from_msats(1)
126 );
127
128 assert_eq!(
129 fee_consensus.fee(Amount::from_sats(1000)),
130 Amount::from_sats(1) + Amount::from_msats(100)
131 );
132
133 assert_eq!(
134 fee_consensus.fee(Amount::from_bitcoins(1)),
135 Amount::from_sats(100_000) + Amount::from_msats(100)
136 );
137
138 assert_eq!(
139 fee_consensus.fee(Amount::from_bitcoins(100_000)),
140 Amount::from_bitcoins(100) + Amount::from_msats(100)
141 );
142}