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: 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: 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 base: Amount,
97 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 fee(&self, amount: Amount) -> Amount {
124 Amount::from_msats(self.fee_msats(amount.msats))
125 }
126
127 fn fee_msats(&self, msats: u64) -> u64 {
128 msats
129 .saturating_mul(self.parts_per_million)
130 .saturating_div(1_000_000)
131 .checked_add(self.base.msats)
132 .expect("The division creates sufficient headroom to add the base fee")
133 }
134}
135
136#[test]
137fn test_fee_consensus() {
138 let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
139
140 assert_eq!(
141 fee_consensus.fee(Amount::from_msats(999)),
142 Amount::from_sats(1)
143 );
144
145 assert_eq!(
146 fee_consensus.fee(Amount::from_sats(1)),
147 Amount::from_msats(1) + Amount::from_sats(1)
148 );
149
150 assert_eq!(
151 fee_consensus.fee(Amount::from_sats(1000)),
152 Amount::from_sats(1) + Amount::from_sats(1)
153 );
154
155 assert_eq!(
156 fee_consensus.fee(Amount::from_bitcoins(1)),
157 Amount::from_sats(100_000) + Amount::from_sats(1)
158 );
159
160 assert_eq!(
161 fee_consensus.fee(Amount::from_bitcoins(100_000)),
162 Amount::from_bitcoins(100) + Amount::from_sats(1)
163 );
164}
165
166#[allow(dead_code)]
167fn migrate_config_consensus(
168 config: &fedimint_ln_common::config::LightningConfigConsensus,
169 peer_count: u16,
170) -> LightningConfigConsensus {
171 LightningConfigConsensus {
172 tpe_agg_pk: AggregatePublicKey(config.threshold_pub_keys.public_key().0.to_affine()),
173 tpe_pks: (0..peer_count)
174 .map(|peer| {
175 (
176 PeerId::from(peer),
177 PublicKeyShare(
178 config
179 .threshold_pub_keys
180 .public_key_share(peer as usize)
181 .0
182 .0
183 .to_affine(),
184 ),
185 )
186 })
187 .collect(),
188 fee_consensus: FeeConsensus::new(1000).expect("Relative fee is within range"),
189 network: config.network.0,
190 }
191}
192
193#[allow(dead_code)]
194fn migrate_config_private(
195 config: &fedimint_ln_common::config::LightningConfigPrivate,
196) -> LightningConfigPrivate {
197 LightningConfigPrivate {
198 sk: SecretKeyShare(config.threshold_sec_key.0.0.0),
199 }
200}
201
202#[allow(dead_code)]
203fn migrate_config_local(
204 config: fedimint_ln_common::config::LightningConfigLocal,
205) -> LightningConfigLocal {
206 LightningConfigLocal {
207 bitcoin_rpc: config.bitcoin_rpc,
208 }
209}