]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Test fallible commitment secret
authorAlec Chen <alecchendev@gmail.com>
Tue, 2 Jul 2024 01:33:25 +0000 (18:33 -0700)
committerAlec Chen <alecchendev@gmail.com>
Wed, 17 Jul 2024 17:57:51 +0000 (10:57 -0700)
lightning/src/ln/async_signer_tests.rs
lightning/src/util/test_channel_signer.rs

index c90db503aeac2988d91eee4965baadb35a51fa34..7a87f7cc9db31d10d8c671dfd6664608cc3cb52f 100644 (file)
@@ -10,6 +10,8 @@
 //! Tests for asynchronous signing. These tests verify that the channel state machine behaves
 //! properly with a signer implementation that asynchronously derives signatures.
 
+use std::collections::HashSet;
+
 use bitcoin::{Transaction, TxOut, TxIn, Amount};
 use bitcoin::blockdata::locktime::absolute::LockTime;
 use bitcoin::transaction::Version;
@@ -17,11 +19,12 @@ use bitcoin::transaction::Version;
 use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS;
 use crate::chain::ChannelMonitorUpdateStatus;
 use crate::events::bump_transaction::WalletSource;
-use crate::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
+use crate::events::{ClosureReason, Event, MessageSendEvent, MessageSendEventsProvider};
 use crate::ln::{functional_test_utils::*, msgs};
 use crate::ln::msgs::ChannelMessageHandler;
 use crate::ln::channelmanager::{PaymentId, RAACommitmentOrder, RecipientOnionFields};
 use crate::util::test_channel_signer::SignerOp;
+use crate::util::logger::Logger;
 
 #[test]
 fn test_async_commitment_signature_for_funding_created() {
@@ -127,11 +130,17 @@ fn test_async_commitment_signature_for_funding_signed() {
 
 #[test]
 fn test_async_commitment_signature_for_commitment_signed() {
-       do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(true);
-       do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(false);
+       for i in 0..=8 {
+               let enable_signer_op_order = vec![
+                       SignerOp::GetPerCommitmentPoint,
+                       SignerOp::ReleaseCommitmentSecret,
+                       SignerOp::SignCounterpartyCommitment,
+               ].into_iter().filter(|&op| i & (1 << op as u8) != 0).collect();
+               do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enable_signer_op_order);
+       }
 }
 
-fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enable_sign_counterparty_commit_first: bool) {
+fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enable_signer_op_order: Vec<SignerOp>) {
        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]);
@@ -160,31 +169,33 @@ fn do_test_async_commitment_signature_for_commitment_signed_revoke_and_ack(enabl
        // Mark dst's signer as unavailable and handle src's commitment_signed: while dst won't yet have a
        // `commitment_signed` of its own to offer, it should publish a `revoke_and_ack`.
        dst.disable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
+       dst.disable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::ReleaseCommitmentSecret);
        dst.disable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
        dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
        check_added_monitors(dst, 1);
 
-       if enable_sign_counterparty_commit_first {
-               // Unblock CS -> no messages should be sent, since we must send RAA first.
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
+       let mut enabled_signer_ops = HashSet::new();
+       log_trace!(dst.logger, "enable_signer_op_order={:?}", enable_signer_op_order);
+       for op in enable_signer_op_order {
+               enabled_signer_ops.insert(op);
+               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, op);
                dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
-               let events = dst.node.get_and_clear_pending_msg_events();
-               assert!(events.is_empty(), "expected no message, got {}", events.len());
 
-               // Unblock revoke_and_ack -> we should send both RAA + CS.
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
-               dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
-               get_revoke_commit_msgs(&dst, &src.node.get_our_node_id());
-       } else {
-               // Unblock revoke_and_ack -> we should send just RAA.
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
-               dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
-               get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
-
-               // Unblock commitment signed -> we should send CS.
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::SignCounterpartyCommitment);
-               dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
-               get_htlc_update_msgs(dst, &src.node.get_our_node_id());
+               if enabled_signer_ops.contains(&SignerOp::GetPerCommitmentPoint) && enabled_signer_ops.contains(&SignerOp::ReleaseCommitmentSecret) {
+                       // We are just able to send revoke_and_ack
+                       if op == SignerOp::GetPerCommitmentPoint || op == SignerOp::ReleaseCommitmentSecret {
+                               get_event_msg!(dst, MessageSendEvent::SendRevokeAndACK, src.node.get_our_node_id());
+                       }
+                       // We either just sent or previously sent revoke_and_ack
+                       // and now we are able to send commitment_signed
+                       if op == SignerOp::SignCounterpartyCommitment {
+                               get_htlc_update_msgs(dst, &src.node.get_our_node_id());
+                       }
+               } else {
+                       // We can't send either message until RAA is unblocked
+                       let events = dst.node.get_and_clear_pending_msg_events();
+                       assert!(events.is_empty(), "expected no message, got {}", events.len());
+               }
        }
 }
 
@@ -296,12 +307,22 @@ enum UnblockSignerAcrossDisconnectCase {
 
 #[test]
 fn test_async_raa_peer_disconnect() {
-       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::AtEnd);
-       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeMonitorRestored);
-       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeReestablish);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::AtEnd, true);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::AtEnd, false);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeMonitorRestored, true);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeMonitorRestored, false);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeReestablish, true);
+       do_test_async_raa_peer_disconnect(UnblockSignerAcrossDisconnectCase::BeforeReestablish, false);
 }
 
-fn do_test_async_raa_peer_disconnect(test_case: UnblockSignerAcrossDisconnectCase) {
+fn do_test_async_raa_peer_disconnect(test_case: UnblockSignerAcrossDisconnectCase, raa_blocked_by_commit_point: bool) {
+       // `raa_blocked_by_commit_point` determines whether we block the RAA by blocking the
+       // signer on `GetPerCommitmentPoint` or `ReleaseCommitmentSecret`.
+       let block_raa_signer_op = if raa_blocked_by_commit_point {
+               SignerOp::GetPerCommitmentPoint
+       } else {
+               SignerOp::ReleaseCommitmentSecret
+       };
        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]);
@@ -334,7 +355,7 @@ fn do_test_async_raa_peer_disconnect(test_case: UnblockSignerAcrossDisconnectCas
 
        // Mark dst's signer as unavailable and handle src's commitment_signed: while dst won't yet have a
        // `commitment_signed` of its own to offer, it should publish a `revoke_and_ack`.
-       dst.disable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
+       dst.disable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, block_raa_signer_op);
        dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
        check_added_monitors(dst, 1);
 
@@ -359,13 +380,13 @@ fn do_test_async_raa_peer_disconnect(test_case: UnblockSignerAcrossDisconnectCas
 
        if test_case == UnblockSignerAcrossDisconnectCase::BeforeReestablish {
                // Reenable the signer before the reestablish.
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
+               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, block_raa_signer_op);
        }
 
        dst.node.handle_channel_reestablish(&src.node.get_our_node_id(), &reestablish_1[0]);
 
        if test_case == UnblockSignerAcrossDisconnectCase::BeforeMonitorRestored {
-               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
+               dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, block_raa_signer_op);
                chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::Completed);
                let (outpoint, latest_update, _) = dst.chain_monitor.latest_monitor_update_id.lock().unwrap().get(&chan_id).unwrap().clone();
                dst.chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
@@ -384,7 +405,7 @@ fn do_test_async_raa_peer_disconnect(test_case: UnblockSignerAcrossDisconnectCas
        }
 
        // Mark dst's signer as available and retry: we now expect to see dst's RAA + CS.
-       dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, SignerOp::GetPerCommitmentPoint);
+       dst.enable_channel_signer_op(&src.node.get_our_node_id(), &chan_id, block_raa_signer_op);
        dst.node.signer_unblocked(Some((src.node.get_our_node_id(), chan_id)));
 
        if test_case == UnblockSignerAcrossDisconnectCase::AtEnd {
index fb20528f9eebbe4eb90d295184c16191ed5f9c4e..0f8eb44f74aa410ce0578c9b30cb1a1d0b19e8cd 100644 (file)
@@ -173,6 +173,10 @@ impl ChannelSigner for TestChannelSigner {
        }
 
        fn release_commitment_secret(&self, idx: u64) -> Result<[u8; 32], ()> {
+               #[cfg(test)]
+               if !self.is_signer_available(SignerOp::ReleaseCommitmentSecret) {
+                       return Err(());
+               }
                {
                        let mut state = self.state.lock().unwrap();
                        assert!(idx == state.last_holder_revoked_commitment || idx == state.last_holder_revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, last revoked {}", idx, state.last_holder_revoked_commitment);