]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Test that we do not fail-backwards HTLCs that the remote on-chained
authorMatt Corallo <git@bluematt.me>
Fri, 14 Sep 2018 19:19:03 +0000 (15:19 -0400)
committerMatt Corallo <git@bluematt.me>
Fri, 14 Sep 2018 19:27:54 +0000 (15:27 -0400)
src/ln/channelmanager.rs
src/ln/channelmonitor.rs

index 5bfe36881afe544b358c41c21c5508ea7ca97201..765c417f3a6856c0f56543cc84d7000655a10a54 100644 (file)
@@ -2198,6 +2198,7 @@ mod tests {
        use bitcoin::util::hash::Sha256dHash;
        use bitcoin::blockdata::block::{Block, BlockHeader};
        use bitcoin::blockdata::transaction::{Transaction, TxOut};
+       use bitcoin::blockdata::transaction::OutPoint as BitcoinOutPoint;
        use bitcoin::blockdata::constants::genesis_block;
        use bitcoin::network::constants::Network;
        use bitcoin::network::serialize::serialize;
@@ -3401,6 +3402,125 @@ mod tests {
                nodes[1].chain_monitor.block_connected_checked(&header, 1, &[&node_txn[0], &node_txn[1]], &[1; 2]);
        }
 
+       #[test]
+       fn test_force_close_fail_back() {
+               // Check which HTLCs are failed-backwards on channel force-closure
+               let mut nodes = create_network(3);
+               create_announced_chan_between_nodes(&nodes, 0, 1);
+               create_announced_chan_between_nodes(&nodes, 1, 2);
+
+               let route = nodes[0].router.get_route(&nodes[2].node.get_our_node_id(), None, &Vec::new(), 1000000, 42).unwrap();
+
+               let our_payment_preimage = [*nodes[0].network_payment_count.borrow(); 32];
+               *nodes[0].network_payment_count.borrow_mut() += 1;
+               let our_payment_hash = {
+                       let mut sha = Sha256::new();
+                       sha.input(&our_payment_preimage[..]);
+                       let mut ret = [0; 32];
+                       sha.result(&mut ret);
+                       ret
+               };
+
+               let mut payment_event = {
+                       nodes[0].node.send_payment(route, our_payment_hash).unwrap();
+                       {
+                               let mut added_monitors = nodes[0].chan_monitor.added_monitors.lock().unwrap();
+                               assert_eq!(added_monitors.len(), 1);
+                               added_monitors.clear();
+                       }
+
+                       let mut events = nodes[0].node.get_and_clear_pending_events();
+                       assert_eq!(events.len(), 1);
+                       SendEvent::from_event(events.remove(0))
+               };
+
+               nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]).unwrap();
+               commitment_signed_dance!(nodes[1], nodes[0], payment_event.commitment_msg, false);
+
+               let events_1 = nodes[1].node.get_and_clear_pending_events();
+               assert_eq!(events_1.len(), 1);
+               match events_1[0] {
+                       Event::PendingHTLCsForwardable { .. } => { },
+                       _ => panic!("Unexpected event"),
+               };
+
+               nodes[1].node.channel_state.lock().unwrap().next_forward = Instant::now();
+               nodes[1].node.process_pending_htlc_forwards();
+
+               let mut events_2 = nodes[1].node.get_and_clear_pending_events();
+               assert_eq!(events_2.len(), 1);
+               payment_event = SendEvent::from_event(events_2.remove(0));
+               assert_eq!(payment_event.msgs.len(), 1);
+
+               {
+                       let mut added_monitors = nodes[1].chan_monitor.added_monitors.lock().unwrap();
+                       assert_eq!(added_monitors.len(), 1);
+                       added_monitors.clear();
+               }
+
+               nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event.msgs[0]).unwrap();
+               nodes[2].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &payment_event.commitment_msg).unwrap();
+
+               {
+                       let mut added_monitors = nodes[2].chan_monitor.added_monitors.lock().unwrap();
+                       assert_eq!(added_monitors.len(), 1);
+                       added_monitors.clear();
+               }
+
+               // nodes[2] now has the latest commitment transaction, but hasn't revoked its previous
+               // state or updated nodes[1]' state. Now force-close and broadcast that commitment/HTLC
+               // transaction and ensure nodes[1] doesn't fail-backwards (this was originally a bug!).
+
+               nodes[2].node.force_close_channel(&payment_event.commitment_msg.channel_id);
+               let events_3 = nodes[2].node.get_and_clear_pending_events();
+               assert_eq!(events_3.len(), 1);
+               match events_3[0] {
+                       Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { contents: msgs::UnsignedChannelUpdate { flags, .. }, .. } } => {
+                               assert_eq!(flags & 0b10, 0b10);
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+
+               let tx = {
+                       let mut node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
+                       // Note that we don't bother broadcasting the HTLC-Success transaction here as we don't
+                       // have a use for it unless nodes[2] learns the preimage somehow, the funds will go
+                       // back to nodes[1] upon timeout otherwise.
+                       assert_eq!(node_txn.len(), 1);
+                       node_txn.remove(0)
+               };
+
+               let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+               nodes[1].chain_monitor.block_connected_checked(&header, 1, &[&tx], &[1]);
+
+               let events_4 = nodes[1].node.get_and_clear_pending_events();
+               // Note no UpdateHTLCs event here from nodes[1] to nodes[0]!
+               assert_eq!(events_4.len(), 1);
+               match events_4[0] {
+                       Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { contents: msgs::UnsignedChannelUpdate { flags, .. }, .. } } => {
+                               assert_eq!(flags & 0b10, 0b10);
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+
+               // Now check that if we add the preimage to ChannelMonitor it broadcasts our HTLC-Success..
+               {
+                       let mut monitors = nodes[2].chan_monitor.simple_monitor.monitors.lock().unwrap();
+                       monitors.get_mut(&OutPoint::new(Sha256dHash::from(&payment_event.commitment_msg.channel_id[..]), 0)).unwrap()
+                               .provide_payment_preimage(&our_payment_hash, &our_payment_preimage);
+               }
+               nodes[2].chain_monitor.block_connected_checked(&header, 1, &[&tx], &[1]);
+               let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
+               assert_eq!(node_txn.len(), 1);
+               assert_eq!(node_txn[0].input.len(), 1);
+               assert_eq!(node_txn[0].input[0].previous_output.txid, tx.txid());
+               assert_eq!(node_txn[0].lock_time, 0); // Must be an HTLC-Success
+               assert_eq!(node_txn[0].input[0].witness.len(), 5); // Must be an HTLC-Success
+               let mut funding_tx_map = HashMap::new();
+               funding_tx_map.insert(tx.txid(), tx);
+               node_txn[0].verify(&funding_tx_map).unwrap();
+       }
+
        #[test]
        fn test_unconf_chan() {
                // After creating a chan between nodes, we disconnect all blocks previously seen to force a channel close on nodes[0] side
index 55e534c826969d3b1af39c11fe32b3ff860987f2..ded2a99dc37cf2933150d8537ec31ecfaddc073e 100644 (file)
@@ -63,6 +63,9 @@ pub trait ManyChannelMonitor: Send + Sync {
 /// If you're using this for local monitoring of your own channels, you probably want to use
 /// `OutPoint` as the key, which will give you a ManyChannelMonitor implementation.
 pub struct SimpleManyChannelMonitor<Key> {
+       #[cfg(test)] // Used in ChannelManager tests to manipulate channels directly
+       pub monitors: Mutex<HashMap<Key, ChannelMonitor>>,
+       #[cfg(not(test))]
        monitors: Mutex<HashMap<Key, ChannelMonitor>>,
        chain_monitor: Arc<ChainWatchInterface>,
        broadcaster: Arc<BroadcasterInterface>