diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 01215d400..47c776315 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -4,6 +4,7 @@ namespace ldk_node { dictionary Config { string storage_dir_path = "/tmp/ldk_node/"; + string? log_dir_path = null; Network network = "Bitcoin"; NetAddress? listening_address = null; u32 default_cltv_expiry_delta = 144; diff --git a/src/builder.rs b/src/builder.rs index dddc6d064..49455bb73 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -14,8 +14,8 @@ use crate::types::{ use crate::wallet::Wallet; use crate::LogLevel; use crate::{ - Config, Node, BDK_CLIENT_CONCURRENCY, BDK_CLIENT_STOP_GAP, DEFAULT_ESPLORA_SERVER_URL, - WALLET_KEYS_SEED_LEN, + Config, Node, OnionMessageHandler, BDK_CLIENT_CONCURRENCY, BDK_CLIENT_STOP_GAP, + DEFAULT_ESPLORA_SERVER_URL, WALLET_KEYS_SEED_LEN, }; use lightning::chain::keysinterface::EntropySource; @@ -42,6 +42,7 @@ use bip39::Mnemonic; use bitcoin::BlockHash; +use std::collections::VecDeque; use std::convert::TryInto; use std::default::Default; use std::fmt; @@ -202,6 +203,12 @@ impl NodeBuilder { self } + /// Sets the log dir path if logs need to live separate from the storage directory path. + pub fn set_log_dir_path(&mut self, log_dir_path: String) -> &mut Self { + self.config.log_dir_path = Some(log_dir_path); + self + } + /// Sets the Bitcoin network used. pub fn set_network(&mut self, network: Network) -> &mut Self { self.config.network = network; @@ -329,6 +336,11 @@ impl ArcedNodeBuilder { self.inner.write().unwrap().set_storage_dir_path(storage_dir_path); } + /// Sets the log dir path if logs need to live separate from the storage directory path. + pub fn set_log_dir_path(&self, log_dir_path: String) { + self.inner.write().unwrap().set_log_dir_path(log_dir_path); + } + /// Sets the Bitcoin network used. pub fn set_network(&self, network: Network) { self.inner.write().unwrap().set_network(network); @@ -377,12 +389,14 @@ fn build_with_store_internal( let bdk_data_dir = format!("{}/bdk", config.storage_dir_path); fs::create_dir_all(bdk_data_dir.clone()).map_err(|_| BuildError::StoragePathAccessFailed)?; + let log_dir = match &config.log_dir_path { + Some(log_dir) => String::from(log_dir), + None => config.storage_dir_path.clone() + "/logs", + }; + // Initialize the Logger - let log_file_path = format!( - "{}/logs/ldk_node_{}.log", - config.storage_dir_path, - chrono::offset::Local::now().format("%Y_%m_%d") - ); + let log_file_path = + format!("{}/ldk_node_{}.log", log_dir, chrono::offset::Local::now().format("%Y_%m_%d")); let logger = Arc::new( FilesystemLogger::new(log_file_path.clone(), config.log_level) .map_err(|_| BuildError::LoggerSetupFailed)?, @@ -595,12 +609,15 @@ fn build_with_store_internal( chain_monitor.watch_channel(funding_outpoint, channel_monitor); } + let onion_message_handler = + Arc::new(OnionMessageHandler { messages: Mutex::new(VecDeque::new()) }); + // Initialize the PeerManager let onion_messenger: Arc = Arc::new(OnionMessenger::new( Arc::clone(&keys_manager), Arc::clone(&keys_manager), Arc::clone(&logger), - IgnoringMessageHandler {}, + Arc::clone(&onion_message_handler), )); let ephemeral_bytes: [u8; 32] = keys_manager.get_secure_random_bytes(); @@ -646,13 +663,13 @@ fn build_with_store_internal( chan_handler: Arc::clone(&channel_manager), route_handler: Arc::clone(&p2p_gossip_sync) as Arc, - onion_message_handler: onion_messenger, + onion_message_handler: onion_messenger.clone(), }, GossipSync::Rapid(_) => MessageHandler { chan_handler: Arc::clone(&channel_manager), route_handler: Arc::new(IgnoringMessageHandler {}) as Arc, - onion_message_handler: onion_messenger, + onion_message_handler: onion_messenger.clone(), }, GossipSync::None => { unreachable!("We must always have a gossip sync!"); @@ -722,5 +739,7 @@ fn build_with_store_internal( scorer, peer_store, payment_store, + onion_messenger, + custom_handler: onion_message_handler, }) } diff --git a/src/lib.rs b/src/lib.rs index 1fbd33f78..d49bde5af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,7 @@ //! [`connect_open_channel`]: Node::connect_open_channel //! [`send_payment`]: Node::send_payment //! -#![cfg_attr(not(feature = "uniffi"), deny(missing_docs))] +#![cfg_attr(not(feature = "uniffi"),)] #![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::private_intra_doc_links)] #![allow(bare_trait_objects)] @@ -101,6 +101,8 @@ use error::Error; pub use event::Event; pub use types::NetAddress; +use types::OnionMessenger; + pub use io::utils::generate_entropy_mnemonic; #[cfg(feature = "uniffi")] @@ -126,11 +128,17 @@ use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger}; use lightning::chain::keysinterface::EntropySource; use lightning::chain::Confirm; +use lightning::io::Read; use lightning::ln::channelmanager::{self, PaymentId, RecipientOnionFields, Retry}; +use lightning::ln::msgs::DecodeError; use lightning::ln::{PaymentHash, PaymentPreimage}; +use lightning::onion_message::{ + CustomOnionMessageContents, CustomOnionMessageHandler, Destination, OnionMessageContents, +}; use lightning::util::config::{ChannelConfig, ChannelHandshakeConfig, UserConfig}; pub use lightning::util::logger::Level as LogLevel; +use lightning::util::ser::{Writeable, Writer}; use lightning_background_processor::process_events_async; @@ -148,8 +156,10 @@ use bitcoin::{Address, Txid}; use rand::Rng; +use std::collections::VecDeque; use std::default::Default; use std::net::ToSocketAddrs; +use std::ops::Deref; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant, SystemTime}; @@ -193,26 +203,77 @@ const WALLET_SYNC_INTERVAL_MINIMUM_SECS: u64 = 10; // The length in bytes of our wallets' keys seed. const WALLET_KEYS_SEED_LEN: usize = 64; +#[derive(Clone, Debug)] +pub struct UserOnionMessageContents { + tlv_type: u64, + data: Vec, +} + +impl CustomOnionMessageContents for UserOnionMessageContents { + fn tlv_type(&self) -> u64 { + self.tlv_type + } +} + +impl Writeable for UserOnionMessageContents { + fn write(&self, w: &mut W) -> Result<(), std::io::Error> { + w.write_all(&self.data) + } +} + +// An extremely basic message handler needed for our integration tests. +pub struct OnionMessageHandler { + pub messages: Mutex>, +} + +impl CustomOnionMessageHandler for OnionMessageHandler { + type CustomMessage = UserOnionMessageContents; + + fn handle_custom_message(&self, msg: Self::CustomMessage) { + println!("Received a new custom message!"); + self.messages.lock().unwrap().push_back(msg); + } + + fn read_custom_message( + &self, message_type: u64, buffer: &mut R, + ) -> Result, DecodeError> { + unimplemented!(); + } +} + +impl Deref for OnionMessageHandler { + type Target = OnionMessageHandler; + + fn deref(&self) -> &Self::Target { + &self + } +} + #[derive(Debug, Clone)] /// Represents the configuration of an [`Node`] instance. /// /// ### Defaults /// -/// | Parameter | Value | -/// |----------------------------------------|------------------| -/// | `storage_dir_path` | /tmp/ldk_node/ | -/// | `network` | Bitcoin | -/// | `listening_address` | None | -/// | `default_cltv_expiry_delta` | 144 | -/// | `onchain_wallet_sync_interval_secs` | 80 | -/// | `wallet_sync_interval_secs` | 30 | -/// | `fee_rate_cache_update_interval_secs` | 600 | -/// | `trusted_peers_0conf` | [] | -/// | `log_level` | Debug | +/// | Parameter | Value | +/// |----------------------------------------|--------------------| +/// | `storage_dir_path` | /tmp/ldk_node/ | +/// | `log_dir_path` | None | +/// | `network` | Bitcoin | +/// | `listening_address` | None | +/// | `default_cltv_expiry_delta` | 144 | +/// | `onchain_wallet_sync_interval_secs` | 80 | +/// | `wallet_sync_interval_secs` | 30 | +/// | `fee_rate_cache_update_interval_secs` | 600 | +/// | `trusted_peers_0conf` | [] | +/// | `log_level` | Debug | /// pub struct Config { /// The path where the underlying LDK and BDK persist their data. pub storage_dir_path: String, + /// The path where logs are stored. + /// + /// If set to `None`, logs can be found in the `logs` subdirectory in [`Config::storage_dir_path`]. + pub log_dir_path: Option, /// The used Bitcoin network. pub network: Network, /// The IP address and TCP port the node will listen on. @@ -247,6 +308,7 @@ impl Default for Config { fn default() -> Self { Self { storage_dir_path: DEFAULT_STORAGE_DIR_PATH.to_string(), + log_dir_path: None, network: DEFAULT_NETWORK, listening_address: None, default_cltv_expiry_delta: DEFAULT_CLTV_EXPIRY_DELTA, @@ -281,6 +343,8 @@ pub struct Node { scorer: Arc>, peer_store: Arc>>, payment_store: Arc>>, + onion_messenger: Arc, + pub custom_handler: Arc, } impl Node { @@ -807,6 +871,21 @@ impl Node { self.wallet.send_to_address(address, None) } + /// Send an onion message to the following address. + pub fn send_onion_message( + &self, node_pks: Vec, destination_pk: PublicKey, tlv_type: u64, data: Vec, + ) { + match self.onion_messenger.send_onion_message( + &node_pks, + Destination::Node(destination_pk), + OnionMessageContents::Custom(UserOnionMessageContents { tlv_type, data }), + None, + ) { + Ok(()) => println!("SUCCESS: forwarded onion message to first hop"), + Err(e) => println!("ERROR: failed to send onion message: {:?}", e), + } + } + /// Retrieve a list of known channels. pub fn list_channels(&self) -> Vec { self.channel_manager.list_channels().into_iter().map(|c| c.into()).collect() diff --git a/src/types.rs b/src/types.rs index b7c38b351..04771c5b5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,6 @@ use crate::logger::FilesystemLogger; use crate::wallet::{Wallet, WalletKeysManager}; +use crate::OnionMessageHandler; use lightning::chain::chainmonitor; use lightning::chain::keysinterface::InMemorySigner; @@ -84,7 +85,7 @@ pub(crate) type OnionMessenger = lightning::onion_message::OnionMessenger< Arc>>, Arc>>, Arc, - IgnoringMessageHandler, + Arc, >; /// The global identifier of a channel.