switchgear_testing/credentials/
lightning.rs

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