Use new ChannelError in channel_reestablish handling
[rust-lightning] / src / ln / channelmanager.rs
index 8875f9c9eea7cb3e8d61fc140b4cc08c242c2506..a9b3dfcd8a9d3751ee7b643dc49c95f951fa27cb 100644 (file)
@@ -22,7 +22,7 @@ use secp256k1;
 
 use chain::chaininterface::{BroadcasterInterface,ChainListener,ChainWatchInterface,FeeEstimator};
 use chain::transaction::OutPoint;
-use ln::channel::{Channel, ChannelKeys};
+use ln::channel::{Channel, ChannelError, ChannelKeys};
 use ln::channelmonitor::ManyChannelMonitor;
 use ln::router::{Route,RouteHop};
 use ln::msgs;
@@ -173,6 +173,48 @@ impl MsgHandleErrInternal {
        fn from_no_close(err: msgs::HandleError) -> Self {
                Self { err, needs_channel_force_close: false }
        }
+       #[inline]
+       fn from_chan_no_close(err: ChannelError, channel_id: [u8; 32]) -> Self {
+               Self {
+                       err: match err {
+                               ChannelError::Ignore(msg) => HandleError {
+                                       err: msg,
+                                       action: Some(msgs::ErrorAction::IgnoreError),
+                               },
+                               ChannelError::Close(msg) => HandleError {
+                                       err: msg,
+                                       action: Some(msgs::ErrorAction::SendErrorMessage {
+                                               msg: msgs::ErrorMessage {
+                                                       channel_id,
+                                                       data: msg.to_string()
+                                               },
+                                       }),
+                               },
+                       },
+                       needs_channel_force_close: false,
+               }
+       }
+       #[inline]
+       fn from_chan_maybe_close(err: ChannelError, channel_id: [u8; 32]) -> Self {
+               Self {
+                       err: match err {
+                               ChannelError::Ignore(msg) => HandleError {
+                                       err: msg,
+                                       action: Some(msgs::ErrorAction::IgnoreError),
+                               },
+                               ChannelError::Close(msg) => HandleError {
+                                       err: msg,
+                                       action: Some(msgs::ErrorAction::SendErrorMessage {
+                                               msg: msgs::ErrorMessage {
+                                                       channel_id,
+                                                       data: msg.to_string()
+                                               },
+                                       }),
+                               },
+                       },
+                       needs_channel_force_close: true,
+               }
+       }
 }
 
 /// We hold back HTLCs we intend to relay for a random interval in the range (this, 5*this). This
@@ -1467,7 +1509,8 @@ impl ChannelManager {
                        }
                };
 
-               let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger)).map_err(|e| MsgHandleErrInternal::from_no_close(e))?;
+               let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger))
+                       .map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id))?;
                let accept_msg = channel.get_accept_channel();
                channel_state.by_id.insert(channel.channel_id(), channel);
                Ok(accept_msg)
@@ -1482,7 +1525,8 @@ impl ChannelManager {
                                                //TODO: see issue #153, need a consistent behavior on obnoxious behavior from random node
                                                return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.temporary_channel_id));
                                        }
-                                       chan.accept_channel(&msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?;
+                                       chan.accept_channel(&msg)
+                                               .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.temporary_channel_id))?;
                                        (chan.get_value_satoshis(), chan.get_funding_redeemscript().to_v0_p2wsh(), chan.get_user_id())
                                },
                                //TODO: same as above
@@ -1572,7 +1616,8 @@ impl ChannelManager {
                                        //TODO: here and below MsgHandleErrInternal, #153 case
                                        return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                }
-                               chan.funding_locked(&msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?;
+                               chan.funding_locked(&msg)
+                                       .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))?;
                                return Ok(self.get_announcement_sigs(chan));
                        },
                        None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
@@ -1692,7 +1737,8 @@ impl ChannelManager {
                                        //TODO: here and below MsgHandleErrInternal, #153 case
                                        return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                }
-                               chan.update_fulfill_htlc(&msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?.clone()
+                               chan.update_fulfill_htlc(&msg)
+                                       .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))?.clone()
                        },
                        None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
                };
@@ -1708,7 +1754,8 @@ impl ChannelManager {
                                        //TODO: here and below MsgHandleErrInternal, #153 case
                                        return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                }
-                               chan.update_fail_htlc(&msg, HTLCFailReason::ErrorPacket { err: msg.reason.clone() }).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))
+                               chan.update_fail_htlc(&msg, HTLCFailReason::ErrorPacket { err: msg.reason.clone() })
+                                       .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))
                        },
                        None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
                }?;
@@ -1780,7 +1827,11 @@ impl ChannelManager {
                                        //TODO: here and below MsgHandleErrInternal, #153 case
                                        return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                }
-                               chan.update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() }).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?;
+                               if (msg.failure_code & 0x8000) != 0 {
+                                       return Err(MsgHandleErrInternal::send_err_msg_close_chan("Got update_fail_malformed_htlc with BADONION set", msg.channel_id));
+                               }
+                               chan.update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() })
+                                       .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))?;
                                Ok(())
                        },
                        None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
@@ -1868,7 +1919,7 @@ impl ChannelManager {
                                        //TODO: here and below MsgHandleErrInternal, #153 case
                                        return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                }
-                               chan.update_fee(&*self.fee_estimator, &msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))
+                               chan.update_fee(&*self.fee_estimator, &msg).map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))
                        },
                        None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
                }
@@ -1922,7 +1973,8 @@ impl ChannelManager {
                                        if chan.get_their_node_id() != *their_node_id {
                                                return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id));
                                        }
-                                       let (funding_locked, revoke_and_ack, commitment_update, channel_monitor) = chan.channel_reestablish(msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e))?;
+                                       let (funding_locked, revoke_and_ack, commitment_update, channel_monitor) = chan.channel_reestablish(msg)
+                                               .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id))?;
                                        (Ok((funding_locked, revoke_and_ack, commitment_update)), channel_monitor)
                                },
                                None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id))
@@ -2326,7 +2378,7 @@ mod tests {
        use rand::{thread_rng,Rng};
 
        use std::cell::RefCell;
-       use std::collections::HashMap;
+       use std::collections::{BTreeSet, HashMap};
        use std::default::Default;
        use std::rc::Rc;
        use std::sync::{Arc, Mutex};
@@ -2648,6 +2700,17 @@ mod tests {
                (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4)
        }
 
+       macro_rules! check_spends {
+               ($tx: expr, $spends_tx: expr) => {
+                       {
+                               let mut funding_tx_map = HashMap::new();
+                               let spends_tx = $spends_tx;
+                               funding_tx_map.insert(spends_tx.txid(), spends_tx);
+                               $tx.verify(&funding_tx_map).unwrap();
+                       }
+               }
+       }
+
        fn close_channel(outbound_node: &Node, inbound_node: &Node, channel_id: &[u8; 32], funding_tx: Transaction, close_inbound_first: bool) -> (msgs::ChannelUpdate, msgs::ChannelUpdate) {
                let (node_a, broadcaster_a) = if close_inbound_first { (&inbound_node.node, &inbound_node.tx_broadcaster) } else { (&outbound_node.node, &outbound_node.tx_broadcaster) };
                let (node_b, broadcaster_b) = if close_inbound_first { (&outbound_node.node, &outbound_node.tx_broadcaster) } else { (&inbound_node.node, &inbound_node.tx_broadcaster) };
@@ -2691,9 +2754,7 @@ mod tests {
                        tx_a = broadcaster_a.txn_broadcasted.lock().unwrap().remove(0);
                }
                assert_eq!(tx_a, tx_b);
-               let mut funding_tx_map = HashMap::new();
-               funding_tx_map.insert(funding_tx.txid(), funding_tx);
-               tx_a.verify(&funding_tx_map).unwrap();
+               check_spends!(tx_a, funding_tx);
 
                let events_2 = node_a.get_and_clear_pending_events();
                assert_eq!(events_2.len(), 1);
@@ -3722,9 +3783,7 @@ mod tests {
                let mut res = Vec::with_capacity(2);
                node_txn.retain(|tx| {
                        if tx.input.len() == 1 && tx.input[0].previous_output.txid == chan.3.txid() {
-                               let mut funding_tx_map = HashMap::new();
-                               funding_tx_map.insert(chan.3.txid(), chan.3.clone());
-                               tx.verify(&funding_tx_map).unwrap();
+                               check_spends!(tx, chan.3.clone());
                                if commitment_tx.is_none() {
                                        res.push(tx.clone());
                                }
@@ -3740,9 +3799,7 @@ mod tests {
                if has_htlc_tx != HTLCType::NONE {
                        node_txn.retain(|tx| {
                                if tx.input.len() == 1 && tx.input[0].previous_output.txid == res[0].txid() {
-                                       let mut funding_tx_map = HashMap::new();
-                                       funding_tx_map.insert(res[0].txid(), res[0].clone());
-                                       tx.verify(&funding_tx_map).unwrap();
+                                       check_spends!(tx, res[0].clone());
                                        if has_htlc_tx == HTLCType::TIMEOUT {
                                                assert!(tx.lock_time != 0);
                                        } else {
@@ -3766,9 +3823,7 @@ mod tests {
                assert_eq!(node_txn.len(), 1);
                node_txn.retain(|tx| {
                        if tx.input.len() == 1 && tx.input[0].previous_output.txid == revoked_tx.txid() {
-                               let mut funding_tx_map = HashMap::new();
-                               funding_tx_map.insert(revoked_tx.txid(), revoked_tx.clone());
-                               tx.verify(&funding_tx_map).unwrap();
+                               check_spends!(tx, revoked_tx.clone());
                                false
                        } else { true }
                });
@@ -3784,10 +3839,7 @@ mod tests {
 
                for tx in prev_txn {
                        if node_txn[0].input[0].previous_output.txid == tx.txid() {
-                               let mut funding_tx_map = HashMap::new();
-                               funding_tx_map.insert(tx.txid(), tx.clone());
-                               node_txn[0].verify(&funding_tx_map).unwrap();
-
+                               check_spends!(node_txn[0], tx.clone());
                                assert!(node_txn[0].input[0].witness[2].len() > 106); // must spend an htlc output
                                assert_eq!(tx.input.len(), 1); // must spend a commitment tx
 
@@ -3951,6 +4003,13 @@ mod tests {
                let payment_preimage_3 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
                // Get the will-be-revoked local txn from nodes[0]
                let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.iter().next().unwrap().1.last_local_commitment_txn.clone();
+               assert_eq!(revoked_local_txn.len(), 2); // First commitment tx, then HTLC tx
+               assert_eq!(revoked_local_txn[0].input.len(), 1);
+               assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_5.3.txid());
+               assert_eq!(revoked_local_txn[0].output.len(), 2); // Only HTLC and output back to 0 are present
+               assert_eq!(revoked_local_txn[1].input.len(), 1);
+               assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
+               assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout
                // Revoke the old state
                claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_3);
 
@@ -3961,11 +4020,9 @@ mod tests {
                                let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
                                assert_eq!(node_txn.len(), 3);
                                assert_eq!(node_txn.pop().unwrap(), node_txn[0]); // An outpoint registration will result in a 2nd block_connected
-                               assert_eq!(node_txn[0].input.len(), 1);
+                               assert_eq!(node_txn[0].input.len(), 2); // We should claim the revoked output and the HTLC output
 
-                               let mut funding_tx_map = HashMap::new();
-                               funding_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone());
-                               node_txn[0].verify(&funding_tx_map).unwrap();
+                               check_spends!(node_txn[0], revoked_local_txn[0].clone());
                                node_txn.swap_remove(0);
                        }
                        test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
@@ -3981,6 +4038,173 @@ mod tests {
                assert_eq!(nodes[1].node.list_channels().len(), 0);
        }
 
+       #[test]
+       fn revoked_output_claim() {
+               // Simple test to ensure a node will claim a revoked output when a stale remote commitment
+               // transaction is broadcast by its counterparty
+               let nodes = create_network(2);
+               let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
+               // node[0] is gonna to revoke an old state thus node[1] should be able to claim the revoked output
+               let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
+               assert_eq!(revoked_local_txn.len(), 1);
+               // Only output is the full channel value back to nodes[0]:
+               assert_eq!(revoked_local_txn[0].output.len(), 1);
+               // Send a payment through, updating everyone's latest commitment txn
+               send_payment(&nodes[0], &vec!(&nodes[1])[..], 5000000);
+
+               // Inform nodes[1] that nodes[0] broadcast a stale tx
+               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_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
+               let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
+               assert_eq!(node_txn.len(), 3); // nodes[1] will broadcast justice tx twice, and its own local state once
+
+               assert_eq!(node_txn[0], node_txn[2]);
+
+               check_spends!(node_txn[0], revoked_local_txn[0].clone());
+               check_spends!(node_txn[1], chan_1.3.clone());
+
+               // Inform nodes[0] that a watchtower cheated on its behalf, so it will force-close the chan
+               nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
+               get_announce_close_broadcast_events(&nodes, 0, 1);
+       }
+
+       #[test]
+       fn claim_htlc_outputs_shared_tx() {
+               // Node revoked old state, htlcs haven't time out yet, claim them in shared justice tx
+               let nodes = create_network(2);
+
+               // Create some new channel:
+               let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+               // Rebalance the network to generate htlc in the two directions
+               send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
+               // node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx
+               let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
+               let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
+
+               // Get the will-be-revoked local txn from node[0]
+               let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
+               assert_eq!(revoked_local_txn.len(), 2); // commitment tx + 1 HTLC-Timeout tx
+               assert_eq!(revoked_local_txn[0].input.len(), 1);
+               assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_1.3.txid());
+               assert_eq!(revoked_local_txn[1].input.len(), 1);
+               assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
+               assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout
+               check_spends!(revoked_local_txn[1], revoked_local_txn[0].clone());
+
+               //Revoke the old state
+               claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1);
+
+               {
+                       let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+
+                       nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
+
+                       nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
+                       let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
+                       assert_eq!(node_txn.len(), 4);
+
+                       assert_eq!(node_txn[0].input.len(), 3); // Claim the revoked output + both revoked HTLC outputs
+                       check_spends!(node_txn[0], revoked_local_txn[0].clone());
+
+                       assert_eq!(node_txn[0], node_txn[3]); // justice tx is duplicated due to block re-scanning
+
+                       let mut witness_lens = BTreeSet::new();
+                       witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len());
+                       witness_lens.insert(node_txn[0].input[1].witness.last().unwrap().len());
+                       witness_lens.insert(node_txn[0].input[2].witness.last().unwrap().len());
+                       assert_eq!(witness_lens.len(), 3);
+                       assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
+                       assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC
+                       assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC
+
+                       // Next nodes[1] broadcasts its current local tx state:
+                       assert_eq!(node_txn[1].input.len(), 1);
+                       assert_eq!(node_txn[1].input[0].previous_output.txid, chan_1.3.txid()); //Spending funding tx unique txouput, tx broadcasted by ChannelManager
+
+                       assert_eq!(node_txn[2].input.len(), 1);
+                       let witness_script = node_txn[2].clone().input[0].witness.pop().unwrap();
+                       assert_eq!(witness_script.len(), 133); //Spending an offered htlc output
+                       assert_eq!(node_txn[2].input[0].previous_output.txid, node_txn[1].txid());
+                       assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid);
+                       assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[1].previous_output.txid);
+               }
+               get_announce_close_broadcast_events(&nodes, 0, 1);
+               assert_eq!(nodes[0].node.list_channels().len(), 0);
+               assert_eq!(nodes[1].node.list_channels().len(), 0);
+       }
+
+       #[test]
+       fn claim_htlc_outputs_single_tx() {
+               // Node revoked old state, htlcs have timed out, claim each of them in separated justice tx
+               let nodes = create_network(2);
+
+               let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+               // Rebalance the network to generate htlc in the two directions
+               send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
+               // node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx, but this
+               // time as two different claim transactions as we're gonna to timeout htlc with given a high current height
+               let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
+               let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
+
+               // Get the will-be-revoked local txn from node[0]
+               let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
+
+               //Revoke the old state
+               claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1);
+
+               {
+                       let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+
+                       nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
+
+                       nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
+                       let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
+                       assert_eq!(node_txn.len(), 12); // ChannelManager : 2, ChannelMontitor: 8 (1 standard revoked output, 2 revocation htlc tx, 1 local commitment tx + 1 htlc timeout tx) * 2 (block-rescan)
+
+                       assert_eq!(node_txn[0], node_txn[7]);
+                       assert_eq!(node_txn[1], node_txn[8]);
+                       assert_eq!(node_txn[2], node_txn[9]);
+                       assert_eq!(node_txn[3], node_txn[10]);
+                       assert_eq!(node_txn[4], node_txn[11]);
+                       assert_eq!(node_txn[3], node_txn[5]); //local commitment tx + htlc timeout tx broadcated by ChannelManger
+                       assert_eq!(node_txn[4], node_txn[6]);
+
+                       assert_eq!(node_txn[0].input.len(), 1);
+                       assert_eq!(node_txn[1].input.len(), 1);
+                       assert_eq!(node_txn[2].input.len(), 1);
+
+                       let mut revoked_tx_map = HashMap::new();
+                       revoked_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone());
+                       node_txn[0].verify(&revoked_tx_map).unwrap();
+                       node_txn[1].verify(&revoked_tx_map).unwrap();
+                       node_txn[2].verify(&revoked_tx_map).unwrap();
+
+                       let mut witness_lens = BTreeSet::new();
+                       witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len());
+                       witness_lens.insert(node_txn[1].input[0].witness.last().unwrap().len());
+                       witness_lens.insert(node_txn[2].input[0].witness.last().unwrap().len());
+                       assert_eq!(witness_lens.len(), 3);
+                       assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
+                       assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC
+                       assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC
+
+                       assert_eq!(node_txn[3].input.len(), 1);
+                       check_spends!(node_txn[3], chan_1.3.clone());
+
+                       assert_eq!(node_txn[4].input.len(), 1);
+                       let witness_script = node_txn[4].input[0].witness.last().unwrap();
+                       assert_eq!(witness_script.len(), 133); //Spending an offered htlc output
+                       assert_eq!(node_txn[4].input[0].previous_output.txid, node_txn[3].txid());
+                       assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid);
+                       assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[1].input[0].previous_output.txid);
+               }
+               get_announce_close_broadcast_events(&nodes, 0, 1);
+               assert_eq!(nodes[0].node.list_channels().len(), 0);
+               assert_eq!(nodes[1].node.list_channels().len(), 0);
+       }
+
        #[test]
        fn test_htlc_ignore_latest_remote_commitment() {
                // Test that HTLC transactions spending the latest remote commitment transaction are simply
@@ -4115,9 +4339,8 @@ mod tests {
                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();
+
+               check_spends!(node_txn[0], tx);
        }
 
        #[test]