1use std::collections::BTreeMap;
2
3use fedimint_core::config::EmptyGenParams;
4use fedimint_core::core::ModuleKind;
5use fedimint_core::encoding::{Decodable, Encodable};
6use fedimint_core::module::serde_json;
7use fedimint_core::{Amount, PeerId, Tiered, plugin_types_trait_impl_config};
8use serde::{Deserialize, Serialize};
9use tbs::{AggregatePublicKey, PublicKeyShare};
10
11use crate::MintCommonInit;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct MintGenParams {
15 pub local: EmptyGenParams,
16 pub consensus: MintGenParamsConsensus,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct MintGenParamsConsensus {
21 denomination_base: u16,
22 fee_consensus: FeeConsensus,
23}
24
25const MAX_DENOMINATION_SIZE: Amount = Amount::from_bitcoins(1_000_000);
29
30impl MintGenParamsConsensus {
31 pub fn new(denomination_base: u16, fee_consensus: FeeConsensus) -> Self {
32 Self {
33 denomination_base,
34 fee_consensus,
35 }
36 }
37
38 pub fn denomination_base(&self) -> u16 {
39 self.denomination_base
40 }
41
42 pub fn fee_consensus(&self) -> FeeConsensus {
43 self.fee_consensus.clone()
44 }
45
46 pub fn gen_denominations(&self) -> Vec<Amount> {
47 Tiered::gen_denominations(self.denomination_base, MAX_DENOMINATION_SIZE)
48 .tiers()
49 .copied()
50 .collect()
51 }
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize)]
55pub struct MintConfig {
56 pub local: MintConfigLocal,
57 pub private: MintConfigPrivate,
58 pub consensus: MintConfigConsensus,
59}
60
61#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
62pub struct MintConfigLocal;
63
64#[derive(Clone, Debug, Serialize, Deserialize, Encodable, Decodable)]
65pub struct MintConfigConsensus {
66 pub peer_tbs_pks: BTreeMap<PeerId, Tiered<PublicKeyShare>>,
69 pub fee_consensus: FeeConsensus,
71 pub max_notes_per_denomination: u16,
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize)]
76pub struct MintConfigPrivate {
77 pub tbs_sks: Tiered<tbs::SecretKeyShare>,
79}
80
81#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable, Hash)]
82pub struct MintClientConfig {
83 pub tbs_pks: Tiered<AggregatePublicKey>,
84 pub fee_consensus: FeeConsensus,
85 pub peer_tbs_pks: BTreeMap<PeerId, Tiered<tbs::PublicKeyShare>>,
86 pub max_notes_per_denomination: u16,
87}
88
89impl std::fmt::Display for MintClientConfig {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 write!(
92 f,
93 "MintClientConfig {}",
94 serde_json::to_string(self).map_err(|_e| std::fmt::Error)?
95 )
96 }
97}
98
99plugin_types_trait_impl_config!(
101 MintCommonInit,
102 MintGenParams,
103 EmptyGenParams,
104 MintGenParamsConsensus,
105 MintConfig,
106 MintConfigLocal,
107 MintConfigPrivate,
108 MintConfigConsensus,
109 MintClientConfig
110);
111
112#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
113pub struct FeeConsensus {
114 base: Amount,
115 parts_per_million: u64,
116}
117
118impl FeeConsensus {
119 pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
130 anyhow::ensure!(
131 parts_per_million <= 1_000,
132 "Relative fee over one thousand parts per million is excessive"
133 );
134
135 Ok(Self {
136 base: Amount::from_msats(100),
137 parts_per_million,
138 })
139 }
140
141 pub fn zero() -> Self {
142 Self {
143 base: Amount::ZERO,
144 parts_per_million: 0,
145 }
146 }
147
148 pub fn fee(&self, amount: Amount) -> Amount {
149 Amount::from_msats(self.fee_msats(amount.msats))
150 }
151
152 fn fee_msats(&self, msats: u64) -> u64 {
153 msats
154 .saturating_mul(self.parts_per_million)
155 .saturating_div(1_000_000)
156 .checked_add(self.base.msats)
157 .expect("The division creates sufficient headroom to add the base fee")
158 }
159}
160
161#[test]
162fn test_fee_consensus() {
163 let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
164
165 assert_eq!(
166 fee_consensus.fee(Amount::from_msats(999)),
167 Amount::from_msats(100)
168 );
169
170 assert_eq!(
171 fee_consensus.fee(Amount::from_sats(1)),
172 Amount::from_msats(100) + Amount::from_msats(1)
173 );
174
175 assert_eq!(
176 fee_consensus.fee(Amount::from_sats(1000)),
177 Amount::from_sats(1) + Amount::from_msats(100)
178 );
179
180 assert_eq!(
181 fee_consensus.fee(Amount::from_bitcoins(1)),
182 Amount::from_sats(100_000) + Amount::from_msats(100)
183 );
184
185 assert_eq!(
186 fee_consensus.fee(Amount::from_bitcoins(100_000)),
187 Amount::from_bitcoins(100) + Amount::from_msats(100)
188 );
189}