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