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 local: LightningConfigLocal,
47 pub private: LightningConfigPrivate,
48 pub consensus: LightningConfigConsensus,
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
52pub struct LightningConfigLocal {
53 pub bitcoin_rpc: BitcoinRpcConfig,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, Encodable, Decodable)]
57pub struct LightningConfigConsensus {
58 pub tpe_agg_pk: AggregatePublicKey,
59 pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
60 pub fee_consensus: FeeConsensus,
61 pub network: Network,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct LightningConfigPrivate {
66 pub sk: SecretKeyShare,
67}
68
69#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
70pub struct LightningClientConfig {
71 pub tpe_agg_pk: AggregatePublicKey,
72 pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
73 pub fee_consensus: FeeConsensus,
74 pub network: Network,
75}
76
77impl std::fmt::Display for LightningClientConfig {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "LightningClientConfig {self:?}")
80 }
81}
82
83plugin_types_trait_impl_config!(
85 LightningCommonInit,
86 LightningGenParams,
87 LightningGenParamsLocal,
88 LightningGenParamsConsensus,
89 LightningConfig,
90 LightningConfigLocal,
91 LightningConfigPrivate,
92 LightningConfigConsensus,
93 LightningClientConfig
94);
95
96#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
97pub struct FeeConsensus {
98 base: Amount,
99 parts_per_million: u64,
100}
101
102impl FeeConsensus {
103 pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
114 anyhow::ensure!(
115 parts_per_million <= 1_000,
116 "Relative fee over one thousand parts per million is excessive"
117 );
118
119 Ok(Self {
120 base: Amount::from_sats(1),
121 parts_per_million,
122 })
123 }
124
125 pub fn fee(&self, amount: Amount) -> Amount {
126 Amount::from_msats(self.fee_msats(amount.msats))
127 }
128
129 fn fee_msats(&self, msats: u64) -> u64 {
130 msats
131 .saturating_mul(self.parts_per_million)
132 .saturating_div(1_000_000)
133 .checked_add(self.base.msats)
134 .expect("The division creates sufficient headroom to add the base fee")
135 }
136}
137
138#[test]
139fn test_fee_consensus() {
140 let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
141
142 assert_eq!(
143 fee_consensus.fee(Amount::from_msats(999)),
144 Amount::from_sats(1)
145 );
146
147 assert_eq!(
148 fee_consensus.fee(Amount::from_sats(1)),
149 Amount::from_msats(1) + Amount::from_sats(1)
150 );
151
152 assert_eq!(
153 fee_consensus.fee(Amount::from_sats(1000)),
154 Amount::from_sats(1) + Amount::from_sats(1)
155 );
156
157 assert_eq!(
158 fee_consensus.fee(Amount::from_bitcoins(1)),
159 Amount::from_sats(100_000) + Amount::from_sats(1)
160 );
161
162 assert_eq!(
163 fee_consensus.fee(Amount::from_bitcoins(100_000)),
164 Amount::from_bitcoins(100) + Amount::from_sats(1)
165 );
166}
167
168#[allow(dead_code)]
169fn migrate_config_consensus(
170 config: &fedimint_ln_common::config::LightningConfigConsensus,
171 peer_count: u16,
172) -> LightningConfigConsensus {
173 LightningConfigConsensus {
174 tpe_agg_pk: AggregatePublicKey(config.threshold_pub_keys.public_key().0.to_affine()),
175 tpe_pks: (0..peer_count)
176 .map(|peer| {
177 (
178 PeerId::from(peer),
179 PublicKeyShare(
180 config
181 .threshold_pub_keys
182 .public_key_share(peer as usize)
183 .0
184 .0
185 .to_affine(),
186 ),
187 )
188 })
189 .collect(),
190 fee_consensus: FeeConsensus::new(1000).expect("Relative fee is within range"),
191 network: config.network.0,
192 }
193}
194
195#[allow(dead_code)]
196fn migrate_config_private(
197 config: &fedimint_ln_common::config::LightningConfigPrivate,
198) -> LightningConfigPrivate {
199 LightningConfigPrivate {
200 sk: SecretKeyShare(config.threshold_sec_key.0.0.0),
201 }
202}
203
204#[allow(dead_code)]
205fn migrate_config_local(
206 config: fedimint_ln_common::config::LightningConfigLocal,
207) -> LightningConfigLocal {
208 LightningConfigLocal {
209 bitcoin_rpc: config.bitcoin_rpc,
210 }
211}