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