1use anyhow::{anyhow, Context, Result};
2use log::trace;
3use std::net::SocketAddr;
4use tonic::transport;
5
6#[derive(Clone, Debug)]
8pub enum Network {
9 Bitcoin = 0,
10 Testnet = 1,
11 Regtest = 2,
12}
13
14impl TryFrom<i16> for Network {
15 type Error = anyhow::Error;
16
17 fn try_from(i: i16) -> Result<Network> {
18 match i {
19 0 => Ok(Network::Bitcoin),
20 1 => Ok(Network::Testnet),
21 2 => Ok(Network::Regtest),
22 e => Err(anyhow!("Unknown numeric network {}", e)),
23 }
24 }
25}
26
27impl TryFrom<String> for Network {
28 type Error = anyhow::Error;
29
30 fn try_from(s: String) -> Result<Network> {
31 match s.to_lowercase().as_ref() {
32 "bitcoin" => Ok(Network::Bitcoin),
33 "testnet" => Ok(Network::Testnet),
34 "regtest" => Ok(Network::Regtest),
35 o => Err(anyhow!("Unknown network {}", o)),
36 }
37 }
38}
39impl std::fmt::Display for Network {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
41 write!(
42 f,
43 "{}",
44 match *self {
45 Network::Bitcoin => "bitcoin",
46 Network::Testnet => "testnet",
47 Network::Regtest => "regtest",
48 }
49 )
50 }
51}
52
53#[derive(Clone, Debug)]
54pub struct Identity {
55 pub id: transport::Identity,
56 pub ca: transport::Certificate,
57}
58
59impl Identity {
60 pub fn from_path(path: &str) -> Result<Identity> {
71 let mut dir = std::env::current_dir()?;
72 dir.push(std::env::var("GL_CERT_PATH").unwrap_or("./certs/".into()));
73 let mut dir = dir.canonicalize().with_context(|| {
74 format!(
75 "could not canonicalize GL_CERT_PATH={}",
76 dir.to_string_lossy()
77 )
78 })?;
79
80 let cacert = dir.join("ca.pem");
81 trace!("Loading root CA from {}", cacert.to_string_lossy());
82 let ca = std::fs::read(cacert).with_context(|| {
84 format!(
85 "Could not load CA certificate from {}",
86 dir.join("ca.pem").to_string_lossy()
87 )
88 })?;
89
90 for p in path.to_string().split("/").skip(1).collect::<Vec<&str>>() {
92 dir = dir.join(p);
93 }
94
95 let client_key_path = format!("{}-key.pem", dir.to_string_lossy());
96 let client_cert_path = format!("{}.crt", dir.to_string_lossy());
97
98 trace!(
99 "Loading identity from {} and certificate from {}",
100 client_key_path,
101 client_cert_path
102 );
103 let client_cert = std::fs::read(&client_cert_path).with_context(|| {
104 format!(
105 "could not read client certificate from {:?}",
106 client_cert_path
107 )
108 })?;
109
110 let client_key = std::fs::read(&client_key_path)
111 .with_context(|| format!("could not read client key from {:?}", client_key_path))?;
112
113 Ok(Identity {
114 id: tonic::transport::Identity::from_pem(client_cert, client_key),
115 ca: tonic::transport::Certificate::from_pem(ca),
116 })
117 }
118}
119
120#[derive(Clone, Debug)]
121pub struct Config {
122 pub identity: Identity,
123 pub hsmd_sock_path: String,
124 pub node_grpc_binding: String,
125 pub node_info: NodeInfo,
126 pub towerd_public_grpc_uri: Option<String>,
127
128 pub clientca: tonic::transport::Certificate,
134
135 pub network: Network,
137
138 pub node_config: NodeConfig,
139}
140
141impl Config {
142 pub fn new() -> Result<Self> {
143 let binding: SocketAddr = std::env::var("GL_NODE_BIND")
144 .context("Missing GL_NODE_BIND environment variable")?
145 .parse()
146 .context("Could not parse address from GL_BIND_NODE")?;
147
148 let towerd_public_grpc_uri: Option<String> = std::env::var("GL_TOWER_PUBLIC_GRPC_URI").ok();
149
150 let clientca_path: String = std::env::var("GL_PLUGIN_CLIENTCA_PATH")
151 .context("Missing GL_PLUGIN_CLIENTCA_PATH environment variable")?;
152
153 let identity = Identity::from_path(&"/users/1/server")?;
154
155 let clientca = tonic::transport::Certificate::from_pem(std::fs::read(clientca_path)?);
156 let network: Network = std::env::var("GL_NODE_NETWORK")
157 .context("Missing GL_NODE_NETWORK")?
158 .try_into()
159 .context("Unknown network in GL_NODE_NETWORK")?;
160
161 let mut cfg = std::env::current_dir()?;
162 cfg.push("node_config.pb");
163 let node_config = NodeConfig::from_file(cfg.as_path())?;
164
165 Ok(Config {
166 identity,
167 hsmd_sock_path: "hsmd.sock".to_string(),
168 node_grpc_binding: binding.to_string(),
169 node_info: NodeInfo::new()?,
170 towerd_public_grpc_uri,
171 clientca,
172 network,
173 node_config,
174 })
175 }
176}
177
178#[derive(Clone, Debug)]
179pub struct NodeInfo {
180 pub node_id: Vec<u8>,
181 pub initmsg: Vec<u8>,
182}
183
184impl NodeInfo {
185 fn new() -> Result<Self> {
186 let node_id = match std::env::var("GL_NODE_ID") {
187 Ok(v) => hex::decode(v)?,
188 Err(_) => return Err(anyhow!("Environment variable GL_NODE_ID is not set")),
189 };
190
191 let initmsg = hex::decode(std::env::var("GL_NODE_INIT").context("Missing GL_NODE_INIT")?)
192 .context("Malformed GL_NODE_INIT")?;
193
194 if node_id.len() != 33 {
195 return Err(anyhow!(
196 "GL_NODE_ID is not a 33 byte hex-encoded public-key",
197 ));
198 }
199
200 Ok(NodeInfo { node_id, initmsg })
201 }
202}
203
204pub use crate::pb::NodeConfig;
205
206impl NodeConfig {
207 pub fn from_file(f: &std::path::Path) -> Result<NodeConfig, anyhow::Error> {
208 log::debug!("Loading node_config from {}", f.display());
209 use prost::Message;
210 let contents = std::fs::read(f)?;
211 NodeConfig::decode(&contents[..]).context("decoding protobuf payload")
212 }
213}