X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fmain.rs;fp=src%2Fmain.rs;h=5a3389e83430852276244d548f48e19b131a60c9;hb=30a9cb780d2dbecfb22e8209e10e60e371f48310;hp=159fa5b777c4d70a317128c072f12f9ec6c20f63;hpb=7aceee96f2642d6dd7ef391a4d25df08cc3b9c2d;p=ldk-sample diff --git a/src/main.rs b/src/main.rs index 159fa5b..5a3389e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,18 +4,19 @@ mod cli; mod convert; mod disk; mod hex_utils; +mod sweep; use crate::bitcoind_client::BitcoindClient; use crate::disk::FilesystemLogger; use bitcoin::blockdata::transaction::Transaction; use bitcoin::consensus::encode; use bitcoin::network::constants::Network; -use bitcoin::secp256k1::Secp256k1; use bitcoin::BlockHash; use bitcoin_bech32::WitnessProgram; use lightning::chain; -use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::chain::keysinterface::{EntropySource, InMemorySigner, KeysManager}; +use lightning::chain::keysinterface::{ + EntropySource, InMemorySigner, KeysManager, SpendableOutputDescriptor, +}; use lightning::chain::{chainmonitor, ChannelMonitorUpdateStatus}; use lightning::chain::{Filter, Watch}; use lightning::events::{Event, PaymentFailureReason, PaymentPurpose}; @@ -30,6 +31,7 @@ use lightning::routing::gossip; use lightning::routing::gossip::{NodeId, P2PGossipSync}; use lightning::routing::router::DefaultRouter; use lightning::util::config::UserConfig; +use lightning::util::persist::KVStorePersister; use lightning::util::ser::ReadableArgs; use lightning_background_processor::{process_events_async, GossipSync}; use lightning_block_sync::init; @@ -52,6 +54,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; +pub(crate) const PENDING_SPENDABLE_OUTPUT_DIR: &'static str = "pending_spendable_outputs"; + pub(crate) enum HTLCStatus { Pending, Succeeded, @@ -107,7 +111,7 @@ async fn handle_ldk_events( channel_manager: &Arc, bitcoind_client: &BitcoindClient, network_graph: &NetworkGraph, keys_manager: &KeysManager, inbound_payments: &PaymentInfoStorage, outbound_payments: &PaymentInfoStorage, - network: Network, event: Event, + persister: &Arc, network: Network, event: Event, ) { match event { Event::FundingGenerationReady { @@ -331,20 +335,23 @@ async fn handle_ldk_events( }); } Event::SpendableOutputs { outputs } => { - let destination_address = bitcoind_client.get_new_address().await; - let output_descriptors = &outputs.iter().map(|a| a).collect::>(); - let tx_feerate = - bitcoind_client.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); - let spending_tx = keys_manager - .spend_spendable_outputs( - output_descriptors, - Vec::new(), - destination_address.script_pubkey(), - tx_feerate, - &Secp256k1::new(), - ) - .unwrap(); - bitcoind_client.broadcast_transaction(&spending_tx); + // SpendableOutputDescriptors, of which outputs is a vec of, are critical to keep track + // of! While a `StaticOutput` descriptor is just an output to a static, well-known key, + // other descriptors are not currently ever regenerated for you by LDK. Once we return + // from this method, the descriptor will be gone, and you may lose track of some funds. + // + // Here we simply persist them to disk, with a background task running which will try + // to spend them regularly (possibly duplicatively/RBF'ing them). These can just be + // treated as normal funds where possible - they are only spendable by us and there is + // no rush to claim them. + for output in outputs { + let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes()); + // Note that if the type here changes our read code needs to change as well. + let output: SpendableOutputDescriptor = output; + persister + .persist(&format!("{}/{}", PENDING_SPENDABLE_OUTPUT_DIR, key), &output) + .unwrap(); + } } Event::ChannelPending { channel_id, counterparty_node_id, .. } => { println!( @@ -693,6 +700,7 @@ async fn start_ldk() { let keys_manager_event_listener = Arc::clone(&keys_manager); let inbound_payments_event_listener = Arc::clone(&inbound_payments); let outbound_payments_event_listener = Arc::clone(&outbound_payments); + let persister_event_listener = Arc::clone(&persister); let network = args.network; let event_handler = move |event: Event| { let channel_manager_event_listener = Arc::clone(&channel_manager_event_listener); @@ -701,6 +709,7 @@ async fn start_ldk() { let keys_manager_event_listener = Arc::clone(&keys_manager_event_listener); let inbound_payments_event_listener = Arc::clone(&inbound_payments_event_listener); let outbound_payments_event_listener = Arc::clone(&outbound_payments_event_listener); + let persister_event_listener = Arc::clone(&persister_event_listener); async move { handle_ldk_events( &channel_manager_event_listener, @@ -709,6 +718,7 @@ async fn start_ldk() { &keys_manager_event_listener, &inbound_payments_event_listener, &outbound_payments_event_listener, + &persister_event_listener, network, event, ) @@ -722,7 +732,7 @@ async fn start_ldk() { // Step 20: Background Processing let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(()); let background_processor = tokio::spawn(process_events_async( - persister, + Arc::clone(&persister), event_handler, chain_monitor.clone(), channel_manager.clone(), @@ -781,24 +791,38 @@ async fn start_ldk() { }); // Regularly broadcast our node_announcement. This is only required (or possible) if we have - // some public channels, and is only useful if we have public listen address(es) to announce. - // In a production environment, this should occur only after the announcement of new channels - // to avoid churn in the global network graph. + // some public channels. let peer_man = Arc::clone(&peer_manager); + let chan_man = Arc::clone(&channel_manager); let network = args.network; - if !args.ldk_announced_listen_addr.is_empty() { - tokio::spawn(async move { - let mut interval = tokio::time::interval(Duration::from_secs(60)); - loop { - interval.tick().await; + tokio::spawn(async move { + // First wait a minute until we have some peers and maybe have opened a channel. + tokio::time::sleep(Duration::from_secs(60)).await; + // Then, update our announcement once an hour to keep it fresh but avoid unnecessary churn + // in the global gossip network. + let mut interval = tokio::time::interval(Duration::from_secs(3600)); + loop { + interval.tick().await; + // Don't bother trying to announce if we don't have any public channls, though our + // peers should drop such an announcement anyway. Note that announcement may not + // propagate until we have a channel with 6+ confirmations. + if chan_man.list_channels().iter().any(|chan| chan.is_public) { peer_man.broadcast_node_announcement( [0; 3], args.ldk_announced_node_name, args.ldk_announced_listen_addr.clone(), ); } - }); - } + } + }); + + tokio::spawn(sweep::periodic_sweep( + ldk_data_dir.clone(), + Arc::clone(&keys_manager), + Arc::clone(&logger), + Arc::clone(&persister), + Arc::clone(&bitcoind_client), + )); // Start the CLI. cli::poll_for_user_input( @@ -809,7 +833,7 @@ async fn start_ldk() { Arc::clone(&onion_messenger), inbound_payments, outbound_payments, - ldk_data_dir.clone(), + ldk_data_dir, network, Arc::clone(&logger), )