1use std::collections::BTreeMap;
2
3pub use bitcoin::Network;
4use fedimint_core::config::ExcessiveRelativeFeeError;
5use fedimint_core::core::ModuleKind;
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::envs::BitcoinRpcConfig;
8use fedimint_core::{Amount, PeerId, plugin_types_trait_impl_config};
9use group::Curve;
10use serde::{Deserialize, Serialize};
11use tpe::{AggregatePublicKey, PublicKeyShare, SecretKeyShare};
12
13use crate::LightningCommonInit;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct LightningConfig {
17 pub private: LightningConfigPrivate,
18 pub consensus: LightningConfigConsensus,
19}
20
21#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
22pub struct LightningConfigLocal {
23 pub bitcoin_rpc: BitcoinRpcConfig,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, Encodable, Decodable)]
27pub struct LightningConfigConsensus {
28 pub tpe_agg_pk: AggregatePublicKey,
29 pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
30 pub fee_consensus: FeeConsensus,
31 pub network: Network,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct LightningConfigPrivate {
36 pub sk: SecretKeyShare,
37}
38
39#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
40pub struct LightningClientConfig {
41 pub tpe_agg_pk: AggregatePublicKey,
42 pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
43 pub fee_consensus: FeeConsensus,
44 pub network: Network,
45}
46
47impl std::fmt::Display for LightningClientConfig {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 write!(f, "LightningClientConfig {self:?}")
50 }
51}
52
53plugin_types_trait_impl_config!(
55 LightningCommonInit,
56 LightningConfig,
57 LightningConfigPrivate,
58 LightningConfigConsensus,
59 LightningClientConfig
60);
61
62#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
63pub struct FeeConsensus {
64 pub base: Amount,
65 pub parts_per_million: u64,
66}
67
68impl FeeConsensus {
69 pub fn new(parts_per_million: u64) -> Result<Self, ExcessiveRelativeFeeError> {
80 const MAX_PARTS_PER_MILLION: u64 = 1_000;
81
82 if parts_per_million > MAX_PARTS_PER_MILLION {
83 return Err(ExcessiveRelativeFeeError {
84 parts_per_million,
85 max: MAX_PARTS_PER_MILLION,
86 });
87 }
88
89 Ok(Self {
90 base: Amount::from_sats(1),
91 parts_per_million,
92 })
93 }
94
95 pub fn zero() -> Self {
96 Self {
97 base: Amount::ZERO,
98 parts_per_million: 0,
99 }
100 }
101
102 pub fn fee(&self, amount: Amount) -> Amount {
103 Amount::from_msats(self.fee_msats(amount.msats))
104 }
105
106 fn fee_msats(&self, msats: u64) -> u64 {
107 msats
108 .saturating_mul(self.parts_per_million)
109 .saturating_div(1_000_000)
110 .checked_add(self.base.msats)
111 .expect("The division creates sufficient headroom to add the base fee")
112 }
113}
114
115#[test]
116fn test_fee_consensus() {
117 let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
118
119 assert_eq!(
120 fee_consensus.fee(Amount::from_msats(999)),
121 Amount::from_sats(1)
122 );
123
124 assert_eq!(
125 fee_consensus.fee(Amount::from_sats(1)),
126 Amount::from_msats(1) + Amount::from_sats(1)
127 );
128
129 assert_eq!(
130 fee_consensus.fee(Amount::from_sats(1000)),
131 Amount::from_sats(1) + Amount::from_sats(1)
132 );
133
134 assert_eq!(
135 fee_consensus.fee(Amount::from_bitcoins(1)),
136 Amount::from_sats(100_000) + Amount::from_sats(1)
137 );
138
139 assert_eq!(
140 fee_consensus.fee(Amount::from_bitcoins(100_000)),
141 Amount::from_bitcoins(100) + Amount::from_sats(1)
142 );
143}
144
145#[test]
146fn a_relative_fee_over_the_limit_is_rejected() {
147 let err: ExcessiveRelativeFeeError =
148 FeeConsensus::new(1_001).expect_err("A fee over one per mille is excessive");
149
150 assert_eq!(err.parts_per_million, 1_001);
151 assert_eq!(err.max, 1_000);
152}
153
154#[allow(dead_code)]
155fn migrate_config_consensus(
156 config: &fedimint_ln_common::config::LightningConfigConsensus,
157 peer_count: u16,
158) -> LightningConfigConsensus {
159 LightningConfigConsensus {
160 tpe_agg_pk: AggregatePublicKey(config.threshold_pub_keys.public_key().0.to_affine()),
161 tpe_pks: (0..peer_count)
162 .map(|peer| {
163 (
164 PeerId::from(peer),
165 PublicKeyShare(
166 config
167 .threshold_pub_keys
168 .public_key_share(peer as usize)
169 .0
170 .0
171 .to_affine(),
172 ),
173 )
174 })
175 .collect(),
176 fee_consensus: FeeConsensus::new(1000).expect("Relative fee is within range"),
177 network: config.network.0,
178 }
179}
180
181#[allow(dead_code)]
182fn migrate_config_private(
183 config: &fedimint_ln_common::config::LightningConfigPrivate,
184) -> LightningConfigPrivate {
185 LightningConfigPrivate {
186 sk: SecretKeyShare(config.threshold_sec_key.0.0.0),
187 }
188}
189
190#[allow(dead_code)]
191fn migrate_config_local(
192 config: fedimint_ln_common::config::LightningConfigLocal,
193) -> LightningConfigLocal {
194 LightningConfigLocal {
195 bitcoin_rpc: config.bitcoin_rpc,
196 }
197}