diff --git a/Cargo.toml b/Cargo.toml index 51790d7..ef87534 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,9 +31,7 @@ lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning log = "0.4.17" tokio = { version = "1.7.1", features = ["fs", "rt-multi-thread"] } tokio-postgres = "0.7.7" -fedimint-tonic-lnd = "0.1.0" -tonic = { version = "0.6.2", features = ["transport", "tls"] } -prost = "0.9.0" +lnd_grpc_rust = "2.3.0" rustls = { version = "0.19.0", features = ["dangerous_configuration"] } refinery = { version = "0.8", features = ["tokio-postgres"]} log4rs = "1.2.0" @@ -54,4 +52,4 @@ protobuf-codegen = "3.2.0" [dev-dependencies] tokio = { version = "1.7.1", features = ["rt-multi-thread"] } -once_cell = "1.18.0" \ No newline at end of file +once_cell = "1.18.0" diff --git a/client/src/main.rs b/client/src/main.rs index 7bcf143..b2bd687 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,5 +1,4 @@ -mod client; -mod services; + use bdk::bitcoin::secp256k1::XOnlyPublicKey; use looper::{ diff --git a/docker-compose.network.yaml b/docker-compose.network.yaml new file mode 100644 index 0000000..f2955a6 --- /dev/null +++ b/docker-compose.network.yaml @@ -0,0 +1,60 @@ +version: '3.3' +name: polar-network-1 +services: + backend1: + environment: + USERID: ${USERID:-1000} + GROUPID: ${GROUPID:-1000} + stop_grace_period: 5m + image: polarlightning/bitcoind:25.0 + container_name: polar-n1-backend1 + hostname: backend1 + command: >- + bitcoind -server=1 -regtest=1 + -rpcauth=polaruser:5e5e98c21f5c814568f8b55d83b23c1c$$066b03f92df30b11de8e4b1b1cd5b1b4281aa25205bd57df9be82caf97a05526 + -debug=1 -zmqpubrawblock=tcp://0.0.0.0:28334 + -zmqpubrawtx=tcp://0.0.0.0:28335 -zmqpubhashblock=tcp://0.0.0.0:28336 + -txindex=1 -dnsseed=0 -upnp=0 -rpcbind=0.0.0.0 -rpcallowip=0.0.0.0/0 + -rpcport=18443 -rest -listen=1 -listenonion=0 -fallbackfee=0.0002 + -blockfilterindex=1 -peerblockfilters=1 + volumes: + - ./volumes/bitcoind/backend1:/home/bitcoin/.bitcoin + expose: + - '18443' + - '18444' + - '28334' + - '28335' + ports: + - '18444:18443' + - '19445:18444' + - '28335:28334' + - '29336:28335' + alice: + environment: + USERID: ${USERID:-1000} + GROUPID: ${GROUPID:-1000} + stop_grace_period: 2m + image: polarlightning/lnd:0.15.0-beta + container_name: polar-n1-alice + hostname: alice + command: >- + lnd --noseedbackup --trickledelay=5000 --alias=alice --externalip=alice + --tlsextradomain=alice --tlsextradomain=polar-n1-alice + --tlsextradomain=host.docker.internal --listen=0.0.0.0:9735 + --rpclisten=0.0.0.0:10009 --restlisten=0.0.0.0:8080 --bitcoin.active + --bitcoin.regtest --bitcoin.node=bitcoind + --bitcoind.rpchost=polar-n1-backend1 --bitcoind.rpcuser=polaruser + --bitcoind.rpcpass=polarpass + --bitcoind.zmqpubrawblock=tcp://polar-n1-backend1:28334 + --bitcoind.zmqpubrawtx=tcp://polar-n1-backend1:28335 + restart: always + volumes: + - ./volumes/lnd/alice:/home/lnd/.lnd + expose: + - '8080' + - '10009' + - '9735' + ports: + - '8085:8080' + - '10005:10009' + - '9739:9735' diff --git a/docker-compose.yaml b/docker-compose.yaml index 858c14c..7e563ac 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -9,10 +9,10 @@ services: - POSTGRES_PASSWORD=postgres - PGDATA=/var/lib/postgresql/data/pgdata volumes: - - postgres-volume:/var/lib/postgresql/data + - ./init/looper.sql:/docker-entrypoint-initdb.d/looper.sql restart: on-failure ports: - "5432:5432" volumes: - postgres-volume: \ No newline at end of file + postgres-volume: diff --git a/init/looper.sql b/init/looper.sql new file mode 100644 index 0000000..705abac --- /dev/null +++ b/init/looper.sql @@ -0,0 +1 @@ +CREATE DATABASE looper; diff --git a/src/lnd/client.rs b/src/lnd/client.rs index 73b1999..8731c79 100644 --- a/src/lnd/client.rs +++ b/src/lnd/client.rs @@ -1,16 +1,14 @@ use crate::{settings, utils}; use hex; +use lnd_grpc_rust::{lnrpc, LndClient}; use std::collections::HashMap; +use std::error::Error; +use std::fmt::Write; +use std::fs; +use serde::{Deserialize, Serialize}; +use tokio::sync::{Mutex, MutexGuard}; -use tokio::sync::Mutex; - -use fedimint_tonic_lnd::{ - invoicesrpc, - lnrpc::{self, FeeLimit}, - routerrpc, Client, -}; - -#[derive(Clone)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct LNDConfig { pub address: String, pub cert_path: String, @@ -26,36 +24,39 @@ pub fn get_lnd_config(cfg: &settings::Config) -> Result DEFAULT_INVOICE_LIFETIME, }; - let address = cfg - .get("lnd.address") - .map_err(|e| LNDGatewayError::new(format!("lnd.address not set: {:?}", e.to_string())))?; - let cert_path = cfg - .get("lnd.cert_path") - .map_err(|e| LNDGatewayError::new(format!("lnd.cert_path not set: {:?}", e.to_string())))?; - let macaroon_path = cfg.get("lnd.macaroon_path").map_err(|e| { - LNDGatewayError::new(format!("lnd.macaroon_path not set: {:?}", e.to_string())) - })?; + // extract all files necessary to connect to lnd + let host = cfg.get::("lnd.address").expect("FailedToReadLndHost"); + let cert_path = cfg.get::("lnd.cert_path").expect("FailedToReadLndHost"); + let macaroon_path = cfg.get::("lnd.macaroon_path").expect("FailedToReadLndHost"); + + // read the files and convert in bytes + let cert_bytes = fs::read(cert_path).expect("FailedToReadTlsCertFile"); + let mac_bytes = fs::read(macaroon_path).expect("FailedToReadMacaroonFile"); + + // Convert the bytes to a hex string + let cert = buffer_as_hex(cert_bytes); + let macaroon = buffer_as_hex(mac_bytes); Ok(LNDConfig { - address, - cert_path, - macaroon_path, + cert_path: cert, + macaroon_path: macaroon, + address: host, invoice_lifetime: invoice_lifetime as i64, }) } -pub async fn new_client(cfg: LNDConfig) -> Result { - fedimint_tonic_lnd::connect( - cfg.address.clone(), +pub async fn new_client(cfg: LNDConfig) -> Result> { + lnd_grpc_rust::connect( cfg.cert_path.clone(), cfg.macaroon_path.clone(), + cfg.address.clone(), ) .await } pub struct LNDGateway { cfg: LNDConfig, - client: Mutex, + client: Mutex, } #[derive(Debug)] @@ -84,23 +85,23 @@ impl LNDGateway { }) } - async fn get_client(&self) -> tokio::sync::MutexGuard<'_, Client> { + async fn get_client(&self) -> MutexGuard<'_, LndClient> { self.client.lock().await } - pub async fn get_info(&self) -> Result { + /* pub async fn get_info(&self) -> Result { let mut client = self.get_client().await; let resp = client.lightning().get_info(lnrpc::GetInfoRequest {}).await; match resp { Ok(resp) => Ok(resp.into_inner()), Err(e) => Err(e), } - } + }*/ pub async fn add_invoice( &self, value: i64, - ) -> Result { + ) -> Result { let mut client = self.get_client().await; // TODO: do we have to generate this? @@ -108,7 +109,7 @@ impl LNDGateway { let payment_addr = LNDGateway::new_payment_addr(); // resolves lint vs compile error dilemma #[allow(deprecated)] - let req = lnrpc::Invoice { + let req = lnd_grpc_rust::lnrpc::Invoice { memo: "looper swap out".to_string(), r_preimage: preimage.to_vec(), r_hash: payment_hash.to_vec(), @@ -159,11 +160,11 @@ impl LNDGateway { &self, value: i64, cltv_timout: u64, - ) -> Result { + ) -> Result { let mut client = self.get_client().await; let (preimage, payment_hash) = Self::new_preimage(); - let req = invoicesrpc::AddHoldInvoiceRequest { + let req = lnd_grpc_rust::invoicesrpc::AddHoldInvoiceRequest { memo: "looper swap out".to_string(), hash: payment_hash.to_vec(), value, @@ -196,11 +197,11 @@ impl LNDGateway { &self, invoice: String, fee_limit: i64, - ) -> Result<(), fedimint_tonic_lnd::Error> { + ) -> Result<(), lnd_grpc_rust::LndClientError> { let mut client = self.get_client().await; // resolves lint vs compile error dilemma #[allow(deprecated)] - let req = routerrpc::SendPaymentRequest { + let req = lnd_grpc_rust::routerrpc::SendPaymentRequest { payment_request: invoice, timeout_seconds: 600, amt: 0, @@ -240,11 +241,11 @@ impl LNDGateway { &self, invoice: String, fee_limit: i64, - ) -> Result { + ) -> Result { let mut client = self.get_client().await; // resolves lint vs compile error dilemma #[allow(deprecated)] - let req = lnrpc::SendRequest { + let req = lnd_grpc_rust::lnrpc::SendRequest { dest: vec![], dest_string: "".to_string(), amt: 0, @@ -254,7 +255,7 @@ impl LNDGateway { payment_request: invoice, // TODO: SET ME to cltv_delta + block height so that we can ensure the invoice can't be held too long final_cltv_delta: 0, - fee_limit: Some(FeeLimit { + fee_limit: Some(lnrpc::FeeLimit { limit: Some(lnrpc::fee_limit::Limit::Fixed(fee_limit)), }), outgoing_chan_id: 0, @@ -287,6 +288,13 @@ impl LNDGateway { } } +pub fn buffer_as_hex(bytes: Vec) -> String { + bytes.iter().fold(String::new(), |mut output, b| { + let _ = write!(output, "{b:02X}"); + output + }) +} + // TODO: should we make every method return this error or is fedimint_tonic_lnd::Error sufficient? #[derive(Debug)] pub struct LNDGatewayError { diff --git a/src/lnd/mod.rs b/src/lnd/mod.rs index b9babe5..fe55431 100644 --- a/src/lnd/mod.rs +++ b/src/lnd/mod.rs @@ -1 +1,2 @@ pub mod client; +pub mod invoice_tracker; diff --git a/src/main.rs b/src/main.rs index 2ab394d..d024434 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ #[macro_use] extern crate rocket; -// #[macro_use] -// extern crate diesel; +use std::io::{self, BufRead}; + +// use bdk::bitcoin::secp256k1::PublicKey; +use db::DB; + +use crate::lnd::client::LNDGateway; mod api; mod db; @@ -15,14 +19,10 @@ pub mod settings; mod utils; pub mod wallet; -use crate::lnd::client::LNDGateway; -// use bdk::bitcoin::secp256k1::PublicKey; -use db::DB; -use std::io::{self, BufRead}; // use std::str::FromStr; // use rand::Rng; - +// #[tokio::main] async fn main() { run().await; @@ -30,21 +30,26 @@ async fn main() { async fn run() { let cfg = settings::build_config().unwrap(); - let db = DB::new(&cfg); - let migration_conn = &mut db.get_conn().unwrap(); db::run_migrations(migration_conn).unwrap(); - let wallet = wallet::LooperWallet::new(&cfg).unwrap(); - - let lndg = LNDGateway::new().await.unwrap(); - - let loopout_svc = services::loop_out::LoopOutService::new(&cfg, db, wallet, lndg).unwrap(); - - let server = api::server::LooperServer::new(loopout_svc); - server.start(); - - let stdin = io::stdin(); - let _line = stdin.lock().lines().next().unwrap().unwrap(); + match wallet::LooperWallet::new(&cfg) { + Ok(wallet) => match LNDGateway::new().await { + Ok(lndg) => { + let loopout_svc = + services::loop_out::LoopOutService::new(&cfg, db, wallet, lndg).unwrap(); + let server = api::server::LooperServer::new(loopout_svc); + server.start(); + let stdin = io::stdin(); + let _line = stdin.lock().lines().next().unwrap().unwrap(); + } + Err(err) => { + panic!("could not start the lndGateway {} ", err.msg); + } + }, + Err(err) => { + panic!("could not create the wallet {} ", err.message); + } + } } diff --git a/src/settings.rs b/src/settings.rs index 0414888..0bd686a 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -12,7 +12,7 @@ pub fn build_config() -> Result { log::info!("loading config for {}", profile); Config::builder() - .add_source(File::with_name("config/default")) + .add_source(File::with_name("config/example")) .add_source(File::with_name(&format!("config/{}", profile)).required(false)) .add_source(Environment::with_prefix("RLS").separator("_")) .build()