diff --git a/tests/common/ldk_node/mod.rs b/tests/common/ldk_node/mod.rs index 68be0e16..20938432 100644 --- a/tests/common/ldk_node/mod.rs +++ b/tests/common/ldk_node/mod.rs @@ -3,6 +3,7 @@ pub mod config; mod convert; mod disk; mod hex_utils; +pub mod node_api; mod peer_utils; mod sweep; @@ -521,7 +522,7 @@ async fn handle_ldk_events( } } -pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { +pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) -> node_api::Node { let (ldk_data_dir, _ldk_dir_binding, ldk_log_dir) = config::setup_data_and_log_dirs(test_name); // ## Setup @@ -541,8 +542,7 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { { Ok(client) => Arc::new(client), Err(e) => { - println!("Failed to connect to bitcoind client: {}", e); - return; + panic!("Failed to connect to bitcoind client: {}", e); } }; @@ -556,11 +556,10 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { bitcoin::Network::Signet => "signet", } { - println!( + panic!( "Chain argument ({}) didn't match bitcoind chain ({})", args.network, bitcoind_chain ); - return; } // Step 2: Initialize the FeeEstimator @@ -605,11 +604,10 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { f.sync_all().expect("Failed to sync node keys seed to disk"); } Err(e) => { - println!( + panic!( "ERROR: Unable to create keys seed file {}: {}", keys_seed_path, e ); - return; } } key @@ -691,7 +689,7 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { fee_estimator.clone(), chain_monitor.clone(), broadcaster.clone(), - router, + router.clone(), logger.clone(), user_config, channel_monitor_mut_references, @@ -711,7 +709,7 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { fee_estimator.clone(), chain_monitor.clone(), broadcaster.clone(), - router, + router.clone(), logger.clone(), keys_manager.clone(), keys_manager.clone(), @@ -1023,4 +1021,20 @@ pub(crate) async fn start_ldk(args: config::LdkUserInfo, test_name: &str) { Arc::clone(&bitcoind_client), Arc::clone(&channel_manager), )); + + return node_api::Node { + logger, + bitcoind_client, + persister, + chain_monitor, + keys_manager, + network_graph, + router, + scorer, + channel_manager, + gossip_sync, + onion_messenger, + peer_manager, + listening_port: args.ldk_peer_listening_port, + }; } diff --git a/tests/common/ldk_node/node_api.rs b/tests/common/ldk_node/node_api.rs new file mode 100644 index 00000000..999d4ded --- /dev/null +++ b/tests/common/ldk_node/node_api.rs @@ -0,0 +1,168 @@ +use crate::common::ldk_node::disk::FilesystemLogger; +use crate::common::ldk_node::{ + BitcoindClient, ChainMonitor, ChannelManager, NetworkGraph, OnionMessenger, PeerManager, +}; + +use bitcoin::secp256k1::PublicKey; +use lightning::onion_message::{ + CustomOnionMessageContents, Destination, OnionMessageContents, OnionMessagePath, +}; +use lightning::routing::router::DefaultRouter; +use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters}; +use lightning::sign::KeysManager; +use lightning::util::ser::{Writeable, Writer}; +use lightning_persister::FilesystemPersister; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +pub(crate) type Router = DefaultRouter< + Arc, + Arc, + Arc>, + ProbabilisticScoringFeeParameters, + Scorer, +>; +pub(crate) type Scorer = ProbabilisticScorer, Arc>; + +pub(crate) type P2PGossipSyncType = lightning::routing::gossip::P2PGossipSync< + Arc, + Arc, + Arc, +>; + +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) + } +} + +pub struct Node { + pub(crate) logger: Arc, + pub(crate) bitcoind_client: Arc, + pub(crate) persister: Arc, + pub(crate) chain_monitor: Arc, + pub(crate) keys_manager: Arc, + pub(crate) network_graph: Arc, + pub(crate) router: Arc, + pub(crate) scorer: Arc>, + pub(crate) channel_manager: Arc, + pub(crate) gossip_sync: Arc, + pub(crate) onion_messenger: Arc, + pub(crate) peer_manager: Arc, + + // Config values + pub(crate) listening_port: u16, +} + +impl Node { + // get_node_info retrieves node_id and listening address. + pub fn get_node_info(&self) -> (PublicKey, SocketAddr) { + let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), self.listening_port); + (self.channel_manager.get_our_node_id(), socket) + } + + pub async fn connect_to_peer( + &self, + pubkey: PublicKey, + peer_addr: SocketAddr, + ) -> Result<(), ()> { + // If we're already connected to peer, then we're good to go. + for (node_pubkey, _) in self.peer_manager.get_peer_node_ids() { + if node_pubkey == pubkey { + return Ok(()); + } + } + let res = match self.do_connect_peer(pubkey, peer_addr).await { + Ok(_) => { + println!("SUCCESS: connected to peer {}", pubkey); + Ok(()) + } + Err(e) => { + println!("ERROR: failed to connect to peer: {e:?}"); + Err(()) + } + }; + res + } + + pub async fn do_connect_peer( + &self, + pubkey: PublicKey, + peer_addr: SocketAddr, + ) -> Result<(), ()> { + match lightning_net_tokio::connect_outbound( + Arc::clone(&self.peer_manager), + pubkey, + peer_addr, + ) + .await + { + Some(connection_closed_future) => { + let mut connection_closed_future = Box::pin(connection_closed_future); + loop { + tokio::select! { + _ = &mut connection_closed_future => return Err(()), + _ = tokio::time::sleep(Duration::from_millis(10)) => {}, + }; + if self + .peer_manager + .get_peer_node_ids() + .iter() + .find(|(id, _)| *id == pubkey) + .is_some() + { + return Ok(()); + } + } + } + None => Err(()), + } + } + + pub async fn send_onion_message( + &self, + mut intermediate_nodes: Vec, + tlv_type: u64, + data: Vec, + ) -> Result<(), ()> { + if intermediate_nodes.len() == 0 { + println!("Need to provide pubkey to send onion message"); + return Err(()); + } + if tlv_type <= 64 { + println!("Need an integral message type above 64"); + return Err(()); + } + let destination = Destination::Node(intermediate_nodes.pop().unwrap()); + let message_path = OnionMessagePath { + intermediate_nodes, + destination, + }; + match self.onion_messenger.send_onion_message( + message_path, + OnionMessageContents::Custom(UserOnionMessageContents { tlv_type, data }), + None, + ) { + Ok(()) => { + println!("SUCCESS: forwarded onion message to first hop"); + Ok(()) + } + Err(e) => { + println!("ERROR: failed to send onion message: {:?}", e); + Ok(()) + } + } + } +}