switchgear_testing/credentials/
lightning.rs

1use crate::credentials::download_credentials;
2use crate::services::{IntegrationTestServices, LightningIntegrationTestServices};
3use anyhow::Context;
4use secp256k1::PublicKey;
5use std::fs;
6use std::path::{Path, PathBuf};
7use tempfile::TempDir;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct ClnRegTestLnNode {
11    pub public_key: PublicKey,
12    pub address: String,
13    pub ca_cert_path: PathBuf,
14    pub client_cert_path: PathBuf,
15    pub client_key_path: PathBuf,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct LndRegTestLnNode {
20    pub public_key: PublicKey,
21    pub address: String,
22    pub tls_cert_path: PathBuf,
23    pub macaroon_path: PathBuf,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27pub enum RegTestLnNode {
28    Cln(ClnRegTestLnNode),
29    Lnd(LndRegTestLnNode),
30}
31
32impl RegTestLnNode {
33    pub fn public_key(&self) -> &PublicKey {
34        match self {
35            RegTestLnNode::Cln(cln) => &cln.public_key,
36            RegTestLnNode::Lnd(lnd) => &lnd.public_key,
37        }
38    }
39
40    pub fn address(&self) -> &str {
41        match self {
42            RegTestLnNode::Cln(cln) => &cln.address,
43            RegTestLnNode::Lnd(lnd) => &lnd.address,
44        }
45    }
46
47    pub fn kind(&self) -> &'static str {
48        match self {
49            RegTestLnNode::Cln(_) => "cln",
50            RegTestLnNode::Lnd(_) => "lnd",
51        }
52    }
53}
54
55#[derive(Copy, Clone, Debug)]
56pub enum RegTestLnNodeType {
57    Cln,
58    Lnd,
59}
60
61pub struct LnCredentials {
62    inner: Option<LnCredentialsInner>,
63}
64
65struct LnCredentialsInner {
66    credentials_dir: TempDir,
67    lightning: LightningIntegrationTestServices,
68}
69
70impl LnCredentials {
71    pub fn create() -> anyhow::Result<Self> {
72        let services = IntegrationTestServices::create()?;
73        let inner = match (services.credentials(), services.lightning()) {
74            (Some(credentials), Some(lightning)) => {
75                let credentials_dir = TempDir::new()?;
76                download_credentials(credentials_dir.path(), credentials)?;
77                Some(LnCredentialsInner {
78                    credentials_dir,
79                    lightning: lightning.clone(),
80                })
81            }
82            _ => None,
83        };
84        Ok(Self { inner })
85    }
86
87    pub fn get_backends(&self) -> anyhow::Result<Vec<RegTestLnNode>> {
88        let inner = match &self.inner {
89            None => return Ok(vec![]),
90            Some(inner) => inner,
91        };
92
93        let credentials = inner.credentials_dir.path().join("credentials");
94        let base_path = credentials.as_path();
95
96        let entries = fs::read_dir(base_path)
97            .with_context(|| format!("reading directory {}", base_path.display()))?;
98
99        let mut backends = Vec::new();
100
101        for entry in entries {
102            let entry = entry
103                .with_context(|| format!("reading directory entry in {}", base_path.display(),))?;
104
105            let path = entry.path();
106
107            if !path.is_dir() {
108                continue;
109            }
110
111            let dir_name = match path.file_name() {
112                Some(name) => match name.to_str() {
113                    Some(s) => s,
114                    None => continue,
115                },
116                None => continue,
117            };
118
119            let node_type = if dir_name.starts_with("cln") {
120                RegTestLnNodeType::Cln
121            } else if dir_name.starts_with("lnd") {
122                RegTestLnNodeType::Lnd
123            } else {
124                continue;
125            };
126
127            let node_id_path = path.join("node_id");
128            let node_id_str = fs::read_to_string(&node_id_path)
129                .with_context(|| format!("reading node ID from {}", node_id_path.display(),))?;
130
131            let node_id_hex = node_id_str.trim();
132            let node_id_bytes = hex::decode(node_id_hex)
133                .with_context(|| format!("decoding {} node ID to hex", node_id_path.display(),))?;
134
135            let public_key = PublicKey::from_slice(&node_id_bytes).with_context(|| {
136                format!("parsing {} public key from bytes", node_id_path.display(),)
137            })?;
138
139            let node = match node_type {
140                RegTestLnNodeType::Cln => RegTestLnNode::Cln(Self::build_cln_node(
141                    public_key,
142                    &inner.lightning.cln,
143                    &path,
144                )?),
145                RegTestLnNodeType::Lnd => RegTestLnNode::Lnd(Self::build_lnd_node(
146                    public_key,
147                    &inner.lightning.lnd,
148                    &path,
149                )?),
150            };
151
152            backends.push(node);
153        }
154
155        Ok(backends)
156    }
157
158    fn build_cln_node(
159        public_key: PublicKey,
160        address: &str,
161        node_path: &Path,
162    ) -> anyhow::Result<ClnRegTestLnNode> {
163        let ca_cert_path = node_path.join("ca.pem");
164        let ca_cert_path = ca_cert_path.canonicalize().with_context(|| {
165            format!("canonicalizing CLN CA cert path {}", ca_cert_path.display(),)
166        })?;
167        let client_cert_path = node_path.join("client.pem");
168        let client_cert_path = client_cert_path.canonicalize().with_context(|| {
169            format!(
170                "canonicalizing CLN client cert path {}",
171                client_cert_path.display(),
172            )
173        })?;
174        let client_key_path = node_path.join("client-key.pem");
175        let client_key_path = client_key_path.canonicalize().with_context(|| {
176            format!(
177                "canonicalizing CLN client key path {}",
178                client_key_path.display(),
179            )
180        })?;
181
182        Ok(ClnRegTestLnNode {
183            public_key,
184            address: address.to_string(),
185            ca_cert_path,
186            client_cert_path,
187            client_key_path,
188        })
189    }
190
191    fn build_lnd_node(
192        public_key: PublicKey,
193        address: &str,
194        node_path: &Path,
195    ) -> anyhow::Result<LndRegTestLnNode> {
196        let tls_cert_path = node_path.join("tls.cert");
197        let tls_cert_path = tls_cert_path.canonicalize().with_context(|| {
198            format!(
199                "canonicalizing LND TLS cert path {}",
200                tls_cert_path.display(),
201            )
202        })?;
203        let macaroon_path = node_path.join("admin.macaroon");
204        let macaroon_path = macaroon_path.canonicalize().with_context(|| {
205            format!(
206                "canonicalizing LND macaroon path {}",
207                macaroon_path.display(),
208            )
209        })?;
210
211        Ok(LndRegTestLnNode {
212            public_key,
213            address: address.to_string(),
214            tls_cert_path,
215            macaroon_path,
216        })
217    }
218}