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;
}
}
+ macro_rules! commitment_signed_dance {
+ ($node_a: expr, $node_b: expr, $commitment_signed: expr, $fail_backwards: expr) => {
+ {
+ {
+ let added_monitors = $node_a.chan_monitor.added_monitors.lock().unwrap();
+ assert!(added_monitors.is_empty());
+ }
+ let (as_revoke_and_ack, as_commitment_signed) = $node_a.node.handle_commitment_signed(&$node_b.node.get_our_node_id(), &$commitment_signed).unwrap();
+ {
+ let mut added_monitors = $node_a.chan_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), 1);
+ added_monitors.clear();
+ }
+ {
+ let added_monitors = $node_b.chan_monitor.added_monitors.lock().unwrap();
+ assert!(added_monitors.is_empty());
+ }
+ assert!($node_b.node.handle_revoke_and_ack(&$node_a.node.get_our_node_id(), &as_revoke_and_ack).unwrap().is_none());
+ {
+ let mut added_monitors = $node_b.chan_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), 1);
+ added_monitors.clear();
+ }
+ let (bs_revoke_and_ack, bs_none) = $node_b.node.handle_commitment_signed(&$node_a.node.get_our_node_id(), &as_commitment_signed.unwrap()).unwrap();
+ assert!(bs_none.is_none());
+ {
+ let mut added_monitors = $node_b.chan_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), 1);
+ added_monitors.clear();
+ }
+ if $fail_backwards {
+ assert!($node_a.node.get_and_clear_pending_events().is_empty());
+ }
+ assert!($node_a.node.handle_revoke_and_ack(&$node_b.node.get_our_node_id(), &bs_revoke_and_ack).unwrap().is_none());
+ {
+ let mut added_monitors = $node_a.chan_monitor.added_monitors.lock().unwrap();
+ if $fail_backwards {
+ assert_eq!(added_monitors.len(), 2);
+ assert!(added_monitors[0].0 != added_monitors[1].0);
+ } else {
+ assert_eq!(added_monitors.len(), 1);
+ }
+ added_monitors.clear();
+ }
+ }
+ }
+ }
+
fn send_along_route(origin_node: &Node, route: Route, expected_route: &[&Node], recv_value: u64) -> ([u8; 32], [u8; 32]) {
let our_payment_preimage = [*origin_node.network_payment_count.borrow(); 32];
*origin_node.network_payment_count.borrow_mut() += 1;
assert_eq!(added_monitors.len(), 0);
}
- let revoke_and_ack = node.node.handle_commitment_signed(&prev_node.node.get_our_node_id(), &payment_event.commitment_msg).unwrap();
- {
- let mut added_monitors = node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
- assert!(prev_node.node.handle_revoke_and_ack(&node.node.get_our_node_id(), &revoke_and_ack.0).unwrap().is_none());
- let prev_revoke_and_ack = prev_node.node.handle_commitment_signed(&node.node.get_our_node_id(), &revoke_and_ack.1.unwrap()).unwrap();
- {
- let mut added_monitors = prev_node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 2);
- added_monitors.clear();
- }
- assert!(node.node.handle_revoke_and_ack(&prev_node.node.get_our_node_id(), &prev_revoke_and_ack.0).unwrap().is_none());
- assert!(prev_revoke_and_ack.1.is_none());
- {
- let mut added_monitors = node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
+ commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
let events_1 = node.node.get_and_clear_pending_events();
assert_eq!(events_1.len(), 1);
}
added_monitors.clear();
}
- let revoke_and_commit = $node.node.handle_commitment_signed(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().1).unwrap();
- {
- let mut added_monitors = $node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
- assert!($prev_node.node.handle_revoke_and_ack(&$node.node.get_our_node_id(), &revoke_and_commit.0).unwrap().is_none());
- let revoke_and_ack = $prev_node.node.handle_commitment_signed(&$node.node.get_our_node_id(), &revoke_and_commit.1.unwrap()).unwrap();
- assert!(revoke_and_ack.1.is_none());
- {
- let mut added_monitors = $prev_node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 2);
- added_monitors.clear();
- }
- assert!($node.node.handle_revoke_and_ack(&$prev_node.node.get_our_node_id(), &revoke_and_ack.0).unwrap().is_none());
- {
- let mut added_monitors = $node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
+ commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, false);
}
}
}
($node: expr, $prev_node: expr, $last_node: expr) => {
{
$node.node.handle_update_fail_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0).unwrap();
- let revoke_and_commit = $node.node.handle_commitment_signed(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().1).unwrap();
-
- {
- let mut added_monitors = $node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
- assert!($prev_node.node.handle_revoke_and_ack(&$node.node.get_our_node_id(), &revoke_and_commit.0).unwrap().is_none());
- {
- let mut added_monitors = $prev_node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
- let revoke_and_ack = $prev_node.node.handle_commitment_signed(&$node.node.get_our_node_id(), &revoke_and_commit.1.unwrap()).unwrap();
- {
- let mut added_monitors = $prev_node.chan_monitor.added_monitors.lock().unwrap();
- assert_eq!(added_monitors.len(), 1);
- added_monitors.clear();
- }
- assert!(revoke_and_ack.1.is_none());
- assert!($node.node.get_and_clear_pending_events().is_empty());
- assert!($node.node.handle_revoke_and_ack(&$prev_node.node.get_our_node_id(), &revoke_and_ack.0).unwrap().is_none());
- {
- let mut added_monitors = $node.chan_monitor.added_monitors.lock().unwrap();
- if $last_node {
- assert_eq!(added_monitors.len(), 1);
- } else {
- assert_eq!(added_monitors.len(), 2);
- assert!(added_monitors[0].0 != added_monitors[1].0);
- }
- added_monitors.clear();
- }
+ commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, !$last_node);
}
}
}
#[derive(PartialEq)]
enum HTLCType { NONE, TIMEOUT, SUCCESS }
- #[derive(PartialEq)]
- enum PenaltyType { NONE, HTLC }
- fn test_txn_broadcast(node: &Node, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, revoked_tx: Option<Transaction>, has_htlc_tx: HTLCType, has_penalty_tx: PenaltyType) -> Vec<Transaction> {
+ /// Tests that the given node has broadcast transactions for the given Channel
+ ///
+ /// First checks that the latest local commitment tx has been broadcast, unless an explicit
+ /// commitment_tx is provided, which may be used to test that a remote commitment tx was
+ /// broadcast and the revoked outputs were claimed.
+ ///
+ /// Next tests that there is (or is not) a transaction that spends the commitment transaction
+ /// that appears to be the type of HTLC transaction specified in has_htlc_tx.
+ ///
+ /// All broadcast transactions must be accounted for in one of the above three types of we'll
+ /// also fail.
+ fn test_txn_broadcast(node: &Node, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, has_htlc_tx: HTLCType) -> Vec<Transaction> {
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert!(node_txn.len() >= if has_htlc_tx == HTLCType::NONE { 0 } else { 1 } + if has_penalty_tx == PenaltyType::NONE { 0 } else { 1 });
+ assert!(node_txn.len() >= if commitment_tx.is_some() { 0 } else { 1 } + if has_htlc_tx == HTLCType::NONE { 0 } else { 1 });
let mut res = Vec::with_capacity(2);
-
- if let Some(explicit_tx) = commitment_tx {
- res.push(explicit_tx.clone());
- } else {
- for tx in node_txn.iter() {
- 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();
+ 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();
+ if commitment_tx.is_none() {
res.push(tx.clone());
}
- }
- }
- if !revoked_tx.is_some() && !(has_penalty_tx == PenaltyType::HTLC) {
- assert_eq!(res.len(), 1);
+ false
+ } else { true }
+ });
+ if let Some(explicit_tx) = commitment_tx {
+ res.push(explicit_tx.clone());
}
+ assert_eq!(res.len(), 1);
+
if has_htlc_tx != HTLCType::NONE {
- for tx in node_txn.iter() {
+ 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());
assert!(tx.lock_time == 0);
}
res.push(tx.clone());
- break;
- }
- }
+ false
+ } else { true }
+ });
assert_eq!(res.len(), 2);
}
- if has_penalty_tx == PenaltyType::HTLC {
- let revoked_tx = revoked_tx.unwrap();
- for tx in node_txn.iter() {
- 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();
- res.push(tx.clone());
- break;
- }
- }
- assert_eq!(res.len(), 1);
- }
- node_txn.clear();
+ assert!(node_txn.is_empty());
res
}
+ /// Tests that the given node has broadcast a claim transaction against the provided revoked
+ /// HTLC transaction.
+ fn test_revoked_htlc_claim_txn_broadcast(node: &Node, revoked_tx: Transaction) {
+ let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
+ 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();
+ false
+ } else { true }
+ });
+ assert!(node_txn.is_empty());
+ }
+
fn check_preimage_claim(node: &Node, prev_txn: &Vec<Transaction>) -> Vec<Transaction> {
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
// Simple case with no pending HTLCs:
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), true);
{
- let mut node_txn = test_txn_broadcast(&nodes[1], &chan_1, None, None, HTLCType::NONE, PenaltyType::NONE);
+ let mut node_txn = test_txn_broadcast(&nodes[1], &chan_1, None, HTLCType::NONE);
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![node_txn.drain(..).next().unwrap()] }, 1);
- test_txn_broadcast(&nodes[0], &chan_1, None, None, HTLCType::NONE, PenaltyType::NONE);
+ test_txn_broadcast(&nodes[0], &chan_1, None, HTLCType::NONE);
}
get_announce_close_broadcast_events(&nodes, 0, 1);
assert_eq!(nodes[0].node.list_channels().len(), 0);
// Simple case of one pending HTLC to HTLC-Timeout
nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id(), true);
{
- let mut node_txn = test_txn_broadcast(&nodes[1], &chan_2, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
+ let mut node_txn = test_txn_broadcast(&nodes[1], &chan_2, None, HTLCType::TIMEOUT);
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[2].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn.drain(..).next().unwrap()] }, 1);
- test_txn_broadcast(&nodes[2], &chan_2, None, None, HTLCType::NONE, PenaltyType::NONE);
+ test_txn_broadcast(&nodes[2], &chan_2, None, HTLCType::NONE);
}
get_announce_close_broadcast_events(&nodes, 1, 2);
assert_eq!(nodes[1].node.list_channels().len(), 0);
// HTLC-Timeout and a nodes[3] claim against it (+ its own announces)
nodes[2].node.peer_disconnected(&nodes[3].node.get_our_node_id(), true);
{
- let node_txn = test_txn_broadcast(&nodes[2], &chan_3, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
+ let node_txn = test_txn_broadcast(&nodes[2], &chan_3, None, HTLCType::TIMEOUT);
// Claim the payment on nodes[3], giving it knowledge of the preimage
claim_funds!(nodes[3], nodes[2], payment_preimage_1);
nodes[3].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
}
- let node_txn = test_txn_broadcast(&nodes[3], &chan_4, None, None, HTLCType::TIMEOUT, PenaltyType::NONE);
+ let node_txn = test_txn_broadcast(&nodes[3], &chan_4, None, HTLCType::TIMEOUT);
// Claim the payment on nodes[4], giving it knowledge of the preimage
claim_funds!(nodes[4], nodes[3], payment_preimage_2);
nodes[4].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
}
- test_txn_broadcast(&nodes[4], &chan_4, None, None, HTLCType::SUCCESS, PenaltyType::NONE);
+ test_txn_broadcast(&nodes[4], &chan_4, None, HTLCType::SUCCESS);
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[0].clone()] }, TEST_FINAL_CLTV - 5);
node_txn[0].verify(&funding_tx_map).unwrap();
node_txn.swap_remove(0);
}
- test_txn_broadcast(&nodes[1], &chan_5, None, None, HTLCType::NONE, PenaltyType::NONE);
+ test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
- let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), None, HTLCType::TIMEOUT, PenaltyType::NONE);
+ let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), HTLCType::TIMEOUT);
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
- test_txn_broadcast(&nodes[1], &chan_5, None, Some(node_txn[1].clone()), HTLCType::NONE, PenaltyType::HTLC);
+ test_revoked_htlc_claim_txn_broadcast(&nodes[1], node_txn[1].clone());
}
get_announce_close_broadcast_events(&nodes, 0, 1);
assert_eq!(nodes[0].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
+ // ignored if we cannot claim them. This originally tickled an invalid unwrap().
+ let nodes = create_network(2);
+ create_announced_chan_between_nodes(&nodes, 0, 1);
+
+ route_payment(&nodes[0], &[&nodes[1]], 10000000);
+ nodes[0].node.force_close_channel(&nodes[0].node.list_channels()[0].channel_id);
+ {
+ let events = nodes[0].node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { contents: msgs::UnsignedChannelUpdate { flags, .. }, .. } } => {
+ assert_eq!(flags & 0b10, 0b10);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+
+ let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
+ assert_eq!(node_txn.len(), 2);
+
+ 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, &[&node_txn[0], &node_txn[1]], &[1; 2]);
+
+ {
+ let events = nodes[1].node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { contents: msgs::UnsignedChannelUpdate { flags, .. }, .. } } => {
+ assert_eq!(flags & 0b10, 0b10);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+
+ // Duplicate the block_connected call since this may happen due to other listeners
+ // registering new transactions
+ 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