32119aef9ea14baa6b68f12b527102f0bb08ea74
[ldk-sample] / src / cli.rs
1 use crate::disk;
2 use crate::hex_utils;
3 use crate::{
4         ChannelManager, FilesystemLogger, HTLCStatus, MillisatAmount, PaymentInfo, PaymentInfoStorage,
5         PeerManager,
6 };
7 use bitcoin::network::constants::Network;
8 use bitcoin::secp256k1::key::PublicKey;
9 use lightning::chain;
10 use lightning::chain::keysinterface::KeysManager;
11 use lightning::ln::features::InvoiceFeatures;
12 use lightning::ln::{PaymentHash, PaymentSecret};
13 use lightning::routing::network_graph::NetGraphMsgHandler;
14 use lightning::routing::router;
15 use lightning::routing::router::RouteHintHop;
16 use lightning::util::config::UserConfig;
17 use lightning_invoice::{utils, Currency, Invoice};
18 use std::env;
19 use std::io;
20 use std::io::{BufRead, Write};
21 use std::net::{SocketAddr, TcpStream};
22 use std::ops::Deref;
23 use std::path::Path;
24 use std::str::FromStr;
25 use std::sync::Arc;
26 use std::time::Duration;
27 use tokio::sync::mpsc;
28
29 pub(crate) struct LdkUserInfo {
30         pub(crate) bitcoind_rpc_username: String,
31         pub(crate) bitcoind_rpc_password: String,
32         pub(crate) bitcoind_rpc_port: u16,
33         pub(crate) bitcoind_rpc_host: String,
34         pub(crate) ldk_storage_dir_path: String,
35         pub(crate) ldk_peer_listening_port: u16,
36         pub(crate) network: Network,
37 }
38
39 pub(crate) fn parse_startup_args() -> Result<LdkUserInfo, ()> {
40         if env::args().len() < 3 {
41                 println!("ldk-tutorial-node requires 3 arguments: `cargo run <bitcoind-rpc-username>:<bitcoind-rpc-password>@<bitcoind-rpc-host>:<bitcoind-rpc-port> ldk_storage_directory_path [<ldk-incoming-peer-listening-port>] [bitcoin-network]`");
42                 return Err(());
43         }
44         let bitcoind_rpc_info = env::args().skip(1).next().unwrap();
45         let bitcoind_rpc_info_parts: Vec<&str> = bitcoind_rpc_info.split("@").collect();
46         if bitcoind_rpc_info_parts.len() != 2 {
47                 println!("ERROR: bad bitcoind RPC URL provided");
48                 return Err(());
49         }
50         let rpc_user_and_password: Vec<&str> = bitcoind_rpc_info_parts[0].split(":").collect();
51         if rpc_user_and_password.len() != 2 {
52                 println!("ERROR: bad bitcoind RPC username/password combo provided");
53                 return Err(());
54         }
55         let bitcoind_rpc_username = rpc_user_and_password[0].to_string();
56         let bitcoind_rpc_password = rpc_user_and_password[1].to_string();
57         let bitcoind_rpc_path: Vec<&str> = bitcoind_rpc_info_parts[1].split(":").collect();
58         if bitcoind_rpc_path.len() != 2 {
59                 println!("ERROR: bad bitcoind RPC path provided");
60                 return Err(());
61         }
62         let bitcoind_rpc_host = bitcoind_rpc_path[0].to_string();
63         let bitcoind_rpc_port = bitcoind_rpc_path[1].parse::<u16>().unwrap();
64
65         let ldk_storage_dir_path = env::args().skip(2).next().unwrap();
66
67         let mut ldk_peer_port_set = true;
68         let ldk_peer_listening_port: u16 = match env::args().skip(3).next().map(|p| p.parse()) {
69                 Some(Ok(p)) => p,
70                 Some(Err(e)) => panic!("{}", e),
71                 None => {
72                         ldk_peer_port_set = false;
73                         9735
74                 }
75         };
76
77         let arg_idx = match ldk_peer_port_set {
78                 true => 4,
79                 false => 3,
80         };
81         let network: Network = match env::args().skip(arg_idx).next().as_ref().map(String::as_str) {
82                 Some("testnet") => Network::Testnet,
83                 Some("regtest") => Network::Regtest,
84                 Some(_) => panic!("Unsupported network provided. Options are: `regtest`, `testnet`"),
85                 None => Network::Testnet,
86         };
87         Ok(LdkUserInfo {
88                 bitcoind_rpc_username,
89                 bitcoind_rpc_password,
90                 bitcoind_rpc_host,
91                 bitcoind_rpc_port,
92                 ldk_storage_dir_path,
93                 ldk_peer_listening_port,
94                 network,
95         })
96 }
97
98 pub(crate) async fn poll_for_user_input(
99         peer_manager: Arc<PeerManager>, channel_manager: Arc<ChannelManager>,
100         keys_manager: Arc<KeysManager>,
101         router: Arc<NetGraphMsgHandler<Arc<dyn chain::Access + Send + Sync>, Arc<FilesystemLogger>>>,
102         inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage,
103         event_notifier: mpsc::Sender<()>, ldk_data_dir: String, logger: Arc<FilesystemLogger>,
104         network: Network,
105 ) {
106         println!("LDK startup successful. To view available commands: \"help\".");
107         println!("LDK logs are available at <your-supplied-ldk-data-dir-path>/.ldk/logs");
108         println!("Local Node ID is {}.", channel_manager.get_our_node_id());
109         let stdin = io::stdin();
110         print!("> ");
111         io::stdout().flush().unwrap(); // Without flushing, the `>` doesn't print
112         for line in stdin.lock().lines() {
113                 let _ = event_notifier.try_send(());
114                 let line = line.unwrap();
115                 let mut words = line.split_whitespace();
116                 if let Some(word) = words.next() {
117                         match word {
118                                 "help" => help(),
119                                 "openchannel" => {
120                                         let peer_pubkey_and_ip_addr = words.next();
121                                         let channel_value_sat = words.next();
122                                         if peer_pubkey_and_ip_addr.is_none() || channel_value_sat.is_none() {
123                                                 println!("ERROR: openchannel has 2 required arguments: `openchannel pubkey@host:port channel_amt_satoshis` [--public]");
124                                                 print!("> ");
125                                                 io::stdout().flush().unwrap();
126                                                 continue;
127                                         }
128                                         let peer_pubkey_and_ip_addr = peer_pubkey_and_ip_addr.unwrap();
129                                         let (pubkey, peer_addr) =
130                                                 match parse_peer_info(peer_pubkey_and_ip_addr.to_string()) {
131                                                         Ok(info) => info,
132                                                         Err(e) => {
133                                                                 println!("{:?}", e.into_inner().unwrap());
134                                                                 print!("> ");
135                                                                 io::stdout().flush().unwrap();
136                                                                 continue;
137                                                         }
138                                                 };
139
140                                         let chan_amt_sat: Result<u64, _> = channel_value_sat.unwrap().parse();
141                                         if chan_amt_sat.is_err() {
142                                                 println!("ERROR: channel amount must be a number");
143                                                 print!("> ");
144                                                 io::stdout().flush().unwrap();
145                                                 continue;
146                                         }
147
148                                         if connect_peer_if_necessary(
149                                                 pubkey,
150                                                 peer_addr,
151                                                 peer_manager.clone(),
152                                                 event_notifier.clone(),
153                                         )
154                                         .is_err()
155                                         {
156                                                 print!("> ");
157                                                 io::stdout().flush().unwrap();
158                                                 continue;
159                                         };
160
161                                         let announce_channel = match words.next() {
162                                                 Some("--public") | Some("--public=true") => true,
163                                                 Some("--public=false") => false,
164                                                 Some(_) => {
165                                                         println!("ERROR: invalid `--public` command format. Valid formats: `--public`, `--public=true` `--public=false`");
166                                                         print!("> ");
167                                                         io::stdout().flush().unwrap();
168                                                         continue;
169                                                 }
170                                                 None => false,
171                                         };
172
173                                         if open_channel(
174                                                 pubkey,
175                                                 chan_amt_sat.unwrap(),
176                                                 announce_channel,
177                                                 channel_manager.clone(),
178                                         )
179                                         .is_ok()
180                                         {
181                                                 let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir.clone());
182                                                 let _ = disk::persist_channel_peer(
183                                                         Path::new(&peer_data_path),
184                                                         peer_pubkey_and_ip_addr,
185                                                 );
186                                         }
187                                 }
188                                 "sendpayment" => {
189                                         let invoice_str = words.next();
190                                         if invoice_str.is_none() {
191                                                 println!("ERROR: sendpayment requires an invoice: `sendpayment <invoice>`");
192                                                 print!("> ");
193                                                 io::stdout().flush().unwrap();
194                                                 continue;
195                                         }
196
197                                         let invoice = match Invoice::from_str(invoice_str.unwrap()) {
198                                                 Ok(inv) => inv,
199                                                 Err(e) => {
200                                                         println!("ERROR: invalid invoice: {:?}", e);
201                                                         print!("> ");
202                                                         io::stdout().flush().unwrap();
203                                                         continue;
204                                                 }
205                                         };
206                                         let mut route_hints = invoice.routes().clone();
207                                         let mut last_hops = Vec::new();
208                                         for hint in route_hints.drain(..) {
209                                                 last_hops.push(hint[hint.len() - 1].clone());
210                                         }
211
212                                         let amt_pico_btc = invoice.amount_pico_btc();
213                                         if amt_pico_btc.is_none() {
214                                                 println!("ERROR: invalid invoice: must contain amount to pay");
215                                                 print!("> ");
216                                                 io::stdout().flush().unwrap();
217                                                 continue;
218                                         }
219                                         let amt_msat = amt_pico_btc.unwrap() / 10;
220
221                                         let payee_pubkey = invoice.recover_payee_pub_key();
222                                         let final_cltv = invoice.min_final_cltv_expiry() as u32;
223
224                                         let mut payment_hash = PaymentHash([0; 32]);
225                                         payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
226
227                                         let payment_secret = match invoice.payment_secret() {
228                                                 Some(secret) => {
229                                                         let mut payment_secret = PaymentSecret([0; 32]);
230                                                         payment_secret.0.copy_from_slice(&secret.0);
231                                                         Some(payment_secret)
232                                                 }
233                                                 None => None,
234                                         };
235
236                                         let invoice_features = match invoice.features() {
237                                                 Some(feat) => Some(feat.clone()),
238                                                 None => None,
239                                         };
240
241                                         send_payment(
242                                                 payee_pubkey,
243                                                 amt_msat,
244                                                 final_cltv,
245                                                 payment_hash,
246                                                 payment_secret,
247                                                 invoice_features,
248                                                 last_hops,
249                                                 router.clone(),
250                                                 channel_manager.clone(),
251                                                 outbound_payments.clone(),
252                                                 logger.clone(),
253                                         );
254                                 }
255                                 "getinvoice" => {
256                                         let amt_str = words.next();
257                                         if amt_str.is_none() {
258                                                 println!("ERROR: getinvoice requires an amount in millisatoshis");
259                                                 print!("> ");
260                                                 io::stdout().flush().unwrap();
261                                                 continue;
262                                         }
263
264                                         let amt_msat: Result<u64, _> = amt_str.unwrap().parse();
265                                         if amt_msat.is_err() {
266                                                 println!("ERROR: getinvoice provided payment amount was not a number");
267                                                 print!("> ");
268                                                 io::stdout().flush().unwrap();
269                                                 continue;
270                                         }
271                                         get_invoice(
272                                                 amt_msat.unwrap(),
273                                                 inbound_payments.clone(),
274                                                 channel_manager.clone(),
275                                                 keys_manager.clone(),
276                                                 network,
277                                         );
278                                 }
279                                 "connectpeer" => {
280                                         let peer_pubkey_and_ip_addr = words.next();
281                                         if peer_pubkey_and_ip_addr.is_none() {
282                                                 println!("ERROR: connectpeer requires peer connection info: `connectpeer pubkey@host:port`");
283                                                 print!("> ");
284                                                 io::stdout().flush().unwrap();
285                                                 continue;
286                                         }
287                                         let (pubkey, peer_addr) =
288                                                 match parse_peer_info(peer_pubkey_and_ip_addr.unwrap().to_string()) {
289                                                         Ok(info) => info,
290                                                         Err(e) => {
291                                                                 println!("{:?}", e.into_inner().unwrap());
292                                                                 print!("> ");
293                                                                 io::stdout().flush().unwrap();
294                                                                 continue;
295                                                         }
296                                                 };
297                                         if connect_peer_if_necessary(
298                                                 pubkey,
299                                                 peer_addr,
300                                                 peer_manager.clone(),
301                                                 event_notifier.clone(),
302                                         )
303                                         .is_ok()
304                                         {
305                                                 println!("SUCCESS: connected to peer {}", pubkey);
306                                         }
307                                 }
308                                 "listchannels" => list_channels(channel_manager.clone()),
309                                 "listpayments" => {
310                                         list_payments(inbound_payments.clone(), outbound_payments.clone())
311                                 }
312                                 "closechannel" => {
313                                         let channel_id_str = words.next();
314                                         if channel_id_str.is_none() {
315                                                 println!("ERROR: closechannel requires a channel ID: `closechannel <channel_id>`");
316                                                 print!("> ");
317                                                 io::stdout().flush().unwrap();
318                                                 continue;
319                                         }
320                                         let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap());
321                                         if channel_id_vec.is_none() {
322                                                 println!("ERROR: couldn't parse channel_id as hex");
323                                                 print!("> ");
324                                                 io::stdout().flush().unwrap();
325                                                 continue;
326                                         }
327                                         let mut channel_id = [0; 32];
328                                         channel_id.copy_from_slice(&channel_id_vec.unwrap());
329                                         close_channel(channel_id, channel_manager.clone());
330                                 }
331                                 "forceclosechannel" => {
332                                         let channel_id_str = words.next();
333                                         if channel_id_str.is_none() {
334                                                 println!("ERROR: forceclosechannel requires a channel ID: `forceclosechannel <channel_id>`");
335                                                 print!("> ");
336                                                 io::stdout().flush().unwrap();
337                                                 continue;
338                                         }
339                                         let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap());
340                                         if channel_id_vec.is_none() {
341                                                 println!("ERROR: couldn't parse channel_id as hex");
342                                                 print!("> ");
343                                                 io::stdout().flush().unwrap();
344                                                 continue;
345                                         }
346                                         let mut channel_id = [0; 32];
347                                         channel_id.copy_from_slice(&channel_id_vec.unwrap());
348                                         force_close_channel(channel_id, channel_manager.clone());
349                                 }
350                                 "nodeinfo" => node_info(channel_manager.clone(), peer_manager.clone()),
351                                 "listpeers" => list_peers(peer_manager.clone()),
352                                 _ => println!("Unknown command. See `\"help\" for available commands."),
353                         }
354                 }
355                 print!("> ");
356                 io::stdout().flush().unwrap();
357         }
358 }
359
360 fn help() {
361         println!("openchannel pubkey@host:port <channel_amt_satoshis>");
362         println!("sendpayment <invoice>");
363         println!("getinvoice <amt_in_millisatoshis>");
364         println!("connectpeer pubkey@host:port");
365         println!("listchannels");
366         println!("listpayments");
367         println!("closechannel <channel_id>");
368         println!("forceclosechannel <channel_id>");
369 }
370
371 fn node_info(channel_manager: Arc<ChannelManager>, peer_manager: Arc<PeerManager>) {
372         println!("\t{{");
373         println!("\t\t node_pubkey: {}", channel_manager.get_our_node_id());
374         println!("\t\t num_channels: {}", channel_manager.list_channels().len());
375         println!("\t\t num_usable_channels: {}", channel_manager.list_usable_channels().len());
376         println!("\t\t num_peers: {}", peer_manager.get_peer_node_ids().len());
377         println!("\t}},");
378 }
379
380 fn list_peers(peer_manager: Arc<PeerManager>) {
381         println!("\t{{");
382         for pubkey in peer_manager.get_peer_node_ids() {
383                 println!("\t\t pubkey: {}", pubkey);
384         }
385         println!("\t}},");
386 }
387
388 fn list_channels(channel_manager: Arc<ChannelManager>) {
389         print!("[");
390         for chan_info in channel_manager.list_channels() {
391                 println!("");
392                 println!("\t{{");
393                 println!("\t\tchannel_id: {},", hex_utils::hex_str(&chan_info.channel_id[..]));
394                 println!(
395                         "\t\tpeer_pubkey: {},",
396                         hex_utils::hex_str(&chan_info.remote_network_id.serialize())
397                 );
398                 let mut pending_channel = false;
399                 match chan_info.short_channel_id {
400                         Some(id) => println!("\t\tshort_channel_id: {},", id),
401                         None => {
402                                 pending_channel = true;
403                         }
404                 }
405                 println!("\t\tpending_open: {},", pending_channel);
406                 println!("\t\tchannel_value_satoshis: {},", chan_info.channel_value_satoshis);
407                 println!("\t\tchannel_can_send_payments: {},", chan_info.is_live);
408                 println!("\t}},");
409         }
410         println!("]");
411 }
412
413 fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage) {
414         let inbound = inbound_payments.lock().unwrap();
415         let outbound = outbound_payments.lock().unwrap();
416         print!("[");
417         for (payment_hash, payment_info) in inbound.deref() {
418                 println!("");
419                 println!("\t{{");
420                 println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
421                 println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0));
422                 println!("\t\thtlc_direction: inbound,");
423                 println!(
424                         "\t\thtlc_status: {},",
425                         match payment_info.status {
426                                 HTLCStatus::Pending => "pending",
427                                 HTLCStatus::Succeeded => "succeeded",
428                                 HTLCStatus::Failed => "failed",
429                         }
430                 );
431
432                 println!("\t}},");
433         }
434
435         for (payment_hash, payment_info) in outbound.deref() {
436                 println!("");
437                 println!("\t{{");
438                 println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
439                 println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0));
440                 println!("\t\thtlc_direction: outbound,");
441                 println!(
442                         "\t\thtlc_status: {},",
443                         match payment_info.status {
444                                 HTLCStatus::Pending => "pending",
445                                 HTLCStatus::Succeeded => "succeeded",
446                                 HTLCStatus::Failed => "failed",
447                         }
448                 );
449
450                 println!("\t}},");
451         }
452         println!("]");
453 }
454
455 pub(crate) fn connect_peer_if_necessary(
456         pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
457         event_notifier: mpsc::Sender<()>,
458 ) -> Result<(), ()> {
459         for node_pubkey in peer_manager.get_peer_node_ids() {
460                 if node_pubkey == pubkey {
461                         return Ok(());
462                 }
463         }
464         match TcpStream::connect_timeout(&peer_addr, Duration::from_secs(10)) {
465                 Ok(stream) => {
466                         let peer_mgr = peer_manager.clone();
467                         let event_ntfns = event_notifier.clone();
468                         tokio::spawn(async move {
469                                 lightning_net_tokio::setup_outbound(peer_mgr, event_ntfns, pubkey, stream).await;
470                         });
471                         let mut peer_connected = false;
472                         while !peer_connected {
473                                 for node_pubkey in peer_manager.get_peer_node_ids() {
474                                         if node_pubkey == pubkey {
475                                                 peer_connected = true;
476                                         }
477                                 }
478                         }
479                 }
480                 Err(e) => {
481                         println!("ERROR: failed to connect to peer: {:?}", e);
482                         return Err(());
483                 }
484         }
485         Ok(())
486 }
487
488 fn open_channel(
489         peer_pubkey: PublicKey, channel_amt_sat: u64, announce_channel: bool,
490         channel_manager: Arc<ChannelManager>,
491 ) -> Result<(), ()> {
492         let mut config = UserConfig::default();
493         if announce_channel {
494                 config.channel_options.announced_channel = true;
495         }
496         // lnd's max to_self_delay is 2016, so we want to be compatible.
497         config.peer_channel_config_limits.their_to_self_delay = 2016;
498         match channel_manager.create_channel(peer_pubkey, channel_amt_sat, 0, 0, None) {
499                 Ok(_) => {
500                         println!("EVENT: initiated channel with peer {}. ", peer_pubkey);
501                         return Ok(());
502                 }
503                 Err(e) => {
504                         println!("ERROR: failed to open channel: {:?}", e);
505                         return Err(());
506                 }
507         }
508 }
509
510 fn send_payment(
511         payee: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash,
512         payment_secret: Option<PaymentSecret>, payee_features: Option<InvoiceFeatures>,
513         route_hints: Vec<RouteHintHop>,
514         router: Arc<NetGraphMsgHandler<Arc<dyn chain::Access + Send + Sync>, Arc<FilesystemLogger>>>,
515         channel_manager: Arc<ChannelManager>, payment_storage: PaymentInfoStorage,
516         logger: Arc<FilesystemLogger>,
517 ) {
518         let network_graph = router.network_graph.read().unwrap();
519         let first_hops = channel_manager.list_usable_channels();
520         let payer_pubkey = channel_manager.get_our_node_id();
521
522         let route = router::get_route(
523                 &payer_pubkey,
524                 &network_graph,
525                 &payee,
526                 payee_features,
527                 Some(&first_hops.iter().collect::<Vec<_>>()),
528                 &route_hints.iter().collect::<Vec<_>>(),
529                 amt_msat,
530                 final_cltv,
531                 logger,
532         );
533         if let Err(e) = route {
534                 println!("ERROR: failed to find route: {}", e.err);
535                 return;
536         }
537         let status = match channel_manager.send_payment(&route.unwrap(), payment_hash, &payment_secret)
538         {
539                 Ok(()) => {
540                         println!("EVENT: initiated sending {} msats to {}", amt_msat, payee);
541                         HTLCStatus::Pending
542                 }
543                 Err(e) => {
544                         println!("ERROR: failed to send payment: {:?}", e);
545                         HTLCStatus::Failed
546                 }
547         };
548         let mut payments = payment_storage.lock().unwrap();
549         payments.insert(
550                 payment_hash,
551                 PaymentInfo {
552                         preimage: None,
553                         secret: payment_secret,
554                         status,
555                         amt_msat: MillisatAmount(Some(amt_msat)),
556                 },
557         );
558 }
559
560 fn get_invoice(
561         amt_msat: u64, payment_storage: PaymentInfoStorage, channel_manager: Arc<ChannelManager>,
562         keys_manager: Arc<KeysManager>, network: Network,
563 ) {
564         let mut payments = payment_storage.lock().unwrap();
565         let currency = match network {
566                 Network::Bitcoin => Currency::Bitcoin,
567                 Network::Testnet => Currency::BitcoinTestnet,
568                 Network::Regtest => Currency::Regtest,
569                 Network::Signet => panic!("Signet unsupported"),
570         };
571         let invoice = match utils::create_invoice_from_channelmanager(
572                 &channel_manager,
573                 keys_manager,
574                 currency,
575                 Some(amt_msat),
576                 "ldk-tutorial-node".to_string(),
577         ) {
578                 Ok(inv) => {
579                         println!("SUCCESS: generated invoice: {}", inv);
580                         inv
581                 }
582                 Err(e) => {
583                         println!("ERROR: failed to create invoice: {:?}", e);
584                         return;
585                 }
586         };
587
588         let mut payment_hash = PaymentHash([0; 32]);
589         payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
590         payments.insert(
591                 payment_hash,
592                 PaymentInfo {
593                         preimage: None,
594                         // We can't add payment secrets to invoices until we support features in invoices.
595                         // Otherwise lnd errors with "destination hop doesn't understand payment addresses"
596                         // (for context, lnd calls payment secrets "payment addresses").
597                         secret: Some(invoice.payment_secret().unwrap().clone()),
598                         status: HTLCStatus::Pending,
599                         amt_msat: MillisatAmount(Some(amt_msat)),
600                 },
601         );
602 }
603
604 fn close_channel(channel_id: [u8; 32], channel_manager: Arc<ChannelManager>) {
605         match channel_manager.close_channel(&channel_id) {
606                 Ok(()) => println!("EVENT: initiating channel close"),
607                 Err(e) => println!("ERROR: failed to close channel: {:?}", e),
608         }
609 }
610
611 fn force_close_channel(channel_id: [u8; 32], channel_manager: Arc<ChannelManager>) {
612         match channel_manager.force_close_channel(&channel_id) {
613                 Ok(()) => println!("EVENT: initiating channel force-close"),
614                 Err(e) => println!("ERROR: failed to force-close channel: {:?}", e),
615         }
616 }
617
618 pub(crate) fn parse_peer_info(
619         peer_pubkey_and_ip_addr: String,
620 ) -> Result<(PublicKey, SocketAddr), std::io::Error> {
621         let mut pubkey_and_addr = peer_pubkey_and_ip_addr.split("@");
622         let pubkey = pubkey_and_addr.next();
623         let peer_addr_str = pubkey_and_addr.next();
624         if peer_addr_str.is_none() || peer_addr_str.is_none() {
625                 return Err(std::io::Error::new(
626                         std::io::ErrorKind::Other,
627                         "ERROR: incorrectly formatted peer
628         info. Should be formatted as: `pubkey@host:port`",
629                 ));
630         }
631
632         let peer_addr: Result<SocketAddr, _> = peer_addr_str.unwrap().parse();
633         if peer_addr.is_err() {
634                 return Err(std::io::Error::new(
635                         std::io::ErrorKind::Other,
636                         "ERROR: couldn't parse pubkey@host:port into a socket address",
637                 ));
638         }
639
640         let pubkey = hex_utils::to_compressed_pubkey(pubkey.unwrap());
641         if pubkey.is_none() {
642                 return Err(std::io::Error::new(
643                         std::io::ErrorKind::Other,
644                         "ERROR: unable to parse given pubkey for node",
645                 ));
646         }
647
648         Ok((pubkey.unwrap(), peer_addr.unwrap()))
649 }