switchgear_testing/credentials/
lightning.rs1use 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}