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