BOLT2: Check we don't send and accept 0-msat HTLC
[rust-lightning] / lightning / src / ln / functional_tests.rs
index 74342589d674b65b0c65b6e58b69e2806f98239d..4dc7e5e1219816d14114ced2f7dbd6b2768def75 100644 (file)
@@ -2360,16 +2360,9 @@ fn claim_htlc_outputs_single_tx() {
                assert_eq!(node_txn[2].input.len(), 1);
                assert_eq!(node_txn[3].input.len(), 1);
                assert_eq!(node_txn[4].input.len(), 1);
-               fn get_txout(out_point: &BitcoinOutPoint, tx: &Transaction) -> Option<TxOut> {
-                       if out_point.txid == tx.txid() {
-                               tx.output.get(out_point.vout as usize).cloned()
-                       } else {
-                               None
-                       }
-               }
-               node_txn[2].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
-               node_txn[3].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
-               node_txn[4].verify(|out|get_txout(out, &revoked_local_txn[0])).unwrap();
+               check_spends!(node_txn[2], revoked_local_txn[0]);
+               check_spends!(node_txn[3], revoked_local_txn[0]);
+               check_spends!(node_txn[4], revoked_local_txn[0]);
 
                let mut witness_lens = BTreeSet::new();
                witness_lens.insert(node_txn[2].input[0].witness.last().unwrap().len());
@@ -5493,7 +5486,6 @@ fn bolt2_open_channel_sending_node_checks_part2() {
 
 #[test]
 fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() {
-       //BOLT2 Requirement: MUST offer amount_msat greater than 0.
        //BOLT2 Requirement: MUST NOT offer amount_msat below the receiving node's htlc_minimum_msat (same validation check catches both of these)
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@ -5503,7 +5495,7 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() {
        let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
        let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
 
-       route.hops[0].fee_msat = 0;
+       route.hops[0].fee_msat = 100;
 
        let err = nodes[0].node.send_payment(route, our_payment_hash);
 
@@ -5516,6 +5508,30 @@ fn test_update_add_htlc_bolt2_sender_value_below_minimum_msat() {
        nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send less than their minimum HTLC value".to_string(), 1);
 }
 
+#[test]
+fn test_update_add_htlc_bolt2_sender_zero_value_msat() {
+       //BOLT2 Requirement: MUST offer amount_msat greater than 0.
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+       let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::supported(), InitFeatures::supported());
+       let mut route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), None, &[], 100000, TEST_FINAL_CLTV).unwrap();
+       let (_, our_payment_hash) = get_payment_preimage_hash!(nodes[0]);
+
+       route.hops[0].fee_msat = 0;
+
+       let err = nodes[0].node.send_payment(route, our_payment_hash);
+
+       if let Err(APIError::ChannelUnavailable{err}) = err {
+               assert_eq!(err, "Cannot send 0-msat HTLC");
+       } else {
+               assert!(false);
+       }
+       assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+       nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot send 0-msat HTLC".to_string(), 1);
+}
+
 #[test]
 fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() {
        //BOLT 2 Requirement: MUST set cltv_expiry less than 500000000.
@@ -6924,13 +6940,7 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
                // Verify claim tx are spending revoked HTLC txn
                assert_eq!(node_txn[4].input.len(), 2);
                assert_eq!(node_txn[4].output.len(), 1);
-               if node_txn[4].input[0].previous_output.txid == revoked_htlc_txn[0].txid() {
-                       assert_eq!(node_txn[4].input[1].previous_output.txid, revoked_htlc_txn[1].txid());
-               } else if node_txn[4].input[0].previous_output.txid == revoked_htlc_txn[1].txid() {
-                       assert_eq!(node_txn[4].input[1].previous_output.txid, revoked_htlc_txn[0].txid());
-               } else {
-                       panic!();
-               }
+               check_spends!(node_txn[4], revoked_htlc_txn[0], revoked_htlc_txn[1]);
                first = node_txn[4].txid();
                // Store both feerates for later comparison
                let fee_1 = revoked_htlc_txn[0].output[0].value + revoked_htlc_txn[1].output[0].value - node_txn[4].output[0].value;
@@ -6954,24 +6964,14 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
 
        // Few more blocks to confirm penalty txn
        let header_135 = connect_blocks(&nodes[0].block_notifier, 5, 130, true, header_130.bitcoin_hash());
-       {
-               let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
-               assert_eq!(node_txn.len(), 0);
-               node_txn.clear();
-       }
+       assert!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().is_empty());
        let header_144 = connect_blocks(&nodes[0].block_notifier, 9, 135, true, header_135);
        let node_txn = {
                let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
                assert_eq!(node_txn.len(), 1);
 
                assert_eq!(node_txn[0].input.len(), 2);
-               if node_txn[0].input[0].previous_output.txid == revoked_htlc_txn[0].txid() {
-                       assert_eq!(node_txn[0].input[1].previous_output.txid, revoked_htlc_txn[1].txid());
-               } else if node_txn[0].input[0].previous_output.txid == revoked_htlc_txn[1].txid() {
-                       assert_eq!(node_txn[0].input[1].previous_output.txid, revoked_htlc_txn[0].txid());
-               } else {
-                       panic!();
-               }
+               check_spends!(node_txn[0], revoked_htlc_txn[0], revoked_htlc_txn[1]);
                //// Verify bumped tx is different and 25% bump heuristic
                assert_ne!(first, node_txn[0].txid());
                let fee_2 = revoked_htlc_txn[0].output[0].value + revoked_htlc_txn[1].output[0].value - node_txn[0].output[0].value;