Check for timing-out HTLCs in remote unrevoked commitments
[rust-lightning] / src / ln / functional_tests.rs
index ca394fcdf98fda96379630cb2d6c9024e4739f05..de2b42d3fa372e32f1aa5fdf64f051b84a9acb01 100644 (file)
@@ -6084,10 +6084,107 @@ fn do_htlc_claim_local_commitment_only(use_dust: bool) {
        check_closed_broadcast!(nodes[1]);
 }
 
+fn do_htlc_claim_current_remote_commitment_only(use_dust: bool) {
+       let mut nodes = create_network(2);
+       let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+       let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &Vec::new(), if use_dust { 50000 } else { 3000000 }, TEST_FINAL_CLTV).unwrap();
+       let (_, payment_hash) = get_payment_preimage_hash!(nodes[0]);
+       nodes[0].node.send_payment(route, payment_hash).unwrap();
+       check_added_monitors!(nodes[0], 1);
+
+       let _as_update = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+
+       // As far as A is concerened, the HTLC is now present only in the latest remote commitment
+       // transaction, however it is not in A's latest local commitment, so we can just broadcast that
+       // to "time out" the HTLC.
+
+       let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+       for i in 1..TEST_FINAL_CLTV + HTLC_FAIL_TIMEOUT_BLOCKS + CHAN_CONFIRM_DEPTH + 1 {
+               nodes[0].chain_monitor.block_connected_checked(&header, i, &Vec::new(), &Vec::new());
+               header.prev_blockhash = header.bitcoin_hash();
+       }
+       test_txn_broadcast(&nodes[0], &chan, None, HTLCType::NONE);
+       check_closed_broadcast!(nodes[0]);
+}
+
+fn do_htlc_claim_previous_remote_commitment_only(use_dust: bool, check_revoke_no_close: bool) {
+       let nodes = create_network(3);
+       let chan = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+       // Fail the payment, but don't deliver A's final RAA, resulting in the HTLC only being present
+       // in B's previous (unrevoked) commitment transaction, but none of A's commitment transactions.
+       // Also optionally test that we *don't* fail the channel in case the commitment transaction was
+       // actually revoked.
+       let htlc_value = if use_dust { 50000 } else { 3000000 };
+       let (_, our_payment_hash) = route_payment(&nodes[0], &[&nodes[1]], htlc_value);
+       assert!(nodes[1].node.fail_htlc_backwards(&our_payment_hash, htlc_value));
+       expect_pending_htlcs_forwardable!(nodes[1]);
+       check_added_monitors!(nodes[1], 1);
+
+       let bs_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fail_htlcs[0]).unwrap();
+       nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bs_updates.commitment_signed).unwrap();
+       check_added_monitors!(nodes[0], 1);
+       let as_updates = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_updates.0).unwrap();
+       check_added_monitors!(nodes[1], 1);
+       nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &as_updates.1).unwrap();
+       check_added_monitors!(nodes[1], 1);
+       let bs_revoke_and_ack = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
+
+       if check_revoke_no_close {
+               nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_revoke_and_ack).unwrap();
+               check_added_monitors!(nodes[0], 1);
+       }
+
+       let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+       for i in 1..TEST_FINAL_CLTV + HTLC_FAIL_TIMEOUT_BLOCKS + CHAN_CONFIRM_DEPTH + 1 {
+               nodes[0].chain_monitor.block_connected_checked(&header, i, &Vec::new(), &Vec::new());
+               header.prev_blockhash = header.bitcoin_hash();
+       }
+       if !check_revoke_no_close {
+               test_txn_broadcast(&nodes[0], &chan, None, HTLCType::NONE);
+               check_closed_broadcast!(nodes[0]);
+       } else {
+               let events = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
+                               assert_eq!(payment_hash, our_payment_hash);
+                               assert!(rejected_by_dest);
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+       }
+}
+
+// Test that we close channels on-chain when broadcastable HTLCs reach their timeout window.
+// There are only a few cases to test here:
+//  * its not really normative behavior, but we test that below-dust HTLCs "included" in
+//    broadcastable commitment transactions result in channel closure,
+//  * its included in an unrevoked-but-previous remote commitment transaction,
+//  * its included in the latest remote or local commitment transactions.
+// We test each of the three possible commitment transactions individually and use both dust and
+// non-dust HTLCs.
+// Note that we don't bother testing both outbound and inbound HTLC failures for each case, and we
+// assume they are handled the same across all six cases, as both outbound and inbound failures are
+// tested for at least one of the cases in other tests.
 #[test]
-fn htlc_claim_local_commitment_only() {
+fn htlc_claim_single_commitment_only_a() {
        do_htlc_claim_local_commitment_only(true);
        do_htlc_claim_local_commitment_only(false);
+
+       do_htlc_claim_current_remote_commitment_only(true);
+       do_htlc_claim_current_remote_commitment_only(false);
+}
+
+#[test]
+fn htlc_claim_single_commitment_only_b() {
+       do_htlc_claim_previous_remote_commitment_only(true, false);
+       do_htlc_claim_previous_remote_commitment_only(false, false);
+       do_htlc_claim_previous_remote_commitment_only(true, true);
+       do_htlc_claim_previous_remote_commitment_only(false, true);
 }
 
 fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)