Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cargo fmt #43

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn main() -> std::io::Result<()> {
let mut lnd_rpc_dir = PathBuf::from(lnd_repo_path);
lnd_rpc_dir.push("lnrpc");
lnd_rpc_dir
},
}
None => PathBuf::from("vendor"),
};

Expand Down
8 changes: 6 additions & 2 deletions examples/getinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
async fn main() {
let mut args = std::env::args_os();
args.next().expect("not even zeroth arg given");
let address = args.next().expect("missing arguments: address, cert file, macaroon file");
let cert_file = args.next().expect("missing arguments: cert file, macaroon file");
let address = args
.next()
.expect("missing arguments: address, cert file, macaroon file");
let cert_file = args
.next()
.expect("missing arguments: cert file, macaroon file");
let macaroon_file = args.next().expect("missing argument: macaroon file");
let address = address.into_string().expect("address is not UTF-8");

Expand Down
8 changes: 6 additions & 2 deletions examples/getversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
async fn main() {
let mut args = std::env::args_os();
args.next().expect("not even zeroth arg given");
let address = args.next().expect("missing arguments: address, cert file, macaroon file");
let cert_file = args.next().expect("missing arguments: cert file, macaroon file");
let address = args
.next()
.expect("missing arguments: address, cert file, macaroon file");
let cert_file = args
.next()
.expect("missing arguments: cert file, macaroon file");
let macaroon_file = args.next().expect("missing argument: macaroon file");
let address = address.into_string().expect("address is not UTF-8");

Expand Down
24 changes: 17 additions & 7 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,29 @@ pub struct ConnectError {

impl From<InternalConnectError> for ConnectError {
fn from(value: InternalConnectError) -> Self {
ConnectError {
internal: value,
}
ConnectError { internal: value }
}
}

#[derive(Debug)]
pub(crate) enum InternalConnectError {
ReadFile { file: PathBuf, error: std::io::Error, },
ParseCert { file: PathBuf, error: std::io::Error, },
InvalidAddress { address: String, error: Box<dyn std::error::Error + Send + Sync + 'static>, },
ReadFile {
file: PathBuf,
error: std::io::Error,
},
ParseCert {
file: PathBuf,
error: std::io::Error,
},
InvalidAddress {
address: String,
error: Box<dyn std::error::Error + Send + Sync + 'static>,
},
TlsConfig(tonic::transport::Error),
Connect { address: String, error: tonic::transport::Error, }
Connect {
address: String,
error: tonic::transport::Error,
},
}

impl fmt::Display for ConnectError {
Expand Down
154 changes: 108 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ MITNFA
/// This is part of public interface so it's re-exported.
pub extern crate tonic;

use std::path::{Path, PathBuf};
use std::convert::TryInto;
pub use error::ConnectError;

use std::convert::TryInto;
use std::path::{Path, PathBuf};

use error::InternalConnectError;
use tonic::codegen::InterceptedService;
use tonic::transport::Channel;
Expand All @@ -81,7 +83,8 @@ use tonic::transport::Channel;
use tracing;

/// Convenience type alias for lightning client.
pub type LightningClient = lnrpc::lightning_client::LightningClient<InterceptedService<Channel, MacaroonInterceptor>>;
pub type LightningClient =
lnrpc::lightning_client::LightningClient<InterceptedService<Channel, MacaroonInterceptor>>;

/// Convenience type alias for wallet client.
pub type WalletKitClient =
Expand All @@ -96,7 +99,8 @@ pub type VersionerClient =
verrpc::versioner_client::VersionerClient<InterceptedService<Channel, MacaroonInterceptor>>;

// Convenience type alias for signer client.
pub type SignerClient = signrpc::signer_client::SignerClient<InterceptedService<Channel, MacaroonInterceptor>>;
pub type SignerClient =
signrpc::signer_client::SignerClient<InterceptedService<Channel, MacaroonInterceptor>>;

/// The client returned by `connect` function
///
Expand Down Expand Up @@ -148,7 +152,7 @@ macro_rules! try_map_err {
Ok(value) => value,
Err(error) => return Err($mapfn(error).into()),
}
}
};
}

/// Messages and other types generated by `tonic`/`prost`
Expand Down Expand Up @@ -183,17 +187,25 @@ pub struct MacaroonInterceptor {

impl tonic::service::Interceptor for MacaroonInterceptor {
fn call(&mut self, mut request: tonic::Request<()>) -> Result<tonic::Request<()>, Error> {
request
.metadata_mut()
.insert("macaroon", tonic::metadata::MetadataValue::from_str(&self.macaroon).expect("hex produced non-ascii"));
request.metadata_mut().insert(
"macaroon",
tonic::metadata::MetadataValue::from_str(&self.macaroon)
.expect("hex produced non-ascii"),
);
Ok(request)
}
}

async fn load_macaroon(path: impl AsRef<Path> + Into<PathBuf>) -> Result<String, InternalConnectError> {
let macaroon = tokio::fs::read(&path)
.await
.map_err(|error| InternalConnectError::ReadFile { file: path.into(), error, })?;
async fn load_macaroon(
path: impl AsRef<Path> + Into<PathBuf>,
) -> Result<String, InternalConnectError> {
let macaroon =
tokio::fs::read(&path)
.await
.map_err(|error| InternalConnectError::ReadFile {
file: path.into(),
error,
})?;
Ok(hex::encode(&macaroon))
}

Expand All @@ -209,82 +221,132 @@ async fn load_macaroon(path: impl AsRef<Path> + Into<PathBuf>) -> Result<String,
/// If you have a motivating use case for use of direct data feel free to open an issue and
/// explain.
#[cfg_attr(feature = "tracing", tracing::instrument(name = "Connecting to LND"))]
pub async fn connect<A, CP, MP>(address: A, cert_file: CP, macaroon_file: MP) -> Result<Client, ConnectError> where A: TryInto<tonic::transport::Endpoint> + std::fmt::Debug + ToString, <A as TryInto<tonic::transport::Endpoint>>::Error: std::error::Error + Send + Sync + 'static, CP: AsRef<Path> + Into<PathBuf> + std::fmt::Debug, MP: AsRef<Path> + Into<PathBuf> + std::fmt::Debug {
pub async fn connect<A, CP, MP>(
address: A,
cert_file: CP,
macaroon_file: MP,
) -> Result<Client, ConnectError>
where
A: TryInto<tonic::transport::Endpoint> + std::fmt::Debug + ToString,
<A as TryInto<tonic::transport::Endpoint>>::Error: std::error::Error + Send + Sync + 'static,
CP: AsRef<Path> + Into<PathBuf> + std::fmt::Debug,
MP: AsRef<Path> + Into<PathBuf> + std::fmt::Debug,
{
let address_str = address.to_string();
let conn = try_map_err!(address
.try_into(), |error| InternalConnectError::InvalidAddress { address: address_str.clone(), error: Box::new(error), })
.tls_config(tls::config(cert_file).await?)
.map_err(InternalConnectError::TlsConfig)?
.connect()
.await
.map_err(|error| InternalConnectError::Connect { address: address_str, error, })?;
let conn = try_map_err!(address.try_into(), |error| {
InternalConnectError::InvalidAddress {
address: address_str.clone(),
error: Box::new(error),
}
})
.tls_config(tls::config(cert_file).await?)
.map_err(InternalConnectError::TlsConfig)?
.connect()
.await
.map_err(|error| InternalConnectError::Connect {
address: address_str,
error,
})?;

let macaroon = load_macaroon(macaroon_file).await?;

let interceptor = MacaroonInterceptor { macaroon, };
let interceptor = MacaroonInterceptor { macaroon };

let client = Client {
lightning: lnrpc::lightning_client::LightningClient::with_interceptor(conn.clone(), interceptor.clone()),
wallet: walletrpc::wallet_kit_client::WalletKitClient::with_interceptor(conn.clone(), interceptor.clone()),
lightning: lnrpc::lightning_client::LightningClient::with_interceptor(
conn.clone(),
interceptor.clone(),
),
wallet: walletrpc::wallet_kit_client::WalletKitClient::with_interceptor(
conn.clone(),
interceptor.clone(),
),
peers: peersrpc::peers_client::PeersClient::with_interceptor(
conn.clone(),
interceptor.clone(),
),
version: verrpc::versioner_client::VersionerClient::with_interceptor(conn.clone(), interceptor.clone()),
version: verrpc::versioner_client::VersionerClient::with_interceptor(
conn.clone(),
interceptor.clone(),
),
signer: signrpc::signer_client::SignerClient::with_interceptor(conn, interceptor),
};
Ok(client)
}

mod tls {
use crate::error::{ConnectError, InternalConnectError};
use rustls::{Certificate, RootCertStore, ServerCertVerified, TLSError};
use std::path::{Path, PathBuf};
use rustls::{RootCertStore, Certificate, TLSError, ServerCertVerified};
use webpki::DNSNameRef;
use crate::error::{ConnectError, InternalConnectError};

pub(crate) async fn config(path: impl AsRef<Path> + Into<PathBuf>) -> Result<tonic::transport::ClientTlsConfig, ConnectError> {
pub(crate) async fn config(
path: impl AsRef<Path> + Into<PathBuf>,
) -> Result<tonic::transport::ClientTlsConfig, ConnectError> {
let mut tls_config = rustls::ClientConfig::new();
tls_config.dangerous().set_certificate_verifier(std::sync::Arc::new(CertVerifier::load(path).await?));
tls_config
.dangerous()
.set_certificate_verifier(std::sync::Arc::new(CertVerifier::load(path).await?));
tls_config.set_protocols(&["h2".into()]);
Ok(tonic::transport::ClientTlsConfig::new()
.rustls_client_config(tls_config))
Ok(tonic::transport::ClientTlsConfig::new().rustls_client_config(tls_config))
}

pub(crate) struct CertVerifier {
certs: Vec<Vec<u8>>
certs: Vec<Vec<u8>>,
}

impl CertVerifier {
pub(crate) async fn load(path: impl AsRef<Path> + Into<PathBuf>) -> Result<Self, InternalConnectError> {
let contents = try_map_err!(tokio::fs::read(&path).await,
|error| InternalConnectError::ReadFile { file: path.into(), error });
pub(crate) async fn load(
path: impl AsRef<Path> + Into<PathBuf>,
) -> Result<Self, InternalConnectError> {
let contents = try_map_err!(tokio::fs::read(&path).await, |error| {
InternalConnectError::ReadFile {
file: path.into(),
error,
}
});
let mut reader = &*contents;

let certs = try_map_err!(rustls_pemfile::certs(&mut reader),
|error| InternalConnectError::ParseCert { file: path.into(), error });
let certs = try_map_err!(rustls_pemfile::certs(&mut reader), |error| {
InternalConnectError::ParseCert {
file: path.into(),
error,
}
});

#[cfg(feature = "tracing")] {
#[cfg(feature = "tracing")]
{
tracing::debug!("Certificates loaded (Count: {})", certs.len());
}

Ok(CertVerifier {
certs: certs,
})
Ok(CertVerifier { certs: certs })
}
}

impl rustls::ServerCertVerifier for CertVerifier {
fn verify_server_cert(&self, _roots: &RootCertStore, presented_certs: &[Certificate], _dns_name: DNSNameRef<'_>, _ocsp_response: &[u8]) -> Result<ServerCertVerified, TLSError> {

fn verify_server_cert(
&self,
_roots: &RootCertStore,
presented_certs: &[Certificate],
_dns_name: DNSNameRef<'_>,
_ocsp_response: &[u8],
) -> Result<ServerCertVerified, TLSError> {
if self.certs.len() != presented_certs.len() {
return Err(TLSError::General(format!("Mismatched number of certificates (Expected: {}, Presented: {})", self.certs.len(), presented_certs.len())));
return Err(TLSError::General(format!(
"Mismatched number of certificates (Expected: {}, Presented: {})",
self.certs.len(),
presented_certs.len()
)));
}

for (c, p) in self.certs.iter().zip(presented_certs.iter()) {
if *p.0 != **c {
return Err(TLSError::General(format!("Server certificates do not match ours")));
return Err(TLSError::General(format!(
"Server certificates do not match ours"
)));
} else {
#[cfg(feature = "tracing")] {
#[cfg(feature = "tracing")]
{
tracing::trace!("Confirmed certificate match");
}
}
Expand Down