]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Test async sign_closing_transaction
authorAlec Chen <alecchendev@gmail.com>
Tue, 2 Jul 2024 06:08:04 +0000 (23:08 -0700)
committerAlec Chen <alecchendev@gmail.com>
Fri, 23 Aug 2024 18:02:05 +0000 (11:02 -0700)
lightning/src/ln/async_signer_tests.rs
lightning/src/util/test_channel_signer.rs

index 06fd39867606ea6607d8eeeb950208694dd6967c..682baa7fde9f4e44b76b7d965635250886f3556b 100644 (file)
@@ -11,7 +11,7 @@
 //! properly with a signer implementation that asynchronously derives signatures.
 
 use std::collections::HashSet;
-
+use bitcoin::key::Secp256k1;
 use bitcoin::{Transaction, TxOut, TxIn, Amount};
 use bitcoin::locktime::absolute::LockTime;
 use bitcoin::transaction::Version;
@@ -20,9 +20,13 @@ 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};
-use crate::ln::{functional_test_utils::*, msgs};
-use crate::ln::msgs::ChannelMessageHandler;
+use crate::ln::chan_utils::ClosingTransaction;
+use crate::ln::channel_state::{ChannelDetails, ChannelShutdownState};
 use crate::ln::channelmanager::{PaymentId, RAACommitmentOrder, RecipientOnionFields};
+use crate::ln::msgs::ChannelMessageHandler;
+use crate::ln::{functional_test_utils::*, msgs};
+use crate::sign::ecdsa::EcdsaChannelSigner;
+use crate::sign::SignerProvider;
 use crate::util::test_channel_signer::SignerOp;
 use crate::util::logger::Logger;
 
@@ -847,3 +851,105 @@ fn test_async_holder_signatures_anchors() {
 fn test_async_holder_signatures_remote_commitment_anchors() {
        do_test_async_holder_signatures(true, true);
 }
+
+#[test]
+fn test_closing_signed() {
+       do_test_closing_signed(false);
+       do_test_closing_signed(true);
+}
+
+fn do_test_closing_signed(extra_closing_signed: bool) {
+       // Based off of `expect_channel_shutdown_state`.
+       // Test that we can asynchronously sign closing transactions.
+       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 nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+       let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
+
+       expect_channel_shutdown_state!(nodes[0], chan_1.2, ChannelShutdownState::NotShuttingDown);
+
+       nodes[0].node.close_channel(&chan_1.2, &nodes[1].node.get_our_node_id()).unwrap();
+
+       expect_channel_shutdown_state!(nodes[0], chan_1.2, ChannelShutdownState::ShutdownInitiated);
+       expect_channel_shutdown_state!(nodes[1], chan_1.2, ChannelShutdownState::NotShuttingDown);
+
+       let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &node_0_shutdown);
+
+       expect_channel_shutdown_state!(nodes[0], chan_1.2, ChannelShutdownState::ShutdownInitiated);
+       expect_channel_shutdown_state!(nodes[1], chan_1.2, ChannelShutdownState::NegotiatingClosingFee);
+
+       let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
+       nodes[0].disable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+       nodes[0].node.handle_shutdown(&nodes[1].node.get_our_node_id(), &node_1_shutdown);
+
+       expect_channel_shutdown_state!(nodes[0], chan_1.2, ChannelShutdownState::NegotiatingClosingFee);
+       expect_channel_shutdown_state!(nodes[1], chan_1.2, ChannelShutdownState::NegotiatingClosingFee);
+
+       let events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert!(events.is_empty(), "Expected no events, got {:?}", events);
+       nodes[0].enable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+       nodes[0].node.signer_unblocked(None);
+
+       let node_0_closing_signed = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id());
+       nodes[1].disable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+       nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_closing_signed);
+
+       let events = nodes[1].node.get_and_clear_pending_msg_events();
+       assert!(events.is_empty(), "Expected no events, got {:?}", events);
+       nodes[1].enable_channel_signer_op(&nodes[0].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+       nodes[1].node.signer_unblocked(None);
+
+       let node_1_closing_signed = get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id());
+
+       nodes[0].disable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+       nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed);
+       let events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert!(events.is_empty(), "Expected no events, got {:?}", events);
+       nodes[0].enable_channel_signer_op(&nodes[1].node.get_our_node_id(), &chan_1.2, SignerOp::SignClosingTransaction);
+
+       if extra_closing_signed {
+               let node_1_closing_signed_2_bad = {
+                       let mut node_1_closing_signed_2 = node_1_closing_signed.clone();
+                       let holder_script = nodes[0].keys_manager.get_shutdown_scriptpubkey().unwrap();
+                       let counterparty_script = nodes[1].keys_manager.get_shutdown_scriptpubkey().unwrap();
+                       let funding_outpoint = bitcoin::OutPoint { txid: chan_1.3.txid(), vout: 0 };
+                       let closing_tx_2 = ClosingTransaction::new(50000, 0, holder_script.into(),
+                               counterparty_script.into(), funding_outpoint);
+
+                       let per_peer_state = nodes[1].node.per_peer_state.read().unwrap();
+                       let mut chan_lock = per_peer_state.get(&nodes[0].node.get_our_node_id()).unwrap().lock().unwrap();
+                       let chan = chan_lock.channel_by_id.get_mut(&chan_1.2).map(|phase| phase.context_mut()).unwrap();
+
+                       let signer = chan.get_mut_signer().as_mut_ecdsa().unwrap();
+                       let signature = signer.sign_closing_transaction(&closing_tx_2, &Secp256k1::new()).unwrap();
+                       node_1_closing_signed_2.signature = signature;
+                       node_1_closing_signed_2
+               };
+               nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed_2_bad);
+
+               let events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       MessageSendEvent::HandleError {
+                               action: msgs::ErrorAction::SendWarningMessage { .. }, ref node_id
+                       } => {
+                               assert_eq!(node_id, &nodes[1].node.get_our_node_id());
+                       },
+                       _ => panic!("Unexpected event: {:?}", events[0]),
+               };
+       }
+
+       nodes[0].node.signer_unblocked(None);
+       let (_, node_0_2nd_closing_signed) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
+
+       nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed.unwrap());
+       let (_, node_1_closing_signed) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
+       assert!(node_1_closing_signed.is_none());
+
+       assert!(nodes[0].node.list_channels().is_empty());
+       assert!(nodes[1].node.list_channels().is_empty());
+       check_closed_event!(nodes[0], 1, ClosureReason::LocallyInitiatedCooperativeClosure, [nodes[1].node.get_our_node_id()], 100000);
+       check_closed_event!(nodes[1], 1, ClosureReason::CounterpartyInitiatedCooperativeClosure, [nodes[0].node.get_our_node_id()], 100000);
+}
index c62b81f307d4dfc5792799c23ae32f12d828a535..e6e665c21762136d87c0c83934bf8e842a735504 100644 (file)
@@ -325,6 +325,10 @@ impl EcdsaChannelSigner for TestChannelSigner {
        }
 
        fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+               #[cfg(test)]
+               if !self.is_signer_available(SignerOp::SignClosingTransaction) {
+                       return Err(());
+               }
                closing_tx.verify(self.inner.funding_outpoint().unwrap().into_bitcoin_outpoint())
                        .expect("derived different closing transaction");
                Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())