From cc0ed69d326106cd4c2f5d0925ba1cd41cd865c6 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Thu, 9 Jun 2022 10:50:14 -0500 Subject: [PATCH 1/6] update to ldk 107 --- Cargo.lock | 43 ++++++--- Cargo.toml | 13 +-- proto/sensei.proto | 2 +- senseicore/Cargo.toml | 13 +-- senseicore/src/chain/bitcoind_client.rs | 34 +++---- senseicore/src/event_handler.rs | 112 ++++++++++++++++++------ senseicore/src/network_graph.rs | 3 +- senseicore/src/node.rs | 24 +++-- senseicore/src/persist.rs | 11 +-- senseicore/src/services/node.rs | 4 +- senseicore/tests/smoke_test.rs | 38 +++----- src/grpc/adaptor.rs | 2 +- 12 files changed, 178 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b3a99c..c920716 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1582,25 +1582,29 @@ dependencies = [ [[package]] name = "lightning" -version = "0.0.106" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d26c0da268a793c9c9a37365c4f6c496bfdbcbe8e17f74f226ce04cca3d721" dependencies = [ "bitcoin", ] [[package]] name = "lightning-background-processor" -version = "0.0.106" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ab2cb885578117635c9389dbc30ab74c4532e2dddc530215bc7aae5fb6d5f6" dependencies = [ "bitcoin", "lightning", + "lightning-rapid-gossip-sync", ] [[package]] name = "lightning-block-sync" -version = "0.0.106" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90da35aa25b605224c7b99d4d2232183545253570d1edce5ddf007ef4cdb485" dependencies = [ "bitcoin", "chunked_transfer", @@ -1612,8 +1616,9 @@ dependencies = [ [[package]] name = "lightning-invoice" -version = "0.14.0" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40813315a166d0bc053c396b6d2d99b26adcb6db937e073755ca13460eb71630" dependencies = [ "bech32", "bitcoin_hashes", @@ -1624,8 +1629,9 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.0.106" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1007cef0f7121a2ca68bcf6aadaab4618e2e63570e84dbcb26e17902c1f943" dependencies = [ "bitcoin", "lightning", @@ -1634,8 +1640,9 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.0.106" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=08ab6581f1c6254a7765340c5c7a553db58d3a89#08ab6581f1c6254a7765340c5c7a553db58d3a89" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513d513afce4c720423828ccac550ca12d00432368b4e5d751790257eb72c078" dependencies = [ "bitcoin", "libc", @@ -1643,6 +1650,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "lightning-rapid-gossip-sync" +version = "0.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f80f7131856f0ae61c22665edf953b11890c53a10e83f6d8f2dc01a5026be3d4" +dependencies = [ + "bitcoin", + "lightning", +] + [[package]] name = "lock_api" version = "0.4.7" @@ -2714,6 +2731,7 @@ dependencies = [ "lightning-invoice", "lightning-net-tokio", "lightning-persister", + "lightning-rapid-gossip-sync", "log", "macaroon", "migration", @@ -2766,6 +2784,7 @@ dependencies = [ "lightning-invoice", "lightning-net-tokio", "lightning-persister", + "lightning-rapid-gossip-sync", "log", "macaroon", "migration", diff --git a/Cargo.toml b/Cargo.toml index a754f53..93dafae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,12 +15,13 @@ name = "senseid" path = "src/main.rs" [dependencies] -lightning = { version = "0.0.106", features = ["max_level_trace"], git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-block-sync = { version = "0.0.106", features = [ "rpc-client" ], git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-invoice = { version = "0.14.0", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-net-tokio = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-persister = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-background-processor = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } +lightning = { version = "0.0.107", features = ["max_level_trace"] } +lightning-block-sync = { version = "0.0.107", features = [ "rpc-client" ] } +lightning-invoice = { version = "0.15.0" } +lightning-net-tokio = { version = "0.0.107" } +lightning-persister = { version = "0.0.107" } +lightning-background-processor = { version = "0.0.107" } +lightning-rapid-gossip-sync = { version = "0.0.107" } base64 = "0.13.0" bitcoin = "0.28.1" bitcoin-bech32 = "0.12" diff --git a/proto/sensei.proto b/proto/sensei.proto index 02dc6b7..92fe3e0 100644 --- a/proto/sensei.proto +++ b/proto/sensei.proto @@ -298,7 +298,7 @@ message Channel { optional uint32 confirmations_required = 11; optional uint32 force_close_spend_delay = 12; bool is_outbound = 13; - bool is_funding_locked = 14; + bool is_channel_ready = 14; bool is_usable = 15; bool is_public = 16; string counterparty_pubkey = 17; diff --git a/senseicore/Cargo.toml b/senseicore/Cargo.toml index d9098fc..5ef3348 100644 --- a/senseicore/Cargo.toml +++ b/senseicore/Cargo.toml @@ -9,12 +9,13 @@ name = "senseicore" path = "src/lib.rs" [dependencies] -lightning = { version = "0.0.106", features = ["max_level_trace"], git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-block-sync = { version = "0.0.106", features = [ "rpc-client" ], git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-invoice = { version = "0.14.0", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-net-tokio = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-persister = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } -lightning-background-processor = { version = "0.0.106", git = "https://github.com/lightningdevkit/rust-lightning", rev = "08ab6581f1c6254a7765340c5c7a553db58d3a89" } +lightning = { version = "0.0.107", features = ["max_level_trace"] } +lightning-block-sync = { version = "0.0.107", features = [ "rpc-client" ] } +lightning-invoice = { version = "0.15.0" } +lightning-net-tokio = { version = "0.0.107" } +lightning-persister = { version = "0.0.107" } +lightning-background-processor = { version = "0.0.107" } +lightning-rapid-gossip-sync = { version = "0.0.107" } base64 = "0.13.0" bitcoin = "0.28" bitcoin-bech32 = "0.12" diff --git a/senseicore/src/chain/bitcoind_client.rs b/senseicore/src/chain/bitcoind_client.rs index a37d781..b56fa0d 100644 --- a/senseicore/src/chain/bitcoind_client.rs +++ b/senseicore/src/chain/bitcoind_client.rs @@ -12,7 +12,6 @@ use std::collections::HashMap; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::time::Duration; -use tokio::sync::Mutex; use bitcoin::hashes::hex::FromHex; use lightning_block_sync::http::JsonResponse; @@ -56,7 +55,7 @@ impl TryInto for JsonResponse { } } pub struct BitcoindClient { - bitcoind_rpc_client: Arc>, + bitcoind_rpc_client: Arc, fees: Arc>, handle: tokio::runtime::Handle, } @@ -75,23 +74,18 @@ impl BlockSource for BitcoindClient { height_hint: Option, ) -> AsyncBlockSourceResult<'a, BlockHeaderData> { Box::pin(async move { - let rpc = self.bitcoind_rpc_client.lock().await; - rpc.get_header(header_hash, height_hint).await + self.bitcoind_rpc_client + .get_header(header_hash, height_hint) + .await }) } fn get_block<'a>(&'a self, header_hash: &'a BlockHash) -> AsyncBlockSourceResult<'a, Block> { - Box::pin(async move { - let rpc = self.bitcoind_rpc_client.lock().await; - rpc.get_block(header_hash).await - }) + Box::pin(async move { self.bitcoind_rpc_client.get_block(header_hash).await }) } fn get_best_block(&self) -> AsyncBlockSourceResult<(BlockHash, Option)> { - Box::pin(async move { - let rpc = self.bitcoind_rpc_client.lock().await; - rpc.get_best_block().await - }) + Box::pin(async move { self.bitcoind_rpc_client.get_best_block().await }) } } @@ -122,7 +116,7 @@ impl BitcoindClient { fees.insert(Target::Normal, AtomicU32::new(2000)); // 8 sats per byte fees.insert(Target::HighPriority, AtomicU32::new(5000)); // 20 sats per byte let client = Self { - bitcoind_rpc_client: Arc::new(Mutex::new(bitcoind_rpc_client)), + bitcoind_rpc_client: Arc::new(bitcoind_rpc_client), fees: Arc::new(fees), handle: handle.clone(), }; @@ -136,16 +130,15 @@ impl BitcoindClient { fn poll_for_fee_estimates( fees: Arc>, - rpc_client: Arc>, + rpc_client: Arc, handle: tokio::runtime::Handle, ) { handle.spawn(async move { loop { let background_estimate = { - let rpc = rpc_client.lock().await; let background_conf_target = serde_json::json!(144); let background_estimate_mode = serde_json::json!("ECONOMICAL"); - let resp = rpc + let resp = rpc_client .call_method::( "estimatesmartfee", &[background_conf_target, background_estimate_mode], @@ -159,10 +152,9 @@ impl BitcoindClient { }; let normal_estimate = { - let rpc = rpc_client.lock().await; let normal_conf_target = serde_json::json!(18); let normal_estimate_mode = serde_json::json!("ECONOMICAL"); - let resp = rpc + let resp = rpc_client .call_method::( "estimatesmartfee", &[normal_conf_target, normal_estimate_mode], @@ -176,10 +168,9 @@ impl BitcoindClient { }; let high_prio_estimate = { - let rpc = rpc_client.lock().await; let high_prio_conf_target = serde_json::json!(6); let high_prio_estimate_mode = serde_json::json!("CONSERVATIVE"); - let resp = rpc + let resp = rpc_client .call_method::( "estimatesmartfee", &[high_prio_conf_target, high_prio_estimate_mode], @@ -235,10 +226,9 @@ impl BroadcasterInterface for BitcoindClient { let bitcoind_rpc_client = self.bitcoind_rpc_client.clone(); let tx_serialized = serde_json::json!(encode::serialize_hex(tx)); self.handle.spawn(async move { - let rpc = bitcoind_rpc_client.lock().await; // This may error due to RL calling `broadcast_transaction` with the same transaction // multiple times, but the error is safe to ignore. - match rpc + match bitcoind_rpc_client .call_method::("sendrawtransaction", &[tx_serialized]) .await { diff --git a/senseicore/src/event_handler.rs b/senseicore/src/event_handler.rs index ee2f4f3..14cf6c5 100644 --- a/senseicore/src/event_handler.rs +++ b/senseicore/src/event_handler.rs @@ -14,13 +14,14 @@ use crate::config::SenseiConfig; use crate::database::SenseiDatabase; use crate::events::SenseiEvent; use crate::hex_utils; -use crate::node::{ChannelManager, HTLCStatus, PaymentOrigin}; +use crate::node::{ChannelManager, HTLCStatus, NetworkGraph, PaymentOrigin}; use bdk::wallet::AddressIndex; use bitcoin::{secp256k1::Secp256k1, Network}; use bitcoin_bech32::WitnessProgram; use entity::sea_orm::ActiveValue; use lightning::chain::chaininterface::BroadcasterInterface; +use lightning::routing::gossip::NodeId; use lightning::{ chain::{chaininterface::ConfirmationTarget, keysinterface::KeysManager}, util::events::{Event, EventHandler, PaymentPurpose}, @@ -42,6 +43,7 @@ pub struct LightningNodeEventHandler { pub tokio_handle: Handle, pub event_sender: broadcast::Sender, pub broadcaster: Arc, + pub network_graph: Arc, } impl EventHandler for LightningNodeEventHandler { @@ -86,33 +88,46 @@ impl EventHandler for LightningNodeEventHandler { Event::PaymentReceived { payment_hash, purpose, - amt, + amount_msat, .. } => { - let (payment_preimage, payment_secret) = match purpose { + println!( + "\nEVENT: received payment from payment hash {} of {} millisatoshis", + hex_utils::hex_str(&payment_hash.0), + amount_msat + ); + + let payment_preimage = match purpose { PaymentPurpose::InvoicePayment { - payment_preimage, - payment_secret, - .. - } => (*payment_preimage, Some(*payment_secret)), - PaymentPurpose::SpontaneousPayment(preimage) => (Some(*preimage), None), + payment_preimage, .. + } => *payment_preimage, + PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage), }; // TODO: if we want 'hodl invoices' we should have user set a flag on the invoice when they create it // then when we receive this event we can store the preimage + flag in db for this payment // user can then manually accept it // or maybe defines some custom logic on if/when to accept it - let status = match self.channel_manager.claim_funds(payment_preimage.unwrap()) { - true => { - println!( - "\nEVENT: received payment from payment hash {} of {} millisatoshis", - hex_utils::hex_str(&payment_hash.0), - amt - ); + self.channel_manager.claim_funds(payment_preimage.unwrap()); + } + Event::PaymentClaimed { + payment_hash, + purpose, + amount_msat, + } => { + println!( + "\nEVENT: claimed payment from payment hash {} of {} millisatoshis", + hex_utils::hex_str(&payment_hash.0), + amount_msat, + ); - HTLCStatus::Succeeded - } - _ => HTLCStatus::Failed, + let (payment_preimage, payment_secret) = match purpose { + PaymentPurpose::InvoicePayment { + payment_preimage, + payment_secret, + .. + } => (*payment_preimage, Some(*payment_secret)), + PaymentPurpose::SpontaneousPayment(preimage) => (Some(*preimage), None), }; let payment_hash = hex_utils::hex_str(&payment_hash.0); @@ -124,12 +139,12 @@ impl EventHandler for LightningNodeEventHandler { let preimage = payment_preimage.map(|preimage| hex_utils::hex_str(&preimage.0)); let secret = payment_secret.map(|secret| hex_utils::hex_str(&secret.0)); - let amt_msat: Option = Some((*amt).try_into().unwrap()); + let amt_msat: Option = Some((*amount_msat).try_into().unwrap()); match existing_payment { Some(payment) => { let mut payment: entity::payment::ActiveModel = payment.into(); - payment.status = ActiveValue::Set(status.to_string()); + payment.status = ActiveValue::Set(HTLCStatus::Succeeded.to_string()); payment.preimage = ActiveValue::Set(preimage); payment.secret = ActiveValue::Set(secret); payment.amt_msat = ActiveValue::Set(amt_msat); @@ -139,7 +154,7 @@ impl EventHandler for LightningNodeEventHandler { None => { let payment = entity::payment::ActiveModel { payment_hash: ActiveValue::Set(payment_hash), - status: ActiveValue::Set(status.to_string()), + status: ActiveValue::Set(HTLCStatus::Succeeded.to_string()), preimage: ActiveValue::Set(preimage), secret: ActiveValue::Set(secret), amt_msat: ActiveValue::Set(amt_msat), @@ -218,11 +233,54 @@ impl EventHandler for LightningNodeEventHandler { } } Event::PaymentForwarded { + prev_channel_id, + next_channel_id, fee_earned_msat, claim_from_onchain_tx, - prev_channel_id: _, - next_channel_id: _, } => { + let read_only_network_graph = self.network_graph.read_only(); + let nodes = read_only_network_graph.nodes(); + let channels = self.channel_manager.list_channels(); + + let node_str = |channel_id: &Option<[u8; 32]>| match channel_id { + None => String::new(), + Some(channel_id) => match channels.iter().find(|c| c.channel_id == *channel_id) + { + None => String::new(), + Some(channel) => { + match nodes.get(&NodeId::from_pubkey(&channel.counterparty.node_id)) { + None => " from private node".to_string(), + Some(node) => match &node.announcement_info { + None => " from unnamed node".to_string(), + Some(info) => { + format!( + " from node {}", + String::from_utf8_lossy(&info.alias) + ) + } + }, + } + } + }, + }; + let channel_str = |channel_id: &Option<[u8; 32]>| { + channel_id + .map(|channel_id| { + format!(" with channel {}", hex_utils::hex_str(&channel_id)) + }) + .unwrap_or_default() + }; + let from_prev_str = format!( + "{}{}", + node_str(prev_channel_id), + channel_str(prev_channel_id) + ); + let to_next_str = format!( + "{}{}", + node_str(next_channel_id), + channel_str(next_channel_id) + ); + let from_onchain_str = if *claim_from_onchain_tx { "from onchain downstream claim" } else { @@ -230,8 +288,8 @@ impl EventHandler for LightningNodeEventHandler { }; if let Some(fee_earned) = fee_earned_msat { println!( - "\nEVENT: Forwarded payment, earning {} msat {}", - fee_earned, from_onchain_str + "\nEVENT: Forwarded payment{}{}, earning {} msat {}", + from_prev_str, to_next_str, fee_earned, from_onchain_str ); // let forwarded_payment = ForwardedPayment { @@ -248,8 +306,8 @@ impl EventHandler for LightningNodeEventHandler { // .unwrap(); } else { println!( - "\nEVENT: Forwarded payment, claiming onchain {}", - from_onchain_str + "\nEVENT: Forwarded payment{}{}, claiming onchain {}", + from_prev_str, to_next_str, from_onchain_str ); } } diff --git a/senseicore/src/network_graph.rs b/senseicore/src/network_graph.rs index 45a1a52..328a66e 100644 --- a/senseicore/src/network_graph.rs +++ b/senseicore/src/network_graph.rs @@ -10,12 +10,11 @@ use bitcoin::secp256k1::PublicKey; use lightning::{ ln::msgs::{self, Init, LightningError, RoutingMessageHandler}, - routing::network_graph::NetworkGraph, util::events::{MessageSendEvent, MessageSendEventsProvider}, }; use std::{ops::Deref, sync::Arc}; -use crate::node::NetworkGraphMessageHandler; +use crate::node::{NetworkGraph, NetworkGraphMessageHandler}; #[derive(Clone)] pub struct SenseiNetworkGraph { diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index a17f7b1..9231797 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -56,7 +56,9 @@ use lightning::ln::peer_handler::{ IgnoringMessageHandler, MessageHandler, PeerManager as LdkPeerManager, }; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph, NodeId, RoutingFees}; +use lightning::routing::gossip::{ + NetworkGraph as LdkNetworkGraph, NodeId, P2PGossipSync, RoutingFees, +}; use lightning::routing::router::{RouteHint, RouteHintHop}; use lightning::routing::scoring::ProbabilisticScorer; use lightning::util::config::UserConfig; @@ -65,6 +67,7 @@ use lightning_background_processor::BackgroundProcessor; use lightning_invoice::utils::DefaultRouter; use lightning_invoice::{payment, utils, Currency, Invoice, InvoiceDescription}; use lightning_net_tokio::SocketDescriptor; +use lightning_rapid_gossip_sync::RapidGossipSync; use macaroon::Macaroon; use rand::{thread_rng, Rng, RngCore}; use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer}; @@ -283,6 +286,11 @@ pub struct PaymentInfo { pub invoice: Option, } +pub type NetworkGraph = LdkNetworkGraph>; + +pub type GossipSync = + lightning_background_processor::GossipSync>, G, A, L>; + pub type ChainMonitor = chainmonitor::ChainMonitor< InMemorySigner, Arc, @@ -335,11 +343,8 @@ pub type SyncableMonitor = ( Arc, ); -pub type NetworkGraphMessageHandler = NetGraphMsgHandler< - Arc, - Arc, - Arc, ->; +pub type NetworkGraphMessageHandler = + P2PGossipSync, Arc, Arc>; fn get_wpkh_descriptors_for_extended_key( xkey: ExtendedKey, @@ -807,6 +812,7 @@ impl LightningNode { chain_manager: chain_manager.clone(), event_sender: event_sender.clone(), broadcaster: broadcaster.clone(), + network_graph: network_graph.clone(), }); let invoice_payer = Arc::new(InvoicePayer::new( @@ -874,7 +880,7 @@ impl LightningNode { invoice_payer.clone(), chain_monitor.clone(), channel_manager.clone(), - Some(network_graph_msg_handler.clone()), + GossipSync::P2P(network_graph_msg_handler.clone()), peer_manager.clone(), logger.clone(), Some(scorer.clone()), @@ -1633,7 +1639,9 @@ pub async fn parse_peer_info( let addr = peer_addr.unwrap().unwrap(); - let listen_addr = public_ip::addr().await.unwrap(); + let listen_addr = public_ip::addr() + .await + .unwrap_or_else(|| [127, 0, 0, 1].into()); let connect_address = match listen_addr == addr.ip() { true => format!("127.0.0.1:{}", addr.port()).parse().unwrap(), diff --git a/senseicore/src/persist.rs b/senseicore/src/persist.rs index f42057a..9f07ab5 100644 --- a/senseicore/src/persist.rs +++ b/senseicore/src/persist.rs @@ -8,7 +8,7 @@ use std::{ sync::Arc, }; -use crate::disk::FilesystemLogger; +use crate::{disk::FilesystemLogger, node::NetworkGraph}; use bitcoin::secp256k1::PublicKey; use bitcoin::{ blockdata::constants::genesis_block, hashes::hex::FromHex, BlockHash, Network, Txid, @@ -19,10 +19,7 @@ use lightning::{ channelmonitor::ChannelMonitor, keysinterface::{KeysInterface, Sign}, }, - routing::{ - network_graph::NetworkGraph, - scoring::{ProbabilisticScorer, ProbabilisticScoringParameters}, - }, + routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters}, util::{ persist::KVStorePersister, ser::{Readable, Writeable}, @@ -179,13 +176,13 @@ impl SenseiPersister { pub fn read_network_graph(&self) -> NetworkGraph { if let Ok(Some(contents)) = self.store.read("network_graph") { let mut cursor = Cursor::new(contents); - if let Ok(graph) = NetworkGraph::read(&mut cursor) { + if let Ok(graph) = NetworkGraph::read(&mut cursor, self.logger.clone()) { return graph; } } let genesis_hash = genesis_block(self.network).header.block_hash(); - NetworkGraph::new(genesis_hash) + NetworkGraph::new(genesis_hash, self.logger.clone()) } pub fn read_scorer( diff --git a/senseicore/src/services/node.rs b/senseicore/src/services/node.rs index 00773bf..7618c03 100644 --- a/senseicore/src/services/node.rs +++ b/senseicore/src/services/node.rs @@ -81,7 +81,7 @@ pub struct Channel { pub confirmations_required: Option, pub force_close_spend_delay: Option, pub is_outbound: bool, - pub is_funding_locked: bool, + pub is_channel_ready: bool, pub is_usable: bool, pub is_public: bool, pub counterparty_pubkey: String, @@ -106,7 +106,7 @@ impl From for Channel { .force_close_spend_delay .map(|delay| delay as u32), is_outbound: channel_detail.is_outbound, - is_funding_locked: channel_detail.is_funding_locked, + is_channel_ready: channel_detail.is_channel_ready, is_usable: channel_detail.is_usable, is_public: channel_detail.is_public, counterparty_pubkey: channel_detail.counterparty.node_id.to_string(), diff --git a/senseicore/tests/smoke_test.rs b/senseicore/tests/smoke_test.rs index 2fa4b87..8d02bae 100644 --- a/senseicore/tests/smoke_test.rs +++ b/senseicore/tests/smoke_test.rs @@ -79,11 +79,11 @@ mod test { .unwrap() { AdminResponse::CreateNode { - id, - listen_addr, - listen_port, + id: _, + listen_addr: _, + listen_port: _, pubkey, - macaroon, + macaroon: _, } => Some(pubkey), _ => None, } @@ -112,10 +112,10 @@ mod test { { AdminResponse::CreateAdmin { pubkey, - macaroon, - id, - token, - role, + macaroon: _, + id: _, + token: _, + role: _, } => { let directory = admin_service.node_directory.lock().await; let handle = directory.get(&pubkey).unwrap().as_ref().unwrap(); @@ -158,23 +158,6 @@ mod test { return false; } - async fn wait_until_async, G: Fn() -> F>( - func: G, - timeout_ms: u64, - interval_ms: u64, - ) -> bool { - let mut current_ms = 0; - while current_ms < timeout_ms { - if func().await { - return true; - } - tokio::time::sleep(Duration::from_millis(interval_ms)).await; - current_ms += interval_ms; - } - - return false; - } - async fn close_channel( bitcoind: &BitcoinD, from: Arc, @@ -331,7 +314,8 @@ mod test { let event = wait_for_event(&mut event_receiver, filter, 15000, 250).await; assert!(event.is_some()); - let funding_txid = match event.unwrap() { + // TODO: looks like I can just remove this? + let _funding_txid = match event.unwrap() { SenseiEvent::TransactionBroadcast { txid, .. } => Some(txid), _ => None, } @@ -551,7 +535,7 @@ mod test { bitcoind: &BitcoinD, persistence_handle: Handle, ) -> AdminService { - let (event_sender, event_receiver): ( + let (event_sender, _event_receiver): ( broadcast::Sender, broadcast::Receiver, ) = broadcast::channel(256); diff --git a/src/grpc/adaptor.rs b/src/grpc/adaptor.rs index a1b5651..18a124e 100644 --- a/src/grpc/adaptor.rs +++ b/src/grpc/adaptor.rs @@ -75,7 +75,7 @@ impl From for ChannelMessage { confirmations_required: channel.confirmations_required, force_close_spend_delay: channel.force_close_spend_delay, is_outbound: channel.is_outbound, - is_funding_locked: channel.is_funding_locked, + is_channel_ready: channel.is_channel_ready, is_usable: channel.is_usable, is_public: channel.is_public, counterparty_pubkey: channel.counterparty_pubkey, From d31adec90f3b6d09e7d49ead3ad697afa0902bf5 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Thu, 9 Jun 2022 11:33:51 -0500 Subject: [PATCH 2/6] expand information returned from get balance --- proto/sensei.proto | 7 +++- senseicore/src/node.rs | 38 +++++++++++++++++-- senseicore/src/services/node.rs | 7 +++- senseicore/tests/smoke_test.rs | 13 ++++--- src/grpc/adaptor.rs | 16 +++++++- system-tests/system-test.py | 6 +-- web-admin/package.json | 2 +- .../src/chain/components/OnChainBalance.tsx | 4 +- 8 files changed, 75 insertions(+), 18 deletions(-) diff --git a/proto/sensei.proto b/proto/sensei.proto index 92fe3e0..bc554e4 100644 --- a/proto/sensei.proto +++ b/proto/sensei.proto @@ -187,7 +187,12 @@ message GetUnusedAddressResponse { message GetBalanceRequest {} message GetBalanceResponse { - uint64 balance_satoshis = 1; + uint64 onchain_balance_sats = 1; + uint64 channel_balance_msats = 2; + uint64 channel_outbound_capacity_msats = 3; + uint64 channel_inbound_capacity_msats = 4; + uint64 usable_channel_outbound_capacity_msats = 5; + uint64 usable_channel_inbound_capacity_msats = 6; } message OpenChannelInfo { diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index 9231797..ec5663b 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -873,7 +873,7 @@ impl LightningNode { let bg_persister = Arc::clone(&persister); - // TODO: should we allow 'child' nodes to update NetworkGraph based on payment failures? + // TODO: should we allow all nodes to update NetworkGraph based on payment failures? // feels like probably but depends on exactly what is updated let background_processor = BackgroundProcessor::start( bg_persister, @@ -1427,10 +1427,40 @@ impl LightningNode { }) } NodeRequest::GetBalance {} => { - let wallet = self.wallet.lock().unwrap(); - let balance = wallet.get_balance().map_err(Error::Bdk)?; + // TODO: split confirmed vs uncofirmed chain balance + // we currently only have 'unconfirmed' utxos from transactions we broadcast + // we never hear about transactions that enter the mempool + let onchain_balance_sats = { + let wallet = self.wallet.lock().unwrap(); + wallet.get_balance().map_err(Error::Bdk)? + }; + + let channels = self.channel_manager.list_channels(); + + let mut channel_balance_msats = 0; + let mut channel_outbound_capacity_msats = 0; + let mut channel_inbound_capacity_msats = 0; + let mut usable_channel_outbound_capacity_msats = 0; + let mut usable_channel_inbound_capacity_msats = 0; + + for channel in channels { + channel_balance_msats += channel.balance_msat; + channel_outbound_capacity_msats += channel.outbound_capacity_msat; + channel_inbound_capacity_msats += channel.inbound_capacity_msat; + + if channel.is_usable { + usable_channel_outbound_capacity_msats += channel.outbound_capacity_msat; + usable_channel_inbound_capacity_msats += channel.inbound_capacity_msat; + } + } + Ok(NodeResponse::GetBalance { - balance_satoshis: balance, + onchain_balance_sats, + channel_balance_msats, + channel_outbound_capacity_msats, + channel_inbound_capacity_msats, + usable_channel_outbound_capacity_msats, + usable_channel_inbound_capacity_msats, }) } NodeRequest::OpenChannels { channels } => { diff --git a/senseicore/src/services/node.rs b/senseicore/src/services/node.rs index 7618c03..ccf4f95 100644 --- a/senseicore/src/services/node.rs +++ b/senseicore/src/services/node.rs @@ -206,7 +206,12 @@ pub enum NodeResponse { address: String, }, GetBalance { - balance_satoshis: u64, + onchain_balance_sats: u64, + channel_balance_msats: u64, + channel_outbound_capacity_msats: u64, + channel_inbound_capacity_msats: u64, + usable_channel_outbound_capacity_msats: u64, + usable_channel_inbound_capacity_msats: u64, }, OpenChannels { channels: Vec, diff --git a/senseicore/tests/smoke_test.rs b/senseicore/tests/smoke_test.rs index 8d02bae..ef486d4 100644 --- a/senseicore/tests/smoke_test.rs +++ b/senseicore/tests/smoke_test.rs @@ -259,9 +259,12 @@ mod test { .unwrap(); } - async fn get_balance(node: Arc) -> u64 { + async fn get_onchain_balance_sats(node: Arc) -> u64 { match node.call(NodeRequest::GetBalance {}).await.unwrap() { - NodeResponse::GetBalance { balance_satoshis } => Some(balance_satoshis), + NodeResponse::GetBalance { + onchain_balance_sats, + .. + } => Some(onchain_balance_sats), _ => None, } .unwrap() @@ -678,9 +681,9 @@ mod test { ) .await; - let alice_balance = get_balance(alice.clone()).await; - let bob_balance = get_balance(bob.clone()).await; - let charlie_balance = get_balance(charlie.clone()).await; + let alice_balance = get_onchain_balance_sats(alice.clone()).await; + let bob_balance = get_onchain_balance_sats(bob.clone()).await; + let charlie_balance = get_onchain_balance_sats(charlie.clone()).await; let alice_initial_balance = 100_000_000 as u64; let bob_initial_balance = 100_000_000 as u64; diff --git a/src/grpc/adaptor.rs b/src/grpc/adaptor.rs index 18a124e..b478b48 100644 --- a/src/grpc/adaptor.rs +++ b/src/grpc/adaptor.rs @@ -195,7 +195,21 @@ impl TryFrom for GetBalanceResponse { fn try_from(res: NodeResponse) -> Result { match res { - NodeResponse::GetBalance { balance_satoshis } => Ok(Self { balance_satoshis }), + NodeResponse::GetBalance { + onchain_balance_sats, + channel_balance_msats, + channel_outbound_capacity_msats, + channel_inbound_capacity_msats, + usable_channel_outbound_capacity_msats, + usable_channel_inbound_capacity_msats, + } => Ok(Self { + onchain_balance_sats, + channel_balance_msats, + channel_outbound_capacity_msats, + channel_inbound_capacity_msats, + usable_channel_outbound_capacity_msats, + usable_channel_inbound_capacity_msats, + }), _ => Err("impossible".to_string()), } } diff --git a/system-tests/system-test.py b/system-tests/system-test.py index 5e80ac9..4f54464 100755 --- a/system-tests/system-test.py +++ b/system-tests/system-test.py @@ -221,7 +221,7 @@ def is_channel_closed(node, meta, is_outbound): def check_balance(node, meta, expected): btc.mine(1) - balance = node.GetBalance(GetBalanceRequest(), metadata=meta).balance_satoshis + balance = node.GetBalance(GetBalanceRequest(), metadata=meta).onchain_balance_sats # print(f'balance {balance} expected {expected}') assert_equal_delta(balance, expected) return True @@ -253,7 +253,7 @@ def fund_node(btc, metadata, grpc, n): address = node.GetUnusedAddress(GetUnusedAddressRequest(), metadata=node_metadata).address btc.sendtoaddress(address, 1) btc.mine(1) - wait_until(f'balance {n}', lambda: node.GetBalance(GetBalanceRequest(), metadata=node_metadata).balance_satoshis > 0) + wait_until(f'balance {n}', lambda: node.GetBalance(GetBalanceRequest(), metadata=node_metadata).onchain_balance_sats > 0) return node, node_metadata, node_res.pubkey @@ -263,7 +263,7 @@ def fund_root_node(btc, metadata): address = node.GetUnusedAddress(GetUnusedAddressRequest(), metadata=metadata).address btc.sendtoaddress(address, 1) btc.mine(1) - wait_until(f'balance root', lambda: node.GetBalance(GetBalanceRequest(), metadata=metadata).balance_satoshis > 0) + wait_until(f'balance root', lambda: node.GetBalance(GetBalanceRequest(), metadata=metadata).onchain_balance_sats > 0) return node diff --git a/web-admin/package.json b/web-admin/package.json index 7952e3c..b6bcac8 100644 --- a/web-admin/package.json +++ b/web-admin/package.json @@ -6,7 +6,7 @@ "dependencies": { "@headlessui/react": "^1.4.2", "@heroicons/react": "^1.0.5", - "@l2-technology/sensei-client": "^0.1.14", + "@l2-technology/sensei-client": "^0.1.16", "@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/typography": "^0.5.0", diff --git a/web-admin/src/chain/components/OnChainBalance.tsx b/web-admin/src/chain/components/OnChainBalance.tsx index 6f6aefc..31222af 100644 --- a/web-admin/src/chain/components/OnChainBalance.tsx +++ b/web-admin/src/chain/components/OnChainBalance.tsx @@ -8,12 +8,12 @@ const OnChainBalance = () => { return null; } - let { balanceSatoshis } = data; + let { onchainBalanceSats } = data; return (
- {Intl.NumberFormat().format(balanceSatoshis)}{" "} + {Intl.NumberFormat().format(onchainBalanceSats)}{" "} sats
From b33c3eae695444e08f6564b3308847b28427fa17 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Sun, 12 Jun 2022 21:58:16 -0400 Subject: [PATCH 3/6] expose more options to channel opens --- Cargo.lock | 28 +-- Cargo.toml | 14 +- proto/sensei.proto | 19 +- senseicore/Cargo.toml | 14 +- senseicore/src/channels.rs | 106 +++++----- senseicore/src/node.rs | 195 ++++++++++-------- senseicore/src/services/node.rs | 52 ++++- senseicore/tests/smoke_test.rs | 58 +++--- src/cli.rs | 23 ++- src/grpc/adaptor.rs | 58 ++++-- src/http/node.rs | 6 +- system-tests/system-test.py | 6 +- web-admin/package-lock.json | 14 +- web-admin/package.json | 2 +- .../src/channels/components/ChannelsList.tsx | 2 +- .../src/channels/mutations/openChannel.ts | 8 +- 16 files changed, 373 insertions(+), 232 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c920716..97c77aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1582,18 +1582,18 @@ dependencies = [ [[package]] name = "lightning" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d26c0da268a793c9c9a37365c4f6c496bfdbcbe8e17f74f226ce04cca3d721" +checksum = "d885bf509066af86ae85354c8959028ad6192c22a2657ef8271e94029d30f9d0" dependencies = [ "bitcoin", ] [[package]] name = "lightning-background-processor" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ab2cb885578117635c9389dbc30ab74c4532e2dddc530215bc7aae5fb6d5f6" +checksum = "0ba6fcb3cef50ae1027a89b40f847b771e831fad843673a350586e29b01b618b" dependencies = [ "bitcoin", "lightning", @@ -1602,9 +1602,9 @@ dependencies = [ [[package]] name = "lightning-block-sync" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90da35aa25b605224c7b99d4d2232183545253570d1edce5ddf007ef4cdb485" +checksum = "b8f1ed50f41785af19f5cd1225b668e87ef0d59bb84e6f8ef2542933e6082a2c" dependencies = [ "bitcoin", "chunked_transfer", @@ -1616,9 +1616,9 @@ dependencies = [ [[package]] name = "lightning-invoice" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40813315a166d0bc053c396b6d2d99b26adcb6db937e073755ca13460eb71630" +checksum = "eaafc1cebaf9ea8d2a57e60aae9fe6095554b8305714f8452cd8a20a3aa5b7ba" dependencies = [ "bech32", "bitcoin_hashes", @@ -1629,9 +1629,9 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1007cef0f7121a2ca68bcf6aadaab4618e2e63570e84dbcb26e17902c1f943" +checksum = "2f0170619152c4d6b947d5ed0de427b85691482a293e0cae52d4336a2220a776" dependencies = [ "bitcoin", "lightning", @@ -1640,9 +1640,9 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513d513afce4c720423828ccac550ca12d00432368b4e5d751790257eb72c078" +checksum = "6e9f154ee5b60e576973da61379767569e2ad7b61a361d716a7d15f37df7e0bc" dependencies = [ "bitcoin", "libc", @@ -1652,9 +1652,9 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" -version = "0.0.107" +version = "0.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80f7131856f0ae61c22665edf953b11890c53a10e83f6d8f2dc01a5026be3d4" +checksum = "08b9947e17c7b97bb267bf3fe6bd51493c1b03a104ab81b246ef3f3ac9077ed9" dependencies = [ "bitcoin", "lightning", diff --git a/Cargo.toml b/Cargo.toml index 93dafae..2fe53cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,13 +15,13 @@ name = "senseid" path = "src/main.rs" [dependencies] -lightning = { version = "0.0.107", features = ["max_level_trace"] } -lightning-block-sync = { version = "0.0.107", features = [ "rpc-client" ] } -lightning-invoice = { version = "0.15.0" } -lightning-net-tokio = { version = "0.0.107" } -lightning-persister = { version = "0.0.107" } -lightning-background-processor = { version = "0.0.107" } -lightning-rapid-gossip-sync = { version = "0.0.107" } +lightning = { version = "0.0.108", features = ["max_level_trace"] } +lightning-block-sync = { version = "0.0.108", features = [ "rpc-client" ] } +lightning-invoice = { version = "0.16.0" } +lightning-net-tokio = { version = "0.0.108" } +lightning-persister = { version = "0.0.108" } +lightning-background-processor = { version = "0.0.108" } +lightning-rapid-gossip-sync = { version = "0.0.108" } base64 = "0.13.0" bitcoin = "0.28.1" bitcoin-bech32 = "0.12" diff --git a/proto/sensei.proto b/proto/sensei.proto index bc554e4..2875bbb 100644 --- a/proto/sensei.proto +++ b/proto/sensei.proto @@ -195,10 +195,19 @@ message GetBalanceResponse { uint64 usable_channel_inbound_capacity_msats = 6; } -message OpenChannelInfo { - string node_connection_string = 1; - uint64 amt_satoshis = 2; + +message OpenChannelRequest { + string counterparty_pubkey = 1; + uint64 amount_sats = 2; bool public = 3; + optional uint64 push_amount_msats = 4; + optional uint64 custom_id = 5; + optional string counterparty_host_port = 6; + optional uint32 forwarding_fee_proportional_millionths = 7; + optional uint32 forwarding_fee_base_msat = 8; + optional uint32 cltv_expiry_delta = 9; + optional uint64 max_dust_htlc_exposure_msat = 10; + optional uint64 force_close_avoidance_max_fee_satoshis = 11; } message OpenChannelResult { @@ -208,10 +217,10 @@ message OpenChannelResult { } message OpenChannelsRequest { - repeated OpenChannelInfo channels = 1; + repeated OpenChannelRequest requests = 1; } message OpenChannelsResponse { - repeated OpenChannelInfo channels = 1; + repeated OpenChannelRequest requests = 1; repeated OpenChannelResult results = 2; } diff --git a/senseicore/Cargo.toml b/senseicore/Cargo.toml index 5ef3348..19d782c 100644 --- a/senseicore/Cargo.toml +++ b/senseicore/Cargo.toml @@ -9,13 +9,13 @@ name = "senseicore" path = "src/lib.rs" [dependencies] -lightning = { version = "0.0.107", features = ["max_level_trace"] } -lightning-block-sync = { version = "0.0.107", features = [ "rpc-client" ] } -lightning-invoice = { version = "0.15.0" } -lightning-net-tokio = { version = "0.0.107" } -lightning-persister = { version = "0.0.107" } -lightning-background-processor = { version = "0.0.107" } -lightning-rapid-gossip-sync = { version = "0.0.107" } +lightning = { version = "0.0.108", features = ["max_level_trace"] } +lightning-block-sync = { version = "0.0.108", features = [ "rpc-client" ] } +lightning-invoice = { version = "0.16.0" } +lightning-net-tokio = { version = "0.0.108" } +lightning-persister = { version = "0.0.108" } +lightning-background-processor = { version = "0.0.108" } +lightning-rapid-gossip-sync = { version = "0.0.108" } base64 = "0.13.0" bitcoin = "0.28" bitcoin-bech32 = "0.12" diff --git a/senseicore/src/channels.rs b/senseicore/src/channels.rs index 9268ce3..b9c077c 100644 --- a/senseicore/src/channels.rs +++ b/senseicore/src/channels.rs @@ -1,11 +1,12 @@ use crate::chain::broadcaster::SenseiBroadcaster; use crate::chain::manager::SenseiChainManager; use crate::error::Error; +use crate::node::{connect_peer_if_necessary, parse_peer_addr, parse_pubkey, PeerManager}; +use crate::services::node::OpenChannelRequest; use crate::{chain::database::WalletDatabase, events::SenseiEvent, node::ChannelManager}; use bdk::{FeeRate, SignOptions}; -use bitcoin::secp256k1::PublicKey; use lightning::chain::chaininterface::ConfirmationTarget; -use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig}; +use rand::{thread_rng, Rng}; use std::sync::{Arc, Mutex}; use std::time::Duration; use tokio::sync::broadcast; @@ -17,15 +18,6 @@ where pub f: F, } -pub struct ChannelOpenRequest { - pub node_connection_string: String, - pub peer_pubkey: PublicKey, - pub channel_amt_sat: u64, - pub push_amt_msat: u64, - pub custom_id: u64, - pub announced_channel: bool, -} - pub struct ChannelOpener { node_id: String, channel_manager: Arc, @@ -33,6 +25,7 @@ pub struct ChannelOpener { chain_manager: Arc, event_receiver: broadcast::Receiver, broadcaster: Arc, + peer_manager: Arc, } impl ChannelOpener { @@ -43,6 +36,7 @@ impl ChannelOpener { wallet: Arc>>, event_receiver: broadcast::Receiver, broadcaster: Arc, + peer_manager: Arc, ) -> Self { Self { node_id, @@ -51,6 +45,7 @@ impl ChannelOpener { wallet, event_receiver, broadcaster, + peer_manager, } } @@ -87,23 +82,28 @@ impl ChannelOpener { pub async fn open_batch( &mut self, - requests: Vec, - ) -> Vec<(ChannelOpenRequest, Result<[u8; 32], Error>)> { - let mut requests_with_results = requests + requests: Vec, + ) -> Vec<(OpenChannelRequest, Result<[u8; 32], Error>)> { + let requests = requests .into_iter() - .map(|request| { - let result = self.initiate_channel_open(&request); - - (request, result) + .map(|request| OpenChannelRequest { + custom_id: Some( + request + .custom_id + .unwrap_or_else(|| thread_rng().gen_range(1..u64::MAX)), + ), + ..request }) .collect::>(); + let mut requests_with_results = vec![]; + let mut filters = vec![]; - let filters = requests_with_results - .iter() - .filter(|(_request, result)| result.is_ok()) - .map(|(request, _result)| { + for request in requests { + let result = self.initiate_channel_open(&request).await; + + if result.is_ok() { let filter_node_id = self.node_id.clone(); - let request_user_channel_id = request.custom_id; + let request_user_channel_id = request.custom_id.unwrap(); let filter = move |event| { if let SenseiEvent::FundingGenerationReady { node_id, @@ -118,9 +118,11 @@ impl ChannelOpener { } false }; - EventFilter { f: filter } - }) - .collect::>>(); + + filters.push(EventFilter { f: filter }) + } + requests_with_results.push((request, result)); + } // TODO: is this appropriate timeout? maybe should accept as param let events = self.wait_for_events(filters, 30000, 500).await; @@ -138,7 +140,7 @@ impl ChannelOpener { .. } = event { - if *user_channel_id == request.custom_id { + if *user_channel_id == request.custom_id.unwrap() { channel_counterparty_node_id = Some(*counterparty_node_id); return true; } @@ -218,32 +220,44 @@ impl ChannelOpener { .collect() } - fn initiate_channel_open(&self, request: &ChannelOpenRequest) -> Result<[u8; 32], Error> { - let config = UserConfig { - peer_channel_config_limits: ChannelHandshakeLimits { - // lnd's max to_self_delay is 2016, so we want to be compatible. - their_to_self_delay: 2016, - ..Default::default() - }, - channel_options: ChannelConfig { - announced_channel: request.announced_channel, - ..Default::default() - }, - ..Default::default() - }; + async fn initiate_channel_open(&self, request: &OpenChannelRequest) -> Result<[u8; 32], Error> { + let counterparty_pubkey = + parse_pubkey(&request.counterparty_pubkey).expect("failed to parse pubkey"); + let already_connected = self + .peer_manager + .get_peer_node_ids() + .contains(&counterparty_pubkey); + if !already_connected { + let counterparty_host_port = request.counterparty_host_port.as_ref().expect("you must provide connection information if you are not already connected to a peer"); + let counterparty_addr = parse_peer_addr(counterparty_host_port) + .await + .expect("failed to parse host port for counterparty"); + connect_peer_if_necessary( + counterparty_pubkey, + counterparty_addr, + self.peer_manager.clone(), + ) + .await + .unwrap_or_else(|_| { + panic!( + "failed to connect to peer {}@{}", + counterparty_pubkey, counterparty_addr + ) + }); + } // TODO: want to be logging channels in db for matching forwarded payments match self.channel_manager.create_channel( - request.peer_pubkey, - request.channel_amt_sat, - request.push_amt_msat, - request.custom_id, - Some(config), + counterparty_pubkey, + request.amount_sats, + request.push_amount_msats.unwrap_or(0), + request.custom_id.unwrap(), + Some(request.into()), ) { Ok(short_channel_id) => { println!( "EVENT: initiated channel with peer {}. ", - request.peer_pubkey + request.counterparty_pubkey ); Ok(short_channel_id) } diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index ec5663b..7be7515 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -11,7 +11,7 @@ use crate::chain::broadcaster::SenseiBroadcaster; use crate::chain::database::WalletDatabase; use crate::chain::fee_estimator::SenseiFeeEstimator; use crate::chain::manager::SenseiChainManager; -use crate::channels::{ChannelOpenRequest, ChannelOpener}; +use crate::channels::ChannelOpener; use crate::config::SenseiConfig; use crate::database::SenseiDatabase; use crate::disk::FilesystemLogger; @@ -21,7 +21,8 @@ use crate::events::SenseiEvent; use crate::network_graph::OptionalNetworkGraphMsgHandler; use crate::persist::{AnyKVStore, DatabaseStore, SenseiPersister}; use crate::services::node::{ - Channel, NodeInfo, NodeRequest, NodeRequestError, NodeResponse, OpenChannelResult, Peer, Utxo, + Channel, NodeInfo, NodeRequest, NodeRequestError, NodeResponse, OpenChannelRequest, + OpenChannelResult, Peer, Utxo, }; use crate::services::{PaginationRequest, PaginationResponse, PaymentsFilter}; use crate::utils::PagedVec; @@ -69,7 +70,7 @@ use lightning_invoice::{payment, utils, Currency, Invoice, InvoiceDescription}; use lightning_net_tokio::SocketDescriptor; use lightning_rapid_gossip_sync::RapidGossipSync; use macaroon::Macaroon; -use rand::{thread_rng, Rng, RngCore}; +use rand::{thread_rng, RngCore}; use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer}; use std::fmt::Display; use std::fs::File; @@ -890,31 +891,56 @@ impl LightningNode { let channel_manager_reconnect = channel_manager.clone(); let peer_manager_reconnect = peer_manager.clone(); - let persister_peer = persister.clone(); + let _persister_peer = persister.clone(); + let network_graph_reconnect = network_graph.clone(); handles.push(tokio::spawn(async move { let mut interval = tokio::time::interval(Duration::from_secs(5)); loop { interval.tick().await; - match persister_peer.read_channel_peer_data().await { - Ok(mut info) => { - for (pubkey, peer_addr) in info.drain() { - for chan_info in channel_manager_reconnect.list_channels() { - if pubkey == chan_info.counterparty.node_id { - let _ = connect_peer_if_necessary( + + for chan_info in channel_manager_reconnect.list_channels() { + let pubkey = chan_info.counterparty.node_id; + if !chan_info.is_usable + && !connected_to_peer(&pubkey, peer_manager_reconnect.clone()) + { + let node_id = NodeId::from_pubkey(&pubkey); + let addresses = { + let network_graph = network_graph_reconnect.read_only(); + network_graph.nodes().get(&node_id).and_then(|info| { + info.announcement_info + .as_ref() + .map(|info| info.addresses.clone()) + }) + }; + + if let Some(addresses) = addresses { + for address in addresses { + let addr = match address { + NetAddress::IPv4 { addr, port } => { + Some(SocketAddr::new(IpAddr::from(addr), port)) + } + NetAddress::IPv6 { addr, port } => { + Some(SocketAddr::new(IpAddr::from(addr), port)) + } + NetAddress::OnionV2(_) => None, + NetAddress::OnionV3 { .. } => None, + }; + + if let Some(addr) = addr { + if let Ok(()) = connect_peer_if_necessary( pubkey, - peer_addr, + addr, peer_manager_reconnect.clone(), ) - .await; + .await + { + break; + } } } } } - Err(e) => println!( - "ERROR: errored reading channel peer info from disk: {:?}", - e - ), - }; + } } })); @@ -988,8 +1014,8 @@ impl LightningNode { pub async fn open_channels( &self, - requests: Vec, - ) -> Vec<(ChannelOpenRequest, Result<[u8; 32], Error>)> { + requests: Vec, + ) -> Vec<(OpenChannelRequest, Result<[u8; 32], Error>)> { let mut opener = ChannelOpener::new( self.id.clone(), self.channel_manager.clone(), @@ -997,13 +1023,14 @@ impl LightningNode { self.wallet.clone(), self.event_sender.subscribe(), self.broadcaster.clone(), + self.peer_manager.clone(), ); opener.open_batch(requests).await } // `custom_id` will be user_channel_id in FundingGenerated event // allows use to tie the create_channel call with the event - pub async fn open_channel(&self, request: ChannelOpenRequest) -> Result<[u8; 32], Error> { + pub async fn open_channel(&self, request: OpenChannelRequest) -> Result<[u8; 32], Error> { let requests = vec![request]; let mut responses = self.open_channels(requests).await; let (_request, result) = responses.pop().unwrap(); @@ -1463,47 +1490,35 @@ impl LightningNode { usable_channel_inbound_capacity_msats, }) } - NodeRequest::OpenChannels { channels } => { - let mut requests = vec![]; - - // TODO: i guess we need to filter out peers we couldn't connect to instead of unwrap() - for channel in &channels { - let (pubkey, addr) = parse_peer_info(channel.node_connection_string.clone()) - .await - .unwrap_or_else(|_| { - panic!( - "failed to parse connection string: {}", - channel.node_connection_string - ) - }); - connect_peer_if_necessary(pubkey, addr, self.peer_manager.clone()) - .await - .unwrap_or_else(|_| { - panic!("failed to connect to peer {}@{}", pubkey, addr) - }); - requests.push(ChannelOpenRequest { - node_connection_string: channel.node_connection_string.clone(), - peer_pubkey: pubkey, - channel_amt_sat: channel.amt_satoshis, - push_amt_msat: 0, - custom_id: thread_rng().gen_range(1..u64::MAX), - announced_channel: channel.public, - }); - } - - let responses = self.open_channels(requests).await; - - let peer_connection_infos = responses - .iter() - .map(|(request, _result)| &request.node_connection_string[..]) - .collect::>(); - - let _ = self - .persister - .batch_persist_channel_peers(&peer_connection_infos); + NodeRequest::OpenChannels { requests } => { + // for channel in &channels { + + // // pub counterparty_pubkey: String, + // // pub amount_sats: u64, + // // pub public: bool, + // // pub counterparty_host_port: Option, + // // pub push_amount_sats: Option, + // // pub forwarding_fee_proportional_millionths: Option, + // // pub forwarding_fee_base_msat: Option, + // // pub cltv_expiry_delta: Option, + // // pub max_dust_htlc_exposure_msat: Option, + // // pub force_close_avoidance_max_fee_satoshis: Option + + // // TODO: these panics are terribile. need to filter them out of the requests instead. + + // requests.push(ChannelOpenRequest { + // peer_pubkey: pubkey, + // amount_sats: channel.amount_sats, + // push_amount_msats: channel.push_amount_msats.unwrap_or(0), + // custom_id: channel.custom_id.unwrap_or_else(|| { thread_rng().gen_range(1..u64::MAX)}), + // announced_channel: channel.public, + // }); + // } + + let responses = self.open_channels(requests.clone()).await; Ok(NodeResponse::OpenChannels { - channels, + requests, results: responses .into_iter() .map(|(_request, result)| match result { @@ -1635,35 +1650,24 @@ impl LightningNode { } } -pub async fn parse_peer_info( - peer_pubkey_and_ip_addr: String, -) -> Result<(PublicKey, SocketAddr), std::io::Error> { - let mut pubkey_and_addr = peer_pubkey_and_ip_addr.split('@'); - let pubkey = pubkey_and_addr.next(); - let peer_addr_str = pubkey_and_addr.next(); - if peer_addr_str.is_none() || peer_addr_str.is_none() { +pub fn parse_pubkey(pubkey: &str) -> Result { + let pubkey = hex_utils::to_compressed_pubkey(pubkey); + if pubkey.is_none() { return Err(std::io::Error::new( std::io::ErrorKind::Other, - "ERROR: incorrectly formatted peer info. Should be formatted as: `pubkey@host:port`", + "ERROR: unable to parse given pubkey for node", )); } + Ok(pubkey.unwrap()) +} - let peer_addr = peer_addr_str - .unwrap() - .to_socket_addrs() - .map(|mut r| r.next()); - if peer_addr.is_err() || peer_addr.as_ref().unwrap().is_none() { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "ERROR: couldn't parse pubkey@host:port into a socket address", - )); - } +pub async fn parse_peer_addr(peer_addr_str: &str) -> Result { + let peer_addr = peer_addr_str.to_socket_addrs().map(|mut r| r.next()); - let pubkey = hex_utils::to_compressed_pubkey(pubkey.unwrap()); - if pubkey.is_none() { + if peer_addr.is_err() || peer_addr.as_ref().unwrap().is_none() { return Err(std::io::Error::new( std::io::ErrorKind::Other, - "ERROR: unable to parse given pubkey for node", + "ERROR: couldn't parse host:port into a socket address", )); } @@ -1678,7 +1682,30 @@ pub async fn parse_peer_info( false => addr, }; - Ok((pubkey.unwrap(), connect_address)) + Ok(connect_address) +} + +pub async fn parse_peer_info( + peer_pubkey_and_ip_addr: String, +) -> Result<(PublicKey, SocketAddr), std::io::Error> { + let mut pubkey_and_addr = peer_pubkey_and_ip_addr.split('@'); + let pubkey = pubkey_and_addr.next(); + let peer_addr_str = pubkey_and_addr.next(); + if pubkey.is_none() || peer_addr_str.is_none() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "ERROR: incorrectly formatted peer info. Should be formatted as: `pubkey@host:port`", + )); + } + + let pubkey = parse_pubkey(pubkey.unwrap())?; + let connect_address = parse_peer_addr(peer_addr_str.unwrap()).await?; + + Ok((pubkey, connect_address)) +} + +pub fn connected_to_peer(pubkey: &PublicKey, peer_manager: Arc) -> bool { + peer_manager.get_peer_node_ids().contains(pubkey) } pub(crate) async fn connect_peer_if_necessary( @@ -1686,10 +1713,8 @@ pub(crate) async fn connect_peer_if_necessary( peer_addr: SocketAddr, peer_manager: Arc, ) -> Result<(), ()> { - for node_pubkey in peer_manager.get_peer_node_ids() { - if node_pubkey == pubkey { - return Ok(()); - } + if connected_to_peer(&pubkey, peer_manager.clone()) { + return Ok(()); } match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await diff --git a/senseicore/src/services/node.rs b/senseicore/src/services/node.rs index ccf4f95..980cfb9 100644 --- a/senseicore/src/services/node.rs +++ b/senseicore/src/services/node.rs @@ -10,6 +10,7 @@ use crate::node::{LightningNode, LocalInvoice}; use bdk::TransactionDetails; use futures::Future; +use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig}; use std::pin::Pin; use std::task::{Context, Poll}; use tower::Service; @@ -116,10 +117,51 @@ impl From for Channel { } #[derive(Deserialize, Serialize, Clone, Debug)] -pub struct OpenChannelInfo { - pub node_connection_string: String, - pub amt_satoshis: u64, +pub struct OpenChannelRequest { + pub counterparty_pubkey: String, + pub amount_sats: u64, pub public: bool, + pub custom_id: Option, + pub push_amount_msats: Option, + pub counterparty_host_port: Option, + pub forwarding_fee_proportional_millionths: Option, + pub forwarding_fee_base_msat: Option, + pub cltv_expiry_delta: Option, + pub max_dust_htlc_exposure_msat: Option, + pub force_close_avoidance_max_fee_satoshis: Option, +} + +impl From<&OpenChannelRequest> for UserConfig { + fn from(request: &OpenChannelRequest) -> Self { + let default_channel_config = ChannelConfig::default(); + Self { + peer_channel_config_limits: ChannelHandshakeLimits { + // lnd's max to_self_delay is 2016, so we want to be compatible. + their_to_self_delay: 2016, + ..Default::default() + }, + channel_options: ChannelConfig { + announced_channel: request.public, + forwarding_fee_proportional_millionths: request + .forwarding_fee_proportional_millionths + .unwrap_or(default_channel_config.forwarding_fee_proportional_millionths), + forwarding_fee_base_msat: request + .forwarding_fee_base_msat + .unwrap_or(default_channel_config.forwarding_fee_base_msat), + cltv_expiry_delta: request + .cltv_expiry_delta + .unwrap_or(default_channel_config.cltv_expiry_delta), + max_dust_htlc_exposure_msat: request + .max_dust_htlc_exposure_msat + .unwrap_or(default_channel_config.max_dust_htlc_exposure_msat), + force_close_avoidance_max_fee_satoshis: request + .force_close_avoidance_max_fee_satoshis + .unwrap_or(default_channel_config.force_close_avoidance_max_fee_satoshis), + ..Default::default() + }, + ..Default::default() + } + } } #[derive(Serialize, Clone, Debug)] @@ -145,7 +187,7 @@ pub enum NodeRequest { GetUnusedAddress {}, GetBalance {}, OpenChannels { - channels: Vec, + requests: Vec, }, SendPayment { invoice: String, @@ -214,7 +256,7 @@ pub enum NodeResponse { usable_channel_inbound_capacity_msats: u64, }, OpenChannels { - channels: Vec, + requests: Vec, results: Vec, }, SendPayment {}, diff --git a/senseicore/tests/smoke_test.rs b/senseicore/tests/smoke_test.rs index ef486d4..93bacc3 100644 --- a/senseicore/tests/smoke_test.rs +++ b/senseicore/tests/smoke_test.rs @@ -8,7 +8,7 @@ mod test { use migration::{Migrator, MigratorTrait}; use senseicore::events::SenseiEvent; use senseicore::node::{HTLCStatus, LightningNode}; - use senseicore::services::node::{Channel, OpenChannelInfo}; + use senseicore::services::node::{Channel, OpenChannelRequest}; use senseicore::services::{PaginationRequest, PaymentsFilter}; use serial_test::serial; use std::{str::FromStr, sync::Arc, time::Duration}; @@ -278,28 +278,31 @@ mod test { ) -> Vec<(Channel, Arc)> { let miner_address = bitcoind.client.get_new_address(None, None).unwrap(); - let channel_infos = to + let channel_requests = to .iter() - .map(|to| { - let node_connection_string = format!( - "{}@{}:{}", - to.get_pubkey(), + .map(|to| OpenChannelRequest { + counterparty_pubkey: to.get_pubkey(), + counterparty_host_port: Some(format!( + "{}:{}", to.listen_addresses.first().unwrap(), to.listen_port - ); - - OpenChannelInfo { - node_connection_string, - amt_satoshis: amt_sat, - public: true, - } + )), + amount_sats: amt_sat, + public: true, + custom_id: None, + push_amount_msats: None, + forwarding_fee_proportional_millionths: None, + forwarding_fee_base_msat: None, + cltv_expiry_delta: None, + max_dust_htlc_exposure_msat: None, + force_close_avoidance_max_fee_satoshis: None, }) - .collect::>(); + .collect::>(); let mut event_receiver = from.event_sender.subscribe(); from.call(NodeRequest::OpenChannels { - channels: channel_infos, + requests: channel_requests, }) .await .unwrap(); @@ -394,20 +397,25 @@ mod test { amt_sat: u64, ) -> Channel { let miner_address = bitcoind.client.get_new_address(None, None).unwrap(); - let node_connection_string = format!( - "{}@{}:{}", - to.get_pubkey(), - to.listen_addresses.first().unwrap(), - to.listen_port - ); - let mut event_receiver = from.event_sender.subscribe(); from.call(NodeRequest::OpenChannels { - channels: vec![OpenChannelInfo { - node_connection_string: node_connection_string, - amt_satoshis: amt_sat, + requests: vec![OpenChannelRequest { + counterparty_pubkey: to.get_pubkey(), + counterparty_host_port: Some(format!( + "{}:{}", + to.listen_addresses.first().unwrap(), + to.listen_port + )), + amount_sats: amt_sat, public: true, + custom_id: None, + push_amount_msats: None, + forwarding_fee_proportional_millionths: None, + forwarding_fee_base_msat: None, + cltv_expiry_delta: None, + max_dust_htlc_exposure_msat: None, + force_close_avoidance_max_fee_satoshis: None, }], }) .await diff --git a/src/cli.rs b/src/cli.rs index a3c6475..e5a8772 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -21,9 +21,9 @@ use tonic::{metadata::MetadataValue, transport::Channel, Request}; use crate::sensei::{ CloseChannelRequest, ConnectPeerRequest, CreateAdminRequest, CreateInvoiceRequest, CreateNodeRequest, GetUnusedAddressRequest, InfoRequest, KeysendRequest, ListChannelsRequest, - ListNodesRequest, ListPaymentsRequest, ListPeersRequest, ListUnspentRequest, OpenChannelInfo, - OpenChannelsRequest, PayInvoiceRequest, SignMessageRequest, StartAdminRequest, - StartNodeRequest, + ListNodesRequest, ListPaymentsRequest, ListPeersRequest, ListUnspentRequest, + OpenChannelRequest, OpenChannelsRequest, PayInvoiceRequest, SignMessageRequest, + StartAdminRequest, StartNodeRequest, }; pub mod sensei { @@ -341,11 +341,22 @@ async fn main() -> Result<(), Box> { .parse() .expect("public must be true or false"); + let mut split = node_connection_string.split('@'); + let pubkey = split.next().expect("you must provide pubkey@host:port"); + let host_and_port = split.next().unwrap(); let request = tonic::Request::new(OpenChannelsRequest { - channels: vec![OpenChannelInfo { - node_connection_string: node_connection_string.to_string(), - amt_satoshis, + requests: vec![OpenChannelRequest { + counterparty_pubkey: pubkey.to_string(), + amount_sats: amt_satoshis, public, + push_amount_msats: None, + custom_id: None, + counterparty_host_port: Some(host_and_port.to_string()), + forwarding_fee_proportional_millionths: None, + forwarding_fee_base_msat: None, + cltv_expiry_delta: None, + max_dust_htlc_exposure_msat: None, + force_close_avoidance_max_fee_satoshis: None, }], }); diff --git a/src/grpc/adaptor.rs b/src/grpc/adaptor.rs index b478b48..2746dbd 100644 --- a/src/grpc/adaptor.rs +++ b/src/grpc/adaptor.rs @@ -9,10 +9,11 @@ use super::sensei::{ self, Channel as ChannelMessage, DeletePaymentRequest, DeletePaymentResponse, - Info as InfoMessage, LabelPaymentRequest, LabelPaymentResponse, OpenChannelsRequest, - OpenChannelsResponse, PaginationRequest, PaginationResponse, Payment as PaymentMessage, - PaymentsFilter, Peer as PeerMessage, StartNodeRequest, StartNodeResponse, StopNodeRequest, - StopNodeResponse, Utxo as UtxoMessage, + Info as InfoMessage, LabelPaymentRequest, LabelPaymentResponse, + OpenChannelRequest as GrpcOpenChannelRequest, OpenChannelsRequest, OpenChannelsResponse, + PaginationRequest, PaginationResponse, Payment as PaymentMessage, PaymentsFilter, + Peer as PeerMessage, StartNodeRequest, StartNodeResponse, StopNodeRequest, StopNodeResponse, + Utxo as UtxoMessage, }; use super::sensei::{ @@ -26,6 +27,7 @@ use super::sensei::{ VerifyMessageResponse, }; +use senseicore::services::node::OpenChannelRequest; use senseicore::services::{ self, node::{Channel, NodeInfo, NodeRequest, NodeResponse, Peer, Utxo}, @@ -218,13 +220,25 @@ impl TryFrom for GetBalanceResponse { impl From for NodeRequest { fn from(req: OpenChannelsRequest) -> Self { NodeRequest::OpenChannels { - channels: req - .channels + requests: req + .requests .into_iter() - .map(|channel| services::node::OpenChannelInfo { - node_connection_string: channel.node_connection_string, - amt_satoshis: channel.amt_satoshis, - public: channel.public, + .map(|request| OpenChannelRequest { + counterparty_pubkey: request.counterparty_pubkey, + amount_sats: request.amount_sats, + public: request.public, + custom_id: request.custom_id, + push_amount_msats: request.push_amount_msats, + counterparty_host_port: request.counterparty_host_port, + forwarding_fee_proportional_millionths: request + .forwarding_fee_proportional_millionths, + forwarding_fee_base_msat: request.forwarding_fee_base_msat, + cltv_expiry_delta: request + .cltv_expiry_delta + .map(|cltv_delta| cltv_delta as u16), + max_dust_htlc_exposure_msat: request.max_dust_htlc_exposure_msat, + force_close_avoidance_max_fee_satoshis: request + .force_close_avoidance_max_fee_satoshis, }) .collect::>(), } @@ -236,13 +250,25 @@ impl TryFrom for OpenChannelsResponse { fn try_from(res: NodeResponse) -> Result { match res { - NodeResponse::OpenChannels { channels, results } => Ok(Self { - channels: channels + NodeResponse::OpenChannels { requests, results } => Ok(Self { + requests: requests .into_iter() - .map(|channel| sensei::OpenChannelInfo { - node_connection_string: channel.node_connection_string, - amt_satoshis: channel.amt_satoshis, - public: channel.public, + .map(|request| GrpcOpenChannelRequest { + counterparty_pubkey: request.counterparty_pubkey, + amount_sats: request.amount_sats, + public: request.public, + custom_id: request.custom_id, + push_amount_msats: request.push_amount_msats, + counterparty_host_port: request.counterparty_host_port, + forwarding_fee_proportional_millionths: request + .forwarding_fee_proportional_millionths, + forwarding_fee_base_msat: request.forwarding_fee_base_msat, + cltv_expiry_delta: request + .cltv_expiry_delta + .map(|cltv_delta| cltv_delta.try_into().unwrap()), + max_dust_htlc_exposure_msat: request.max_dust_htlc_exposure_msat, + force_close_avoidance_max_fee_satoshis: request + .force_close_avoidance_max_fee_satoshis, }) .collect::>(), results: results diff --git a/src/http/node.rs b/src/http/node.rs index c65a826..918607a 100644 --- a/src/http/node.rs +++ b/src/http/node.rs @@ -16,7 +16,7 @@ use axum::routing::{get, post}; use axum::Router; use http::{HeaderValue, StatusCode}; use senseicore::services::admin::AdminRequest; -use senseicore::services::node::{NodeRequest, NodeRequestError, NodeResponse, OpenChannelInfo}; +use senseicore::services::node::{NodeRequest, NodeRequestError, NodeResponse, OpenChannelRequest}; use senseicore::services::{ListChannelsParams, ListPaymentsParams, ListTransactionsParams}; use senseicore::utils; use serde::Deserialize; @@ -70,13 +70,13 @@ impl From for NodeRequest { #[derive(Deserialize)] pub struct BatchOpenChannelParams { - channels: Vec, + requests: Vec, } impl From for NodeRequest { fn from(params: BatchOpenChannelParams) -> Self { Self::OpenChannels { - channels: params.channels, + requests: params.requests, } } } diff --git a/system-tests/system-test.py b/system-tests/system-test.py index 4f54464..4369ab2 100755 --- a/system-tests/system-test.py +++ b/system-tests/system-test.py @@ -19,7 +19,7 @@ from sensei_pb2 import ( CloseChannelRequest, GetStatusRequest, CreateNodeRequest, GetUnusedAddressRequest, GetBalanceRequest, - ListPaymentsRequest, OpenChannelsRequest, OpenChannelInfo, ListChannelsRequest, CreateInvoiceRequest, + ListPaymentsRequest, OpenChannelsRequest, OpenChannelRequest, ListChannelsRequest, CreateInvoiceRequest, PaginationRequest, PayInvoiceRequest ) @@ -148,7 +148,7 @@ def run(): bob, meta_b, id_b = fund_node(btc, metadata, senseid, 1) print('Create channel alice -> bob') - oc_res = alice.OpenChannels(OpenChannelsRequest(channels=[OpenChannelInfo(node_connection_string=f"{id_b}@127.0.0.1:10000", amt_satoshis=CHANNEL_VALUE_SAT, public=True)]), + oc_res = alice.OpenChannels(OpenChannelsRequest(requests=[OpenChannelRequest(counterparty_pubkey=f"{id_b}", counterparty_host_port=f"127.0.0.1:10000", amount_sats=CHANNEL_VALUE_SAT, public=True)]), metadata=meta_a) print(oc_res) @@ -158,7 +158,7 @@ def run(): charlie, meta_c, id_c = fund_node(btc, metadata, senseid, 2) print('Create channel bob -> charlie') - bob.OpenChannels(OpenChannelsRequest(channels=[OpenChannelInfo(node_connection_string=f"{id_c}@127.0.0.1:10001", amt_satoshis=CHANNEL_VALUE_SAT, public=True)]), + bob.OpenChannels(OpenChannelsRequest(requests=[OpenChannelRequest(counterparty_pubkey=f"{id_c}", counterparty_host_port=f"127.0.0.1:10001", amount_sats=CHANNEL_VALUE_SAT, public=True)]), metadata=meta_b) wait_until('channel at charlie', lambda: charlie.ListChannels(ListChannelsRequest(), metadata=meta_c).channels[0]) assert not charlie.ListChannels(ListChannelsRequest(), metadata=meta_c).channels[0].is_usable diff --git a/web-admin/package-lock.json b/web-admin/package-lock.json index 74bc649..fdf598a 100644 --- a/web-admin/package-lock.json +++ b/web-admin/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@headlessui/react": "^1.4.2", "@heroicons/react": "^1.0.5", - "@l2-technology/sensei-client": "^0.1.14", + "@l2-technology/sensei-client": "^0.1.17", "@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/typography": "^0.5.0", @@ -3189,9 +3189,9 @@ } }, "node_modules/@l2-technology/sensei-client": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@l2-technology/sensei-client/-/sensei-client-0.1.14.tgz", - "integrity": "sha512-d9D0rXjSLiwcwLgO1JU6JvZ5/Plo7H/QNYuEzsONudWvpy9j5KYTUCE9tkioxom/LloddMQkU8ilH4BsJcGEWw==" + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@l2-technology/sensei-client/-/sensei-client-0.1.17.tgz", + "integrity": "sha512-vRZ52cjt4otX9/tUoiEM7b/MF8ZEcrlfZoEodHNdMyJ2g8OxTaLEjXeIXGD0nHJNPYgMONC+8jOU0//XGIgRGg==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -25922,9 +25922,9 @@ } }, "@l2-technology/sensei-client": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/@l2-technology/sensei-client/-/sensei-client-0.1.14.tgz", - "integrity": "sha512-d9D0rXjSLiwcwLgO1JU6JvZ5/Plo7H/QNYuEzsONudWvpy9j5KYTUCE9tkioxom/LloddMQkU8ilH4BsJcGEWw==" + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@l2-technology/sensei-client/-/sensei-client-0.1.17.tgz", + "integrity": "sha512-vRZ52cjt4otX9/tUoiEM7b/MF8ZEcrlfZoEodHNdMyJ2g8OxTaLEjXeIXGD0nHJNPYgMONC+8jOU0//XGIgRGg==" }, "@nodelib/fs.scandir": { "version": "2.1.5", diff --git a/web-admin/package.json b/web-admin/package.json index b6bcac8..60a5f67 100644 --- a/web-admin/package.json +++ b/web-admin/package.json @@ -6,7 +6,7 @@ "dependencies": { "@headlessui/react": "^1.4.2", "@heroicons/react": "^1.0.5", - "@l2-technology/sensei-client": "^0.1.16", + "@l2-technology/sensei-client": "^0.1.17", "@tailwindcss/aspect-ratio": "^0.4.0", "@tailwindcss/forms": "^0.4.0", "@tailwindcss/typography": "^0.5.0", diff --git a/web-admin/src/channels/components/ChannelsList.tsx b/web-admin/src/channels/components/ChannelsList.tsx index 4097a94..c2f7ec3 100644 --- a/web-admin/src/channels/components/ChannelsList.tsx +++ b/web-admin/src/channels/components/ChannelsList.tsx @@ -198,7 +198,7 @@ const ChannelsList = () => { const transformResults = (channels: Channel[]) => { return channels.map((channel) => { - let status = channel.isFundingLocked + let status = channel.isChannelReady ? channel.isUsable ? "ready" : "counterparty_offline" diff --git a/web-admin/src/channels/mutations/openChannel.ts b/web-admin/src/channels/mutations/openChannel.ts index e1dd2bd..db5acdb 100644 --- a/web-admin/src/channels/mutations/openChannel.ts +++ b/web-admin/src/channels/mutations/openChannel.ts @@ -5,7 +5,13 @@ const openChannel = async ( amtSatoshis: number, isPublic: boolean ) => { - return await sensei.openChannel(nodeConnectionString, amtSatoshis, isPublic); + const connectionParts = nodeConnectionString.split("@"); + return await sensei.openChannel({ + counterpartyPubkey: connectionParts[0], + counterpartyHostPort: connectionParts[1], + amountSats: amtSatoshis, + public: isPublic + }); }; export default openChannel; From f8e25b142cbba2a8f88c8d24083c60455cb8d957 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Sun, 12 Jun 2022 22:02:01 -0400 Subject: [PATCH 4/6] update to bdk 0.19 --- Cargo.lock | 138 +++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- entity/Cargo.toml | 2 +- senseicore/Cargo.toml | 2 +- 4 files changed, 138 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 97c77aa..fe543be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,8 +344,9 @@ dependencies = [ [[package]] name = "bdk" -version = "0.19.0-dev" -source = "git+https://github.com/bitcoindevkit/bdk?rev=fbd98b4c5a88a10f8dfc22b4c54187eba4d68afe#fbd98b4c5a88a10f8dfc22b4c54187eba4d68afe" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2c4c49915b82a2576bc7dee83d2f274387904b79305e6ae8b622daa0b282c1" dependencies = [ "async-trait", "bdk-macros", @@ -599,6 +600,15 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -1052,6 +1062,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures" version = "0.3.21" @@ -2240,6 +2256,25 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.8", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -2250,7 +2285,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", ] [[package]] @@ -2264,6 +2299,16 @@ dependencies = [ "rand_core 0.6.3", ] +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.3.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2284,6 +2329,21 @@ dependencies = [ "rand_core 0.6.3", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -2302,6 +2362,15 @@ dependencies = [ "getrandom 0.2.6", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -2311,6 +2380,68 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.8", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.2.13" @@ -2676,6 +2807,7 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26947345339603ae8395f68e2f3d85a6b0a8ddfe6315818e80b8504415099db0" dependencies = [ + "rand 0.6.5", "secp256k1-sys", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 2fe53cd..7a3f712 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ tokio = { version = "1.17", features = [ "io-util", "macros", "rt", "rt-multi-th log = "0.4.16" env_logger = "0.9.0" bitcoincore-rpc = "0.15" -bdk = { git = "https://github.com/bitcoindevkit/bdk", rev = "fbd98b4c5a88a10f8dfc22b4c54187eba4d68afe" } +bdk = "0.19" tonic = "0.6" prost = "0.9" pin-project = "1.0" diff --git a/entity/Cargo.toml b/entity/Cargo.toml index d295321..8f16530 100644 --- a/entity/Cargo.toml +++ b/entity/Cargo.toml @@ -11,7 +11,7 @@ path = "src/lib.rs" [dependencies] serde = { version = "1", features = ["derive"] } serde_json = { version = "1.0" } -bdk = { git = "https://github.com/bitcoindevkit/bdk", rev = "fbd98b4c5a88a10f8dfc22b4c54187eba4d68afe" } +bdk = "0.19" [dependencies.sea-orm] version = "^0.7.1" diff --git a/senseicore/Cargo.toml b/senseicore/Cargo.toml index 19d782c..5845ae5 100644 --- a/senseicore/Cargo.toml +++ b/senseicore/Cargo.toml @@ -32,7 +32,7 @@ tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-threa log = "0.4.16" env_logger = "0.9.0" bitcoincore-rpc = "0.15" -bdk = { git = "https://github.com/bitcoindevkit/bdk", rev = "fbd98b4c5a88a10f8dfc22b4c54187eba4d68afe" } +bdk = "0.19" pin-project = "1.0" hyper = "0.14" clap = { version = "3.0", features = [ "derive", "env" ] } From 87b538e460779e00695353b90ba2e15d2fc80564 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Sun, 12 Jun 2022 22:52:03 -0400 Subject: [PATCH 5/6] add network graph info endpoint --- proto/sensei.proto | 8 ++++++++ senseicore/src/node.rs | 20 +++++++++++++++++++ senseicore/src/services/node.rs | 6 ++++++ src/cli.rs | 9 +++++++-- src/grpc/adaptor.rs | 35 ++++++++++++++++++++++++++++----- src/grpc/node.rs | 17 +++++++++++++--- src/http/node.rs | 15 ++++++++++++++ 7 files changed, 100 insertions(+), 10 deletions(-) diff --git a/proto/sensei.proto b/proto/sensei.proto index 2875bbb..98ae8ef 100644 --- a/proto/sensei.proto +++ b/proto/sensei.proto @@ -36,6 +36,7 @@ service Node { rpc SignMessage (SignMessageRequest) returns (SignMessageResponse); rpc VerifyMessage (VerifyMessageRequest) returns (VerifyMessageResponse); rpc ListUnspent (ListUnspentRequest) returns (ListUnspentResponse); + rpc NetworkGraphInfo (NetworkGraphInfoRequest) returns (NetworkGraphInfoResponse); } message ListNode { @@ -409,3 +410,10 @@ message Utxo { message ListUnspentResponse { repeated Utxo utxos = 1; } + +message NetworkGraphInfoRequest {} +message NetworkGraphInfoResponse { + uint64 num_channels = 1; + uint64 num_nodes = 2; + uint64 num_known_edge_policies = 3; +} diff --git a/senseicore/src/node.rs b/senseicore/src/node.rs index 7be7515..d006fc4 100644 --- a/senseicore/src/node.rs +++ b/senseicore/src/node.rs @@ -1646,6 +1646,26 @@ impl LightningNode { let utxos = self.list_unspent()?; Ok(NodeResponse::ListUnspent { utxos }) } + NodeRequest::NetworkGraphInfo {} => { + let graph = self.network_graph.read_only(); + let channels = graph.channels(); + + let mut num_known_edge_policies: u64 = 0; + for (_key, channel_info) in channels.iter() { + if channel_info.one_to_two.is_some() { + num_known_edge_policies += 1; + } + if channel_info.two_to_one.is_some() { + num_known_edge_policies += 1; + } + } + + Ok(NodeResponse::NetworkGraphInfo { + num_channels: graph.channels().len() as u64, + num_nodes: graph.nodes().len() as u64, + num_known_edge_policies, + }) + } } } } diff --git a/senseicore/src/services/node.rs b/senseicore/src/services/node.rs index 980cfb9..810fc7f 100644 --- a/senseicore/src/services/node.rs +++ b/senseicore/src/services/node.rs @@ -237,6 +237,7 @@ pub enum NodeRequest { signature: String, }, ListUnspent {}, + NetworkGraphInfo {}, } #[derive(Serialize)] @@ -299,6 +300,11 @@ pub enum NodeResponse { ListUnspent { utxos: Vec, }, + NetworkGraphInfo { + num_channels: u64, + num_nodes: u64, + num_known_edge_policies: u64, + }, Error(NodeRequestError), } diff --git a/src/cli.rs b/src/cli.rs index e5a8772..40f9cb6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -22,8 +22,8 @@ use crate::sensei::{ CloseChannelRequest, ConnectPeerRequest, CreateAdminRequest, CreateInvoiceRequest, CreateNodeRequest, GetUnusedAddressRequest, InfoRequest, KeysendRequest, ListChannelsRequest, ListNodesRequest, ListPaymentsRequest, ListPeersRequest, ListUnspentRequest, - OpenChannelRequest, OpenChannelsRequest, PayInvoiceRequest, SignMessageRequest, - StartAdminRequest, StartNodeRequest, + NetworkGraphInfoRequest, OpenChannelRequest, OpenChannelsRequest, PayInvoiceRequest, + SignMessageRequest, StartAdminRequest, StartNodeRequest, }; pub mod sensei { @@ -462,6 +462,11 @@ async fn main() -> Result<(), Box> { let response = client.list_unspent(request).await?; println!("{:?}", response.into_inner()); } + "networkgraphinfo" => { + let request = tonic::Request::new(NetworkGraphInfoRequest {}); + let response = client.network_graph_info(request).await?; + println!("{:?}", response.into_inner()); + } "nodeinfo" => { let request = tonic::Request::new(InfoRequest {}); let response = client.info(request).await?; diff --git a/src/grpc/adaptor.rs b/src/grpc/adaptor.rs index 2746dbd..5bb2b97 100644 --- a/src/grpc/adaptor.rs +++ b/src/grpc/adaptor.rs @@ -9,11 +9,11 @@ use super::sensei::{ self, Channel as ChannelMessage, DeletePaymentRequest, DeletePaymentResponse, - Info as InfoMessage, LabelPaymentRequest, LabelPaymentResponse, - OpenChannelRequest as GrpcOpenChannelRequest, OpenChannelsRequest, OpenChannelsResponse, - PaginationRequest, PaginationResponse, Payment as PaymentMessage, PaymentsFilter, - Peer as PeerMessage, StartNodeRequest, StartNodeResponse, StopNodeRequest, StopNodeResponse, - Utxo as UtxoMessage, + Info as InfoMessage, LabelPaymentRequest, LabelPaymentResponse, NetworkGraphInfoRequest, + NetworkGraphInfoResponse, OpenChannelRequest as GrpcOpenChannelRequest, OpenChannelsRequest, + OpenChannelsResponse, PaginationRequest, PaginationResponse, Payment as PaymentMessage, + PaymentsFilter, Peer as PeerMessage, StartNodeRequest, StartNodeResponse, StopNodeRequest, + StopNodeResponse, Utxo as UtxoMessage, }; use super::sensei::{ @@ -607,3 +607,28 @@ impl TryFrom for ListUnspentResponse { } } } + +impl From for NodeRequest { + fn from(_req: NetworkGraphInfoRequest) -> Self { + NodeRequest::NetworkGraphInfo {} + } +} + +impl TryFrom for NetworkGraphInfoResponse { + type Error = String; + + fn try_from(res: NodeResponse) -> Result { + match res { + NodeResponse::NetworkGraphInfo { + num_channels, + num_nodes, + num_known_edge_policies, + } => Ok(Self { + num_channels, + num_nodes, + num_known_edge_policies, + }), + _ => Err("impossible".to_string()), + } + } +} diff --git a/src/grpc/node.rs b/src/grpc/node.rs index 525d831..19b41d4 100644 --- a/src/grpc/node.rs +++ b/src/grpc/node.rs @@ -20,9 +20,10 @@ use super::{ KeysendRequest, KeysendResponse, LabelPaymentRequest, LabelPaymentResponse, ListChannelsRequest, ListChannelsResponse, ListPaymentsRequest, ListPaymentsResponse, ListPeersRequest, ListPeersResponse, ListUnspentRequest, ListUnspentResponse, - OpenChannelsRequest, OpenChannelsResponse, PayInvoiceRequest, PayInvoiceResponse, - SignMessageRequest, SignMessageResponse, StartNodeRequest, StartNodeResponse, - StopNodeRequest, StopNodeResponse, VerifyMessageRequest, VerifyMessageResponse, + NetworkGraphInfoRequest, NetworkGraphInfoResponse, OpenChannelsRequest, + OpenChannelsResponse, PayInvoiceRequest, PayInvoiceResponse, SignMessageRequest, + SignMessageResponse, StartNodeRequest, StartNodeResponse, StopNodeRequest, + StopNodeResponse, VerifyMessageRequest, VerifyMessageResponse, }, utils::raw_macaroon_from_metadata, }; @@ -303,4 +304,14 @@ impl Node for NodeService { .map(Response::new) .map_err(|_e| Status::unknown("unknown error")) } + async fn network_graph_info( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + self.authenticated_request(request.metadata().clone(), request.into_inner().into()) + .await? + .try_into() + .map(Response::new) + .map_err(|_e| Status::unknown("unknown error")) + } } diff --git a/src/http/node.rs b/src/http/node.rs index 918607a..d546012 100644 --- a/src/http/node.rs +++ b/src/http/node.rs @@ -214,6 +214,7 @@ pub fn add_routes(router: Router) -> Router { .route("/v1/node/peers/connect", post(connect_peer)) .route("/v1/node/sign/message", post(sign_message)) .route("/v1/node/verify/message", post(verify_message)) + .route("/v1/node/network-graph/info", get(network_graph_info)) } pub async fn get_unused_address( @@ -573,3 +574,17 @@ pub async fn list_unspent( ) .await } + +pub async fn network_graph_info( + Extension(admin_service): Extension>, + AuthHeader { macaroon, token: _ }: AuthHeader, + cookies: Cookies, +) -> Result, StatusCode> { + handle_authenticated_request( + admin_service, + NodeRequest::NetworkGraphInfo {}, + macaroon, + cookies, + ) + .await +} From eba1b00aaf9915f2e488da25b4d637aaa1679783 Mon Sep 17 00:00:00 2001 From: John Cantrell Date: Wed, 15 Jun 2022 14:39:03 -0400 Subject: [PATCH 6/6] add docker build and push to hub to cd flow --- .github/workflows/cd.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index e678241..1142c43 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -8,6 +8,8 @@ name: CD env: CLI: senseicli DAEMON: senseid + DOCKER_CLI_EXPERIMENTAL: enabled + jobs: # This job downloads and stores `cross` as an artifact, so that it can be @@ -307,3 +309,34 @@ jobs: asset_path: ${{ env.DAEMON }}.tar.gz asset_name: ${{ env.DAEMON }}-${{steps.tag.outputs.tag}}-${{ matrix.target }}.tar.gz asset_content_type: application/gzip + build: + needs: build-web-admin + name: Build Docker image + runs-on: ubuntu-18.04 + steps: + - name: Set VERSION + run: echo "VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Login to Docker Hub + run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin + + - name: Checkout project + uses: actions/checkout@v2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + id: qemu + + - name: Setup Docker buildx action + uses: docker/setup-buildx-action@v1 + id: buildx + + - name: Show available Docker buildx platforms + run: echo ${{ steps.buildx.outputs.platforms }} + + - name: Run Docker buildx + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ${{ secrets.DOCKER_HUB_USER }}/sensei:$VERSION \ + --push .