fedimint_lnv2_common/
config.rs

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
82// Wire together the configs for this module
83plugin_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    /// The lightning module will charge a non-configurable base fee of one
102    /// satoshi per transaction input and output to account for the costs
103    /// incurred by the federation for processing the transaction. On top of
104    /// that the federation may charge a additional relative fee per input and
105    /// output of up to one thousand parts per million which is equal to one
106    /// tenth of one percent.
107    ///
108    /// # Errors
109    /// - This constructor returns an error if the relative fee is in excess of
110    ///   one thousand parts per million.
111    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}