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

tests: basic cli/server integration tests #171

Open
wants to merge 6 commits 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
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

GO_BUILD := go build
CARGO_TEST := cargo test
CARGO_BUILD := cargo build
LND_PKG := github.com/lightningnetwork/lnd

TMP_DIR := "/tmp"
BIN_DIR := $(TMP_DIR)/lndk-tests/bin

UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
TMP_DIR=${TMPDIR}
endif

itest:
@$(call print, "Building lnd for itests.")
@echo Building lnd for itests.
git submodule update --init --recursive
cd lnd/cmd/lnd; $(GO_BUILD) -tags="peersrpc signrpc walletrpc dev" -o $(TMP_DIR)/lndk-tests/bin/lnd-itest$(EXEC_SUFFIX)
$(CARGO_TEST) --test '*' -- --test-threads=1 --nocapture
cd lnd/cmd/lnd; $(GO_BUILD) -tags="peersrpc signrpc walletrpc dev" -o $(BIN_DIR)/lnd-itest$(EXEC_SUFFIX)

@echo Building lndk-cli for itests.
# This outputs the lndk-cli binary into $(BINDIR)/debug/
$(CARGO_BUILD) --bin=lndk-cli --target-dir=$(BIN_DIR)

$(CARGO_TEST) --test '*' -- --test-threads=1 --nocapture
4 changes: 2 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use lightning::offers::invoice::Bolt12Invoice;
use lndk::lndk_offers::decode;
use lndk::lndkrpc::offers_client::OffersClient;
use lndk::lndkrpc::{GetInvoiceRequest, PayInvoiceRequest, PayOfferRequest};
use lndk::server::{DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT};
use lndk::{
Bolt12InvoiceString, DEFAULT_DATA_DIR, DEFAULT_RESPONSE_INVOICE_TIMEOUT, DEFAULT_SERVER_HOST,
DEFAULT_SERVER_PORT, TLS_CERT_FILENAME,
Bolt12InvoiceString, DEFAULT_DATA_DIR, DEFAULT_RESPONSE_INVOICE_TIMEOUT, TLS_CERT_FILENAME,
};
use std::fs::File;
use std::io::BufReader;
Expand Down
10 changes: 4 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ pub fn init_logger(config: LogConfig) {
});
}

pub const DEFAULT_SERVER_HOST: &str = "127.0.0.1";
pub const DEFAULT_SERVER_PORT: u16 = 7000;
pub const LDK_LOGGER_NAME: &str = "ldk";
pub const DEFAULT_DATA_DIR: &str = ".lndk";

Expand Down Expand Up @@ -213,8 +211,8 @@ impl LndkOnionMessenger {
}

// Create an onion messenger that depends on LND's signer client and consume related events.
let mut node_client = client.signer().clone();
let node_signer = LndNodeSigner::new(pubkey, &mut node_client);
let node_client = client.clone().signer_read_only();
let node_signer = LndNodeSigner::new(pubkey, node_client);
let messenger_utils = MessengerUtilities::new();
let network_graph = &NetworkGraph::new(network, &messenger_utils);
let message_router = &DefaultMessageRouter::new(network_graph, &messenger_utils);
Expand All @@ -229,10 +227,10 @@ impl LndkOnionMessenger {
IgnoringMessageHandler {},
);

let mut peers_client = client.lightning().clone();
let peers_client = client.lightning().clone();
self.run_onion_messenger(
peer_support,
&mut peers_client,
&peers_client,
onion_messenger,
network,
args.signals,
Expand Down
17 changes: 8 additions & 9 deletions src/lnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::offers::invoice_request::{InvoiceRequest, UnsignedInvoiceRequest};
use lightning::sign::{KeyMaterial, NodeSigner, Recipient};
use log::error;
use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error;
use std::fmt::Display;
Expand Down Expand Up @@ -48,7 +47,7 @@ pub fn get_lnd_client(cfg: LndCfg) -> Result<Client, ConnectError> {
}

/// LndCfg specifies the configuration required to connect to LND's grpc client.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct LndCfg {
pub address: String,
pub creds: Creds,
Expand Down Expand Up @@ -233,23 +232,23 @@ pub fn has_build_tags(version: &Version, requirement: Option<BuildTagsRequiremen
}

/// LndNodeSigner provides signing operations using LND's signer subserver.
pub(crate) struct LndNodeSigner<'a> {
pub(crate) struct LndNodeSigner {
pubkey: PublicKey,
secp_ctx: Secp256k1<secp256k1::All>,
signer: RefCell<&'a mut tonic_lnd::SignerClient>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally lol

signer: tonic_lnd::SignerClient,
}

impl<'a> LndNodeSigner<'a> {
pub(crate) fn new(pubkey: PublicKey, signer: &'a mut tonic_lnd::SignerClient) -> Self {
impl LndNodeSigner {
pub(crate) fn new(pubkey: PublicKey, signer: tonic_lnd::SignerClient) -> Self {
LndNodeSigner {
pubkey,
secp_ctx: Secp256k1::new(),
signer: RefCell::new(signer),
signer,
}
}
}

impl<'a> NodeSigner for LndNodeSigner<'a> {
impl NodeSigner for LndNodeSigner {
/// Get node id based on the provided [`Recipient`].
///
/// This method must return the same value each time it is called with a given [`Recipient`]
Expand Down Expand Up @@ -287,7 +286,7 @@ impl<'a> NodeSigner for LndNodeSigner<'a> {
*other_key
};

let shared_secret = match block_on(self.signer.borrow_mut().derive_shared_key(
let shared_secret = match block_on(self.signer.clone().derive_shared_key(
tonic_lnd::signrpc::SharedKeyRequest {
ephemeral_pubkey: tweaked_key.serialize().into_iter().collect::<Vec<u8>>(),
key_desc: None,
Expand Down
60 changes: 11 additions & 49 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,18 @@

use home::home_dir;
use internal::*;
use lndk::lnd::{get_lnd_client, validate_lnd_creds, LndCfg};
use lndk::server::{generate_tls_creds, read_tls, LNDKServer};
use lndk::lnd::{validate_lnd_creds, LndCfg};
use lndk::server::setup_server;
use lndk::{
lndkrpc, setup_logger, Cfg, LifecycleSignals, LndkOnionMessenger, OfferHandler,
DEFAULT_DATA_DIR, DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT,
setup_logger, Cfg, LifecycleSignals, LndkOnionMessenger, OfferHandler, DEFAULT_DATA_DIR,
};
use lndkrpc::offers_server::OffersServer;
use log::{error, info};
use std::fs::create_dir_all;
use std::path::PathBuf;
use std::process::exit;
use std::sync::Arc;
use tokio::select;
use tokio::signal::unix::SignalKind;
use tonic::transport::{Server, ServerTlsConfig};
use tonic_lnd::lnrpc::GetInfoRequest;

#[macro_use]
extern crate configure_me;
Expand Down Expand Up @@ -93,51 +89,17 @@
let handler = Arc::new(OfferHandler::new(config.response_invoice_timeout));
let messenger = LndkOnionMessenger::new();

let mut client = get_lnd_client(args.lnd.clone()).expect("failed to connect to lnd");
let info = client
.lightning()
.get_info(GetInfoRequest {})
.await
.expect("failed to get info")
.into_inner();

let grpc_host = match config.grpc_host {
Some(host) => host,
None => DEFAULT_SERVER_HOST.to_string(),
};
let grpc_port = match config.grpc_port {
Some(port) => port,
None => DEFAULT_SERVER_PORT,
};
let addr = format!("{grpc_host}:{grpc_port}").parse().map_err(|e| {
error!("Error parsing API address: {e}");
})?;
let lnd_tls_str = creds.get_certificate_string()?;

// The user passed in a TLS cert to help us establish a secure connection to LND. But now we
// need to generate a TLS credentials for connecting securely to the LNDK server.
generate_tls_creds(data_dir.clone(), config.tls_ip).map_err(|e| {
error!("Error generating tls credentials: {e}");
})?;
let identity = read_tls(data_dir).map_err(|e| {
error!("Error reading tls credentials: {e}");
})?;

let server = LNDKServer::new(
let server_fut = setup_server(
args.lnd.clone(),
config.grpc_host,
config.grpc_port,
data_dir,
config.tls_ip,

Check warning on line 97 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L92-L97

Added lines #L92 - L97 were not covered by tests
Arc::clone(&handler),
&info.identity_pubkey,
lnd_tls_str,
address,
)
.await;

let server_fut = Server::builder()
.tls_config(ServerTlsConfig::new().identity(identity))
.expect("couldn't configure tls")
.add_service(OffersServer::new(server))
.serve_with_shutdown(addr, listener);

info!("Starting lndk's grpc server at address {grpc_host}:{grpc_port}");
.await
.map_err(|e| error!("Error setting up server: {:?}", e))?;

Check warning on line 102 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L101-L102

Added lines #L101 - L102 were not covered by tests

select! {
_ = messenger.run(args, Arc::clone(&handler)) => {
Expand Down
8 changes: 3 additions & 5 deletions src/onion_messenger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl LndkOnionMessenger {
>(
&self,
current_peers: HashMap<PublicKey, bool>,
ln_client: &mut tonic_lnd::LightningClient,
ln_client: &tonic_lnd::LightningClient,
onion_messenger: OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>,
network: Network,
signals: LifecycleSignals,
Expand Down Expand Up @@ -266,10 +266,8 @@ impl LndkOnionMessenger {
}
});

// Consume events is our main controlling loop, so we run it inline here. We use a RefCell
// in onion_messenger to allow interior mutability (see LndNodeSigner) so this
// function can't safely be passed off to another thread. This function is expected
// to finish if any producing thread exits (because we're no longer receiving the
// Consume events is our main controlling loop, so we run it inline here. This function is
// expected to finish if any producing thread exits (because we're no longer receiving the
// events we need).
let rate_limiter = &mut TokenLimiter::new(
current_peers.keys().copied(),
Expand Down
80 changes: 72 additions & 8 deletions src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,51 @@ use lightning::offers::invoice::{BlindedPayInfo, Bolt12Invoice};
use lightning::offers::offer::Offer;
use lightning::sign::EntropySource;
use lightning::util::ser::Writeable;
use lndkrpc::offers_server::Offers;
use lndkrpc::offers_server::{Offers, OffersServer};
use lndkrpc::{
Bolt12InvoiceContents, DecodeInvoiceRequest, FeatureBit, GetInvoiceRequest, GetInvoiceResponse,
PayInvoiceRequest, PayInvoiceResponse, PayOfferRequest, PayOfferResponse, PaymentHash,
PaymentPaths,
};
use log::{error, info};
use rcgen::{generate_simple_self_signed, CertifiedKey, Error as RcgenError};
use std::error::Error;
use std::fmt::Display;
use std::fs::{metadata, set_permissions, File};
use std::future::Future;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
use tonic::metadata::MetadataMap;
use tonic::transport::Identity;
use tonic::transport::{Identity, Server, ServerTlsConfig};
use tonic::{Request, Response, Status};
use tonic_lnd::lnrpc::GetInfoRequest;

pub const DEFAULT_SERVER_HOST: &str = "127.0.0.1";
pub const DEFAULT_SERVER_PORT: u16 = 7000;

pub struct LNDKServer {
offer_handler: Arc<OfferHandler>,
node_id: PublicKey,
// The LND tls cert we need to establish a connection with LND.
lnd_cert: String,
address: String,
lnd_address: String,
}

impl LNDKServer {
pub async fn new(
offer_handler: Arc<OfferHandler>,
node_id: &str,
lnd_cert: String,
address: String,
lnd_address: String,
) -> Self {
Self {
offer_handler,
node_id: PublicKey::from_str(node_id).unwrap(),
lnd_cert,
address,
lnd_address,
}
}
}
Expand All @@ -69,7 +75,7 @@ impl Offers for LNDKServer {
cert: self.lnd_cert.clone(),
macaroon,
};
let lnd_cfg = LndCfg::new(self.address.clone(), creds);
let lnd_cfg = LndCfg::new(self.lnd_address.clone(), creds);
let mut client = get_lnd_client(lnd_cfg)
.map_err(|e| Status::unavailable(format!("Couldn't connect to lnd: {e}")))?;

Expand Down Expand Up @@ -165,7 +171,7 @@ impl Offers for LNDKServer {
cert: self.lnd_cert.clone(),
macaroon,
};
let lnd_cfg = LndCfg::new(self.address.clone(), creds);
let lnd_cfg = LndCfg::new(self.lnd_address.clone(), creds);
let mut client = get_lnd_client(lnd_cfg)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about pass this client to LNDKServer?
In each LNDKServer's handler methods such as pay_offer, get_invoice, we initialize lnd client but it seems redundant. It seems this is out of scope of this PR, but I would like to know if there are any reasons for this.

.map_err(|e| Status::unavailable(format!("Couldn't connect to lnd: {e}")))?;

Expand Down Expand Up @@ -252,7 +258,7 @@ impl Offers for LNDKServer {
cert: self.lnd_cert.clone(),
macaroon,
};
let lnd_cfg = LndCfg::new(self.address.clone(), creds);
let lnd_cfg = LndCfg::new(self.lnd_address.clone(), creds);
let client = get_lnd_client(lnd_cfg)
.map_err(|e| Status::unavailable(format!("Couldn't connect to lnd: {e}")))?;

Expand Down Expand Up @@ -310,6 +316,64 @@ fn check_auth_metadata(metadata: &MetadataMap) -> Result<String, Status> {
Ok(macaroon)
}

pub async fn setup_server(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming it as setup_and_run or just run feels more concrete to me because this function actually runs the server, not just setup.
How about implementing this functions as a method of LNDKServer the same as the run method of LndkOnionMessenger to maintains consistency across projects.

lnd_cfg: LndCfg,
grpc_host: Option<String>,
grpc_port: Option<u16>,
data_dir: PathBuf,
tls_ip: Option<String>,
handler: Arc<OfferHandler>,
lnd_address: String,
) -> Result<impl Future<Output = Result<(), tonic::transport::Error>>, ()> {
let mut client = get_lnd_client(lnd_cfg.clone()).expect("failed to connect to lnd");
let info = client
.lightning()
.get_info(GetInfoRequest {})
.await
.expect("failed to get info")
.into_inner();

let grpc_host = match grpc_host {
Some(host) => host,
None => DEFAULT_SERVER_HOST.to_string(),
};
let grpc_port = match grpc_port {
Some(port) => port,
None => DEFAULT_SERVER_PORT,
};
let addr = format!("{grpc_host}:{grpc_port}").parse().map_err(|e| {
error!("Error parsing API address: {e}");
})?;
let lnd_tls_str = lnd_cfg.creds.get_certificate_string()?;

// The user passed in a TLS cert to help us establish a secure connection to LND. But now we
// need to generate a TLS credentials for connecting securely to the LNDK server.
generate_tls_creds(data_dir.clone(), tls_ip).map_err(|e| {
error!("Error generating tls credentials: {e}");
})?;
let identity = read_tls(data_dir).map_err(|e| {
error!("Error reading tls credentials: {e}");
})?;

let server = LNDKServer::new(
Arc::clone(&handler),
&info.identity_pubkey,
lnd_tls_str,
lnd_address,
)
.await;

let server_fut = Server::builder()
.tls_config(ServerTlsConfig::new().identity(identity))
.expect("couldn't configure tls")
.add_service(OffersServer::new(server))
.serve(addr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it good to use serve instead of serve_with_shutdown, which the original code used?


info!("Starting lndk's grpc server at address {grpc_host}:{grpc_port}");

Ok(server_fut)
}

/// An error that occurs when generating TLS credentials.
#[derive(Debug)]
pub enum CertificateGenFailure {
Expand Down
Loading
Loading