From dc0340ebc257563f6a08fdc40a86eaadbadcc190 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 12 Jan 2024 17:46:37 +0100 Subject: [PATCH] Add basic `OutputSweeper` test in BP .. we simply check that the `OutputSweeper` generates a spending tx and that the `TrackedSpendableOutput` is pruned once it reaches `ANTI_REORG_DELAY`. --- lightning-background-processor/src/lib.rs | 50 ++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/lightning-background-processor/src/lib.rs b/lightning-background-processor/src/lib.rs index 0f2c67538d6..2c1c75e1fb6 100644 --- a/lightning-background-processor/src/lib.rs +++ b/lightning-background-processor/src/lib.rs @@ -918,17 +918,18 @@ impl Drop for BackgroundProcessor { #[cfg(all(feature = "std", test))] mod tests { + use bitcoin::ScriptBuf; use bitcoin::blockdata::constants::{genesis_block, ChainHash}; use bitcoin::blockdata::locktime::absolute::LockTime; use bitcoin::blockdata::transaction::{Transaction, TxOut}; use bitcoin::network::constants::Network; use bitcoin::secp256k1::{SecretKey, PublicKey, Secp256k1}; - use lightning::chain::{BestBlock, Confirm, chainmonitor}; + use lightning::chain::{BestBlock, Confirm, chainmonitor, Filter}; use lightning::chain::channelmonitor::ANTI_REORG_DELAY; - use lightning::sign::{InMemorySigner, KeysManager}; + use lightning::chain::chaininterface::{ConfirmationTarget, FeeEstimator}; + use lightning::sign::{InMemorySigner, KeysManager, SpendableOutputDescriptor}; use lightning::chain::transaction::OutPoint; use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent}; - use lightning::{get_event_msg, get_event}; use lightning::ln::PaymentHash; use lightning::ln::channelmanager; use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId}; @@ -946,6 +947,7 @@ mod tests { CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, CHANNEL_MANAGER_PERSISTENCE_KEY, NETWORK_GRAPH_PERSISTENCE_PRIMARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_SECONDARY_NAMESPACE, NETWORK_GRAPH_PERSISTENCE_KEY, SCORER_PERSISTENCE_PRIMARY_NAMESPACE, SCORER_PERSISTENCE_SECONDARY_NAMESPACE, SCORER_PERSISTENCE_KEY}; + use lightning::util::sweep::OutputSweeper; use lightning_persister::fs_store::FilesystemStore; use std::collections::VecDeque; use std::{fs, env}; @@ -1007,6 +1009,7 @@ mod tests { logger: Arc, best_block: BestBlock, scorer: Arc>, + sweeper: Arc, Arc, Arc, Arc, Arc>>, } impl Node { @@ -1269,10 +1272,29 @@ mod tests { let kv_store = Arc::new(FilesystemStore::new(format!("{}_persister_{}", &persist_dir, i).into())); let now = Duration::from_secs(genesis_block.header.time as u64); let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos())); + let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new(Some(chain_source.clone()), tx_broadcaster.clone(), logger.clone(), fee_estimator.clone(), kv_store.clone())); let best_block = BestBlock::from_network(network); let params = ChainParameters { network, best_block }; let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), router.clone(), logger.clone(), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), UserConfig::default(), params, genesis_block.header.time)); + + let spend_fee_estimator = Arc::clone(&fee_estimator); + let spend_keys_manager = Arc::clone(&keys_manager); + let spend_outputs_callback = move |output_descriptors: &[&SpendableOutputDescriptor]| { + let fee_rate = spend_fee_estimator + .get_est_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee); + spend_keys_manager.spend_spendable_outputs( + output_descriptors, + Vec::new(), + ScriptBuf::new(), + fee_rate, + None, + &Secp256k1::new(), + ) + }; + let sweeper = Arc::new(OutputSweeper::new(Arc::clone(&tx_broadcaster), + Arc::clone(&keys_manager), Arc::clone(&kv_store), best_block, + None::>, Arc::clone(&logger), None, spend_outputs_callback)); let p2p_gossip_sync = Arc::new(P2PGossipSync::new(network_graph.clone(), Some(chain_source.clone()), logger.clone())); let rapid_gossip_sync = Arc::new(RapidGossipSync::new(network_graph.clone(), logger.clone())); let msg_handler = MessageHandler { @@ -1281,7 +1303,7 @@ mod tests { onion_message_handler: IgnoringMessageHandler{}, custom_message_handler: IgnoringMessageHandler{} }; let peer_manager = Arc::new(PeerManager::new(msg_handler, 0, &seed, logger.clone(), keys_manager.clone())); - let node = Node { node: manager, p2p_gossip_sync, rapid_gossip_sync, peer_manager, chain_monitor, kv_store, tx_broadcaster, network_graph, logger, best_block, scorer }; + let node = Node { node: manager, p2p_gossip_sync, rapid_gossip_sync, peer_manager, chain_monitor, kv_store, tx_broadcaster, network_graph, logger, best_block, scorer, sweeper }; nodes.push(node); } @@ -1350,15 +1372,18 @@ mod tests { 1 => { node.node.transactions_confirmed(&header, &txdata, height); node.chain_monitor.transactions_confirmed(&header, &txdata, height); + node.sweeper.transactions_confirmed(&header, &txdata, height); }, x if x == depth => { node.node.best_block_updated(&header, height); node.chain_monitor.best_block_updated(&header, height); + node.sweeper.best_block_updated(&header, height); }, _ => {}, } } } + fn confirm_transaction(node: &mut Node, tx: &Transaction) { confirm_transaction_depth(node, tx, ANTI_REORG_DELAY); } @@ -1590,6 +1615,9 @@ mod tests { let _as_channel_update = get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); nodes[1].node.handle_channel_ready(&nodes[0].node.get_our_node_id(), &as_funding); let _bs_channel_update = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id()); + let broadcast_funding = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap(); + assert_eq!(broadcast_funding.txid(), funding_tx.txid()); + assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty()); if !std::thread::panicking() { bg_processor.stop().unwrap(); @@ -1615,10 +1643,22 @@ mod tests { .recv_timeout(Duration::from_secs(EVENT_DEADLINE)) .expect("Events not handled within deadline"); match event { - Event::SpendableOutputs { .. } => {}, + Event::SpendableOutputs { outputs, channel_id } => { + nodes[0].sweeper.track_spendable_outputs(outputs, channel_id, false); + }, _ => panic!("Unexpected event: {:?}", event), } + assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1); + let tracked_output = nodes[0].sweeper.tracked_spendable_outputs().first().unwrap().clone(); + let sweep_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().pop().unwrap(); + assert_eq!(sweep_tx.txid(), tracked_output.latest_spending_tx.as_ref().unwrap().txid()); + + confirm_transaction_depth(&mut nodes[0], &sweep_tx, 5); + assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 1); + confirm_transaction_depth(&mut nodes[0], &sweep_tx, ANTI_REORG_DELAY); + assert_eq!(nodes[0].sweeper.tracked_spendable_outputs().len(), 0); + if !std::thread::panicking() { bg_processor.stop().unwrap(); }