Fix badly formatted log
[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, ToSocketAddrs};
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                                         .await
155                                         .is_err()
156                                         {
157                                                 print!("> ");
158                                                 io::stdout().flush().unwrap();
159                                                 continue;
160                                         };
161
162                                         let announce_channel = match words.next() {
163                                                 Some("--public") | Some("--public=true") => true,
164                                                 Some("--public=false") => false,
165                                                 Some(_) => {
166                                                         println!("ERROR: invalid `--public` command format. Valid formats: `--public`, `--public=true` `--public=false`");
167                                                         print!("> ");
168                                                         io::stdout().flush().unwrap();
169                                                         continue;
170                                                 }
171                                                 None => false,
172                                         };
173
174                                         if open_channel(
175                                                 pubkey,
176                                                 chan_amt_sat.unwrap(),
177                                                 announce_channel,
178                                                 channel_manager.clone(),
179                                         )
180                                         .is_ok()
181                                         {
182                                                 let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir.clone());
183                                                 let _ = disk::persist_channel_peer(
184                                                         Path::new(&peer_data_path),
185                                                         peer_pubkey_and_ip_addr,
186                                                 );
187                                         }
188                                 }
189                                 "sendpayment" => {
190                                         let invoice_str = words.next();
191                                         if invoice_str.is_none() {
192                                                 println!("ERROR: sendpayment requires an invoice: `sendpayment <invoice>`");
193                                                 print!("> ");
194                                                 io::stdout().flush().unwrap();
195                                                 continue;
196                                         }
197
198                                         let invoice = match Invoice::from_str(invoice_str.unwrap()) {
199                                                 Ok(inv) => inv,
200                                                 Err(e) => {
201                                                         println!("ERROR: invalid invoice: {:?}", e);
202                                                         print!("> ");
203                                                         io::stdout().flush().unwrap();
204                                                         continue;
205                                                 }
206                                         };
207                                         let mut route_hints = invoice.routes().clone();
208                                         let mut last_hops = Vec::new();
209                                         for hint in route_hints.drain(..) {
210                                                 last_hops.push(hint[hint.len() - 1].clone());
211                                         }
212
213                                         let amt_pico_btc = invoice.amount_pico_btc();
214                                         if amt_pico_btc.is_none() {
215                                                 println!("ERROR: invalid invoice: must contain amount to pay");
216                                                 print!("> ");
217                                                 io::stdout().flush().unwrap();
218                                                 continue;
219                                         }
220                                         let amt_msat = amt_pico_btc.unwrap() / 10;
221
222                                         let payee_pubkey = invoice.recover_payee_pub_key();
223                                         let final_cltv = invoice.min_final_cltv_expiry() as u32;
224
225                                         let mut payment_hash = PaymentHash([0; 32]);
226                                         payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
227
228                                         let payment_secret = match invoice.payment_secret() {
229                                                 Some(secret) => {
230                                                         let mut payment_secret = PaymentSecret([0; 32]);
231                                                         payment_secret.0.copy_from_slice(&secret.0);
232                                                         Some(payment_secret)
233                                                 }
234                                                 None => None,
235                                         };
236
237                                         let invoice_features = match invoice.features() {
238                                                 Some(feat) => Some(feat.clone()),
239                                                 None => None,
240                                         };
241
242                                         send_payment(
243                                                 payee_pubkey,
244                                                 amt_msat,
245                                                 final_cltv,
246                                                 payment_hash,
247                                                 payment_secret,
248                                                 invoice_features,
249                                                 last_hops,
250                                                 router.clone(),
251                                                 channel_manager.clone(),
252                                                 outbound_payments.clone(),
253                                                 logger.clone(),
254                                         );
255                                 }
256                                 "getinvoice" => {
257                                         let amt_str = words.next();
258                                         if amt_str.is_none() {
259                                                 println!("ERROR: getinvoice requires an amount in millisatoshis");
260                                                 print!("> ");
261                                                 io::stdout().flush().unwrap();
262                                                 continue;
263                                         }
264
265                                         let amt_msat: Result<u64, _> = amt_str.unwrap().parse();
266                                         if amt_msat.is_err() {
267                                                 println!("ERROR: getinvoice provided payment amount was not a number");
268                                                 print!("> ");
269                                                 io::stdout().flush().unwrap();
270                                                 continue;
271                                         }
272                                         get_invoice(
273                                                 amt_msat.unwrap(),
274                                                 inbound_payments.clone(),
275                                                 channel_manager.clone(),
276                                                 keys_manager.clone(),
277                                                 network,
278                                         );
279                                 }
280                                 "connectpeer" => {
281                                         let peer_pubkey_and_ip_addr = words.next();
282                                         if peer_pubkey_and_ip_addr.is_none() {
283                                                 println!("ERROR: connectpeer requires peer connection info: `connectpeer pubkey@host:port`");
284                                                 print!("> ");
285                                                 io::stdout().flush().unwrap();
286                                                 continue;
287                                         }
288                                         let (pubkey, peer_addr) =
289                                                 match parse_peer_info(peer_pubkey_and_ip_addr.unwrap().to_string()) {
290                                                         Ok(info) => info,
291                                                         Err(e) => {
292                                                                 println!("{:?}", e.into_inner().unwrap());
293                                                                 print!("> ");
294                                                                 io::stdout().flush().unwrap();
295                                                                 continue;
296                                                         }
297                                                 };
298                                         if connect_peer_if_necessary(
299                                                 pubkey,
300                                                 peer_addr,
301                                                 peer_manager.clone(),
302                                                 event_notifier.clone(),
303                                         )
304                                         .await
305                                         .is_ok()
306                                         {
307                                                 println!("SUCCESS: connected to peer {}", pubkey);
308                                         }
309                                 }
310                                 "listchannels" => list_channels(channel_manager.clone()),
311                                 "listpayments" => {
312                                         list_payments(inbound_payments.clone(), outbound_payments.clone())
313                                 }
314                                 "closechannel" => {
315                                         let channel_id_str = words.next();
316                                         if channel_id_str.is_none() {
317                                                 println!("ERROR: closechannel requires a channel ID: `closechannel <channel_id>`");
318                                                 print!("> ");
319                                                 io::stdout().flush().unwrap();
320                                                 continue;
321                                         }
322                                         let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap());
323                                         if channel_id_vec.is_none() {
324                                                 println!("ERROR: couldn't parse channel_id as hex");
325                                                 print!("> ");
326                                                 io::stdout().flush().unwrap();
327                                                 continue;
328                                         }
329                                         let mut channel_id = [0; 32];
330                                         channel_id.copy_from_slice(&channel_id_vec.unwrap());
331                                         close_channel(channel_id, channel_manager.clone());
332                                 }
333                                 "forceclosechannel" => {
334                                         let channel_id_str = words.next();
335                                         if channel_id_str.is_none() {
336                                                 println!("ERROR: forceclosechannel requires a channel ID: `forceclosechannel <channel_id>`");
337                                                 print!("> ");
338                                                 io::stdout().flush().unwrap();
339                                                 continue;
340                                         }
341                                         let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap());
342                                         if channel_id_vec.is_none() {
343                                                 println!("ERROR: couldn't parse channel_id as hex");
344                                                 print!("> ");
345                                                 io::stdout().flush().unwrap();
346                                                 continue;
347                                         }
348                                         let mut channel_id = [0; 32];
349                                         channel_id.copy_from_slice(&channel_id_vec.unwrap());
350                                         force_close_channel(channel_id, channel_manager.clone());
351                                 }
352                                 "nodeinfo" => node_info(channel_manager.clone(), peer_manager.clone()),
353                                 "listpeers" => list_peers(peer_manager.clone()),
354                                 _ => println!("Unknown command. See `\"help\" for available commands."),
355                         }
356                 }
357                 print!("> ");
358                 io::stdout().flush().unwrap();
359         }
360 }
361
362 fn help() {
363         println!("openchannel pubkey@host:port <channel_amt_satoshis>");
364         println!("sendpayment <invoice>");
365         println!("getinvoice <amt_in_millisatoshis>");
366         println!("connectpeer pubkey@host:port");
367         println!("listchannels");
368         println!("listpayments");
369         println!("closechannel <channel_id>");
370         println!("forceclosechannel <channel_id>");
371 }
372
373 fn node_info(channel_manager: Arc<ChannelManager>, peer_manager: Arc<PeerManager>) {
374         println!("\t{{");
375         println!("\t\t node_pubkey: {}", channel_manager.get_our_node_id());
376         println!("\t\t num_channels: {}", channel_manager.list_channels().len());
377         println!("\t\t num_usable_channels: {}", channel_manager.list_usable_channels().len());
378         println!("\t\t num_peers: {}", peer_manager.get_peer_node_ids().len());
379         println!("\t}},");
380 }
381
382 fn list_peers(peer_manager: Arc<PeerManager>) {
383         println!("\t{{");
384         for pubkey in peer_manager.get_peer_node_ids() {
385                 println!("\t\t pubkey: {}", pubkey);
386         }
387         println!("\t}},");
388 }
389
390 fn list_channels(channel_manager: Arc<ChannelManager>) {
391         print!("[");
392         for chan_info in channel_manager.list_channels() {
393                 println!("");
394                 println!("\t{{");
395                 println!("\t\tchannel_id: {},", hex_utils::hex_str(&chan_info.channel_id[..]));
396                 println!(
397                         "\t\tpeer_pubkey: {},",
398                         hex_utils::hex_str(&chan_info.remote_network_id.serialize())
399                 );
400                 let mut pending_channel = false;
401                 match chan_info.short_channel_id {
402                         Some(id) => println!("\t\tshort_channel_id: {},", id),
403                         None => {
404                                 pending_channel = true;
405                         }
406                 }
407                 println!("\t\tpending_open: {},", pending_channel);
408                 println!("\t\tchannel_value_satoshis: {},", chan_info.channel_value_satoshis);
409                 println!("\t\tchannel_can_send_payments: {},", chan_info.is_usable);
410                 println!("\t}},");
411         }
412         println!("]");
413 }
414
415 fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage) {
416         let inbound = inbound_payments.lock().unwrap();
417         let outbound = outbound_payments.lock().unwrap();
418         print!("[");
419         for (payment_hash, payment_info) in inbound.deref() {
420                 println!("");
421                 println!("\t{{");
422                 println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
423                 println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0));
424                 println!("\t\thtlc_direction: inbound,");
425                 println!(
426                         "\t\thtlc_status: {},",
427                         match payment_info.status {
428                                 HTLCStatus::Pending => "pending",
429                                 HTLCStatus::Succeeded => "succeeded",
430                                 HTLCStatus::Failed => "failed",
431                         }
432                 );
433
434                 println!("\t}},");
435         }
436
437         for (payment_hash, payment_info) in outbound.deref() {
438                 println!("");
439                 println!("\t{{");
440                 println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
441                 println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0));
442                 println!("\t\thtlc_direction: outbound,");
443                 println!(
444                         "\t\thtlc_status: {},",
445                         match payment_info.status {
446                                 HTLCStatus::Pending => "pending",
447                                 HTLCStatus::Succeeded => "succeeded",
448                                 HTLCStatus::Failed => "failed",
449                         }
450                 );
451
452                 println!("\t}},");
453         }
454         println!("]");
455 }
456
457 pub(crate) async fn connect_peer_if_necessary(
458         pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
459         event_notifier: mpsc::Sender<()>,
460 ) -> Result<(), ()> {
461         for node_pubkey in peer_manager.get_peer_node_ids() {
462                 if node_pubkey == pubkey {
463                         return Ok(());
464                 }
465         }
466         match lightning_net_tokio::connect_outbound(
467                 Arc::clone(&peer_manager),
468                 event_notifier,
469                 pubkey,
470                 peer_addr,
471         )
472         .await
473         {
474                 Some(conn_closed_fut) => {
475                         let mut closed_fut_box = Box::pin(conn_closed_fut);
476                         let mut peer_connected = false;
477                         while !peer_connected {
478                                 match futures::poll!(&mut closed_fut_box) {
479                                         std::task::Poll::Ready(_) => {
480                                                 println!("ERROR: Peer disconnected before we finished the handshake");
481                                                 return Err(());
482                                         }
483                                         std::task::Poll::Pending => {}
484                                 }
485                                 for node_pubkey in peer_manager.get_peer_node_ids() {
486                                         if node_pubkey == pubkey {
487                                                 peer_connected = true;
488                                         }
489                                 }
490                                 // Avoid blocking the tokio context by sleeping a bit
491                                 tokio::time::sleep(Duration::from_millis(10)).await;
492                         }
493                 }
494                 None => {
495                         println!("ERROR: failed to connect to peer");
496                         return Err(());
497                 }
498         }
499         Ok(())
500 }
501
502 fn open_channel(
503         peer_pubkey: PublicKey, channel_amt_sat: u64, announce_channel: bool,
504         channel_manager: Arc<ChannelManager>,
505 ) -> Result<(), ()> {
506         let mut config = UserConfig::default();
507         if announce_channel {
508                 config.channel_options.announced_channel = true;
509         }
510         // lnd's max to_self_delay is 2016, so we want to be compatible.
511         config.peer_channel_config_limits.their_to_self_delay = 2016;
512         match channel_manager.create_channel(peer_pubkey, channel_amt_sat, 0, 0, None) {
513                 Ok(_) => {
514                         println!("EVENT: initiated channel with peer {}. ", peer_pubkey);
515                         return Ok(());
516                 }
517                 Err(e) => {
518                         println!("ERROR: failed to open channel: {:?}", e);
519                         return Err(());
520                 }
521         }
522 }
523
524 fn send_payment(
525         payee: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash,
526         payment_secret: Option<PaymentSecret>, payee_features: Option<InvoiceFeatures>,
527         route_hints: Vec<RouteHintHop>,
528         router: Arc<NetGraphMsgHandler<Arc<dyn chain::Access + Send + Sync>, Arc<FilesystemLogger>>>,
529         channel_manager: Arc<ChannelManager>, payment_storage: PaymentInfoStorage,
530         logger: Arc<FilesystemLogger>,
531 ) {
532         let network_graph = router.network_graph.read().unwrap();
533         let first_hops = channel_manager.list_usable_channels();
534         let payer_pubkey = channel_manager.get_our_node_id();
535
536         let route = router::get_route(
537                 &payer_pubkey,
538                 &network_graph,
539                 &payee,
540                 payee_features,
541                 Some(&first_hops.iter().collect::<Vec<_>>()),
542                 &route_hints.iter().collect::<Vec<_>>(),
543                 amt_msat,
544                 final_cltv,
545                 logger,
546         );
547         if let Err(e) = route {
548                 println!("ERROR: failed to find route: {}", e.err);
549                 return;
550         }
551         let status = match channel_manager.send_payment(&route.unwrap(), payment_hash, &payment_secret)
552         {
553                 Ok(()) => {
554                         println!("EVENT: initiated sending {} msats to {}", amt_msat, payee);
555                         HTLCStatus::Pending
556                 }
557                 Err(e) => {
558                         println!("ERROR: failed to send payment: {:?}", e);
559                         HTLCStatus::Failed
560                 }
561         };
562         let mut payments = payment_storage.lock().unwrap();
563         payments.insert(
564                 payment_hash,
565                 PaymentInfo {
566                         preimage: None,
567                         secret: payment_secret,
568                         status,
569                         amt_msat: MillisatAmount(Some(amt_msat)),
570                 },
571         );
572 }
573
574 fn get_invoice(
575         amt_msat: u64, payment_storage: PaymentInfoStorage, channel_manager: Arc<ChannelManager>,
576         keys_manager: Arc<KeysManager>, network: Network,
577 ) {
578         let mut payments = payment_storage.lock().unwrap();
579         let currency = match network {
580                 Network::Bitcoin => Currency::Bitcoin,
581                 Network::Testnet => Currency::BitcoinTestnet,
582                 Network::Regtest => Currency::Regtest,
583                 Network::Signet => panic!("Signet unsupported"),
584         };
585         let invoice = match utils::create_invoice_from_channelmanager(
586                 &channel_manager,
587                 keys_manager,
588                 currency,
589                 Some(amt_msat),
590                 "ldk-tutorial-node".to_string(),
591         ) {
592                 Ok(inv) => {
593                         println!("SUCCESS: generated invoice: {}", inv);
594                         inv
595                 }
596                 Err(e) => {
597                         println!("ERROR: failed to create invoice: {:?}", e);
598                         return;
599                 }
600         };
601
602         let mut payment_hash = PaymentHash([0; 32]);
603         payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
604         payments.insert(
605                 payment_hash,
606                 PaymentInfo {
607                         preimage: None,
608                         // We can't add payment secrets to invoices until we support features in invoices.
609                         // Otherwise lnd errors with "destination hop doesn't understand payment addresses"
610                         // (for context, lnd calls payment secrets "payment addresses").
611                         secret: Some(invoice.payment_secret().unwrap().clone()),
612                         status: HTLCStatus::Pending,
613                         amt_msat: MillisatAmount(Some(amt_msat)),
614                 },
615         );
616 }
617
618 fn close_channel(channel_id: [u8; 32], channel_manager: Arc<ChannelManager>) {
619         match channel_manager.close_channel(&channel_id) {
620                 Ok(()) => println!("EVENT: initiating channel close"),
621                 Err(e) => println!("ERROR: failed to close channel: {:?}", e),
622         }
623 }
624
625 fn force_close_channel(channel_id: [u8; 32], channel_manager: Arc<ChannelManager>) {
626         match channel_manager.force_close_channel(&channel_id) {
627                 Ok(()) => println!("EVENT: initiating channel force-close"),
628                 Err(e) => println!("ERROR: failed to force-close channel: {:?}", e),
629         }
630 }
631
632 pub(crate) fn parse_peer_info(
633         peer_pubkey_and_ip_addr: String,
634 ) -> Result<(PublicKey, SocketAddr), std::io::Error> {
635         let mut pubkey_and_addr = peer_pubkey_and_ip_addr.split("@");
636         let pubkey = pubkey_and_addr.next();
637         let peer_addr_str = pubkey_and_addr.next();
638         if peer_addr_str.is_none() || peer_addr_str.is_none() {
639                 return Err(std::io::Error::new(
640                         std::io::ErrorKind::Other,
641                         "ERROR: incorrectly formatted peer info. Should be formatted as: `pubkey@host:port`",
642                 ));
643         }
644
645         let peer_addr = peer_addr_str.unwrap().to_socket_addrs().map(|mut r| r.next());
646         if peer_addr.is_err() || peer_addr.as_ref().unwrap().is_none() {
647                 return Err(std::io::Error::new(
648                         std::io::ErrorKind::Other,
649                         "ERROR: couldn't parse pubkey@host:port into a socket address",
650                 ));
651         }
652
653         let pubkey = hex_utils::to_compressed_pubkey(pubkey.unwrap());
654         if pubkey.is_none() {
655                 return Err(std::io::Error::new(
656                         std::io::ErrorKind::Other,
657                         "ERROR: unable to parse given pubkey for node",
658                 ));
659         }
660
661         Ok((pubkey.unwrap(), peer_addr.unwrap().unwrap()))
662 }