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 LightningConfig {
16    pub private: LightningConfigPrivate,
17    pub consensus: LightningConfigConsensus,
18}
19
20#[derive(Clone, Debug, Serialize, Deserialize, Decodable, Encodable)]
21pub struct LightningConfigLocal {
22    pub bitcoin_rpc: BitcoinRpcConfig,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, Encodable, Decodable)]
26pub struct LightningConfigConsensus {
27    pub tpe_agg_pk: AggregatePublicKey,
28    pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
29    pub fee_consensus: FeeConsensus,
30    pub network: Network,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct LightningConfigPrivate {
35    pub sk: SecretKeyShare,
36}
37
38#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
39pub struct LightningClientConfig {
40    pub tpe_agg_pk: AggregatePublicKey,
41    pub tpe_pks: BTreeMap<PeerId, PublicKeyShare>,
42    pub fee_consensus: FeeConsensus,
43    pub network: Network,
44}
45
46impl std::fmt::Display for LightningClientConfig {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "LightningClientConfig {self:?}")
49    }
50}
51
52// Wire together the configs for this module
53plugin_types_trait_impl_config!(
54    LightningCommonInit,
55    LightningConfig,
56    LightningConfigPrivate,
57    LightningConfigConsensus,
58    LightningClientConfig
59);
60
61#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
62pub struct FeeConsensus {
63    pub base: Amount,
64    pub parts_per_million: u64,
65}
66
67impl FeeConsensus {
68    /// The lightning module will charge a non-configurable base fee of one
69    /// satoshi per transaction input and output to account for the costs
70    /// incurred by the federation for processing the transaction. On top of
71    /// that the federation may charge a additional relative fee per input and
72    /// output of up to one thousand parts per million which is equal to one
73    /// tenth of one percent.
74    ///
75    /// # Errors
76    /// - This constructor returns an error if the relative fee is in excess of
77    ///   one thousand parts per million.
78    pub fn new(parts_per_million: u64) -> anyhow::Result<Self> {
79        anyhow::ensure!(
80            parts_per_million <= 1_000,
81            "Relative fee over one thousand parts per million is excessive"
82        );
83
84        Ok(Self {
85            base: Amount::from_sats(1),
86            parts_per_million,
87        })
88    }
89
90    pub fn zero() -> Self {
91        Self {
92            base: Amount::ZERO,
93            parts_per_million: 0,
94        }
95    }
96
97    pub fn fee(&self, amount: Amount) -> Amount {
98        Amount::from_msats(self.fee_msats(amount.msats))
99    }
100
101    fn fee_msats(&self, msats: u64) -> u64 {
102        msats
103            .saturating_mul(self.parts_per_million)
104            .saturating_div(1_000_000)
105            .checked_add(self.base.msats)
106            .expect("The division creates sufficient headroom to add the base fee")
107    }
108}
109
110#[test]
111fn test_fee_consensus() {
112    let fee_consensus = FeeConsensus::new(1_000).expect("Relative fee is within range");
113
114    assert_eq!(
115        fee_consensus.fee(Amount::from_msats(999)),
116        Amount::from_sats(1)
117    );
118
119    assert_eq!(
120        fee_consensus.fee(Amount::from_sats(1)),
121        Amount::from_msats(1) + Amount::from_sats(1)
122    );
123
124    assert_eq!(
125        fee_consensus.fee(Amount::from_sats(1000)),
126        Amount::from_sats(1) + Amount::from_sats(1)
127    );
128
129    assert_eq!(
130        fee_consensus.fee(Amount::from_bitcoins(1)),
131        Amount::from_sats(100_000) + Amount::from_sats(1)
132    );
133
134    assert_eq!(
135        fee_consensus.fee(Amount::from_bitcoins(100_000)),
136        Amount::from_bitcoins(100) + Amount::from_sats(1)
137    );
138}
139
140#[allow(dead_code)]
141fn migrate_config_consensus(
142    config: &fedimint_ln_common::config::LightningConfigConsensus,
143    peer_count: u16,
144) -> LightningConfigConsensus {
145    LightningConfigConsensus {
146        tpe_agg_pk: AggregatePublicKey(config.threshold_pub_keys.public_key().0.to_affine()),
147        tpe_pks: (0..peer_count)
148            .map(|peer| {
149                (
150                    PeerId::from(peer),
151                    PublicKeyShare(
152                        config
153                            .threshold_pub_keys
154                            .public_key_share(peer as usize)
155                            .0
156                            .0
157                            .to_affine(),
158                    ),
159                )
160            })
161            .collect(),
162        fee_consensus: FeeConsensus::new(1000).expect("Relative fee is within range"),
163        network: config.network.0,
164    }
165}
166
167#[allow(dead_code)]
168fn migrate_config_private(
169    config: &fedimint_ln_common::config::LightningConfigPrivate,
170) -> LightningConfigPrivate {
171    LightningConfigPrivate {
172        sk: SecretKeyShare(config.threshold_sec_key.0.0.0),
173    }
174}
175
176#[allow(dead_code)]
177fn migrate_config_local(
178    config: fedimint_ln_common::config::LightningConfigLocal,
179) -> LightningConfigLocal {
180    LightningConfigLocal {
181        bitcoin_rpc: config.bitcoin_rpc,
182    }
183}