use anyhow::{anyhow, Context, Result};
use log::trace;
use std::net::SocketAddr;
use tonic::transport;
#[derive(Clone, Debug)]
pub enum Network {
Bitcoin = 0,
Testnet = 1,
Regtest = 2,
}
impl TryFrom<i16> for Network {
type Error = anyhow::Error;
fn try_from(i: i16) -> Result<Network> {
match i {
0 => Ok(Network::Bitcoin),
1 => Ok(Network::Testnet),
2 => Ok(Network::Regtest),
e => Err(anyhow!("Unknown numeric network {}", e)),
}
}
}
impl TryFrom<String> for Network {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Network> {
match s.to_lowercase().as_ref() {
"bitcoin" => Ok(Network::Bitcoin),
"testnet" => Ok(Network::Testnet),
"regtest" => Ok(Network::Regtest),
o => Err(anyhow!("Unknown network {}", o)),
}
}
}
impl std::fmt::Display for Network {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"{}",
match *self {
Network::Bitcoin => "bitcoin",
Network::Testnet => "testnet",
Network::Regtest => "regtest",
}
)
}
}
#[derive(Clone, Debug)]
pub struct Identity {
pub id: transport::Identity,
pub ca: transport::Certificate,
}
impl Identity {
pub fn from_path(path: &str) -> Result<Identity> {
let mut dir = std::env::current_dir()?;
dir.push(std::env::var("GL_CERT_PATH").unwrap_or("./certs/".into()));
let mut dir = dir.canonicalize().with_context(|| {
format!(
"could not canonicalize GL_CERT_PATH={}",
dir.to_string_lossy()
)
})?;
let cacert = dir.join("ca.pem");
trace!("Loading root CA from {}", cacert.to_string_lossy());
let ca = std::fs::read(cacert).with_context(|| {
format!(
"Could not load CA certificate from {}",
dir.join("ca.pem").to_string_lossy()
)
})?;
for p in path.to_string().split("/").skip(1).collect::<Vec<&str>>() {
dir = dir.join(p);
}
let client_key_path = format!("{}-key.pem", dir.to_string_lossy());
let client_cert_path = format!("{}.crt", dir.to_string_lossy());
trace!(
"Loading identity from {} and certificate from {}",
client_key_path,
client_cert_path
);
let client_cert = std::fs::read(&client_cert_path).with_context(|| {
format!(
"could not read client certificate from {:?}",
client_cert_path
)
})?;
let client_key = std::fs::read(&client_key_path)
.with_context(|| format!("could not read client key from {:?}", client_key_path))?;
Ok(Identity {
id: tonic::transport::Identity::from_pem(client_cert, client_key),
ca: tonic::transport::Certificate::from_pem(ca),
})
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub identity: Identity,
pub hsmd_sock_path: String,
pub node_grpc_binding: String,
pub node_info: NodeInfo,
pub towerd_public_grpc_uri: Option<String>,
pub clientca: tonic::transport::Certificate,
pub network: Network,
pub node_config: NodeConfig,
}
impl Config {
pub fn new() -> Result<Self> {
let binding: SocketAddr = std::env::var("GL_NODE_BIND")
.context("Missing GL_NODE_BIND environment variable")?
.parse()
.context("Could not parse address from GL_BIND_NODE")?;
let towerd_public_grpc_uri: Option<String> = std::env::var("GL_TOWER_PUBLIC_GRPC_URI").ok();
let clientca_path: String = std::env::var("GL_PLUGIN_CLIENTCA_PATH")
.context("Missing GL_PLUGIN_CLIENTCA_PATH environment variable")?;
let identity = Identity::from_path(&"/users/1/server")?;
let clientca = tonic::transport::Certificate::from_pem(std::fs::read(clientca_path)?);
let network: Network = std::env::var("GL_NODE_NETWORK")
.context("Missing GL_NODE_NETWORK")?
.try_into()
.context("Unknown network in GL_NODE_NETWORK")?;
let mut cfg = std::env::current_dir()?;
cfg.push("node_config.pb");
let node_config = NodeConfig::from_file(cfg.as_path())?;
Ok(Config {
identity,
hsmd_sock_path: "hsmd.sock".to_string(),
node_grpc_binding: binding.to_string(),
node_info: NodeInfo::new()?,
towerd_public_grpc_uri,
clientca,
network,
node_config,
})
}
}
#[derive(Clone, Debug)]
pub struct NodeInfo {
pub node_id: Vec<u8>,
pub initmsg: Vec<u8>,
}
impl NodeInfo {
fn new() -> Result<Self> {
let node_id = match std::env::var("GL_NODE_ID") {
Ok(v) => hex::decode(v)?,
Err(_) => return Err(anyhow!("Environment variable GL_NODE_ID is not set")),
};
let initmsg = hex::decode(std::env::var("GL_NODE_INIT").context("Missing GL_NODE_INIT")?)
.context("Malformed GL_NODE_INIT")?;
if node_id.len() != 33 {
return Err(anyhow!(
"GL_NODE_ID is not a 33 byte hex-encoded public-key",
));
}
Ok(NodeInfo { node_id, initmsg })
}
}
pub use crate::pb::NodeConfig;
impl NodeConfig {
pub fn from_file(f: &std::path::Path) -> Result<NodeConfig, anyhow::Error> {
log::debug!("Loading node_config from {}", f.display());
use prost::Message;
let contents = std::fs::read(f)?;
NodeConfig::decode(&contents[..]).context("decoding protobuf payload")
}
}