Add support for retrieving and suspending the channel signer
authorChris Waterson <waterson@gmail.com>
Wed, 6 Sep 2023 18:38:34 +0000 (11:38 -0700)
committerChris Waterson <waterson@gmail.com>
Wed, 25 Oct 2023 16:26:32 +0000 (09:26 -0700)
This adds a `get_signer` method to the context so that a test can get ahold of
the channel signer. Adds a `set_available` method on the `TestChannelSigner`
to allow a test to enable and disable the signer: when disabled some of the
signer's methods will return `Err` which will typically activate the error
handling case. Adds a `set_channel_signer_available` function on the test
`Node` class to make it easy to enable and disable a specific signer.

Fix fuzz test

fuzz/src/chanmon_consistency.rs
lightning/src/ln/channel.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/util/test_channel_signer.rs

index dddd97cae4554c8f22a09af98bf5d1098ea596fa..ea0b78dc8ae77096cb1d1e21f30f05512915937c 100644 (file)
@@ -267,6 +267,7 @@ impl SignerProvider for KeyProvider {
                        inner,
                        state,
                        disable_revocation_policy_check: false,
+                       available: Arc::new(Mutex::new(true)),
                })
        }
 
index 9c984c8e2f217c211502ff7e67afa7d4a4255c39..ceee6ca3b573ae26ffc80008808f263e20a206a1 100644 (file)
@@ -1077,6 +1077,12 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                self.outbound_scid_alias
        }
 
+       /// Returns the holder signer for this channel.
+       #[cfg(test)]
+       pub fn get_signer(&self) -> &ChannelSignerType<<SP::Target as SignerProvider>::Signer> {
+               return &self.holder_signer
+       }
+
        /// Only allowed immediately after deserialization if get_outbound_scid_alias returns 0,
        /// indicating we were written by LDK prior to 0.0.106 which did not set outbound SCID aliases
        /// or prior to any channel actions during `Channel` initialization.
index 2992f207d8be80cc625217e22e0553539f4696a2..b35c49321cef7501bfbb5c16645a7a18f5676169 100644 (file)
@@ -30,6 +30,8 @@ use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysI
 use crate::util::errors::APIError;
 use crate::util::config::{UserConfig, MaxDustHTLCExposure};
 use crate::util::ser::{ReadableArgs, Writeable};
+#[cfg(test)]
+use crate::util::logger::Logger;
 
 use bitcoin::blockdata::block::{Block, BlockHeader};
 use bitcoin::blockdata::transaction::{Transaction, TxOut};
@@ -436,6 +438,25 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
        pub fn get_block_header(&self, height: u32) -> BlockHeader {
                self.blocks.lock().unwrap()[height as usize].0.header
        }
+       /// Changes the channel signer's availability for the specified peer and channel.
+       ///
+       /// When `available` is set to `true`, the channel signer will behave normally. When set to
+       /// `false`, the channel signer will act like an off-line remote signer and will return `Err` for
+       /// several of the signing methods. Currently, only `get_per_commitment_point` and
+       /// `release_commitment_secret` are affected by this setting.
+       #[cfg(test)]
+       pub fn set_channel_signer_available(&self, peer_id: &PublicKey, chan_id: &ChannelId, available: bool) {
+               let per_peer_state = self.node.per_peer_state.read().unwrap();
+               let chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap();
+               let signer = (|| {
+                       match chan_lock.channel_by_id.get(chan_id) {
+                               Some(phase) => phase.context().get_signer(),
+                               None => panic!("Couldn't find a channel with id {}", chan_id),
+                       }
+               })();
+               log_debug!(self.logger, "Setting channel signer for {} as available={}", chan_id, available);
+               signer.as_ecdsa().unwrap().set_available(available);
+       }
 }
 
 /// If we need an unsafe pointer to a `Node` (ie to reference it in a thread
index 942671cf46b5a328b76295d9c400ad6bb6e342ab..ab576fee90de6c9920ad1e6836b3e9b207bec92b 100644 (file)
@@ -56,6 +56,9 @@ pub struct TestChannelSigner {
        /// Channel state used for policy enforcement
        pub state: Arc<Mutex<EnforcementState>>,
        pub disable_revocation_policy_check: bool,
+       /// When `true` (the default), the signer will respond immediately with signatures. When `false`,
+       /// the signer will return an error indicating that it is unavailable.
+       pub available: Arc<Mutex<bool>>,
 }
 
 impl PartialEq for TestChannelSigner {
@@ -71,7 +74,8 @@ impl TestChannelSigner {
                Self {
                        inner,
                        state,
-                       disable_revocation_policy_check: false
+                       disable_revocation_policy_check: false,
+                       available: Arc::new(Mutex::new(true)),
                }
        }
 
@@ -84,7 +88,8 @@ impl TestChannelSigner {
                Self {
                        inner,
                        state,
-                       disable_revocation_policy_check
+                       disable_revocation_policy_check,
+                       available: Arc::new(Mutex::new(true)),
                }
        }
 
@@ -94,6 +99,16 @@ impl TestChannelSigner {
        pub fn get_enforcement_state(&self) -> MutexGuard<EnforcementState> {
                self.state.lock().unwrap()
        }
+
+       /// Marks the signer's availability.
+       ///
+       /// When `true`, methods are forwarded to the underlying signer as normal. When `false`, some
+       /// methods will return `Err` indicating that the signer is unavailable. Intended to be used for
+       /// testing asynchronous signing.
+       #[cfg(test)]
+       pub fn set_available(&self, available: bool) {
+               *self.available.lock().unwrap() = available;
+       }
 }
 
 impl ChannelSigner for TestChannelSigner {
@@ -133,6 +148,9 @@ impl EcdsaChannelSigner for TestChannelSigner {
                self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx);
 
                {
+                       if !*self.available.lock().unwrap() {
+                               return Err(());
+                       }
                        let mut state = self.state.lock().unwrap();
                        let actual_commitment_number = commitment_tx.commitment_number();
                        let last_commitment_number = state.last_counterparty_commitment;
@@ -149,6 +167,9 @@ impl EcdsaChannelSigner for TestChannelSigner {
        }
 
        fn validate_counterparty_revocation(&self, idx: u64, _secret: &SecretKey) -> Result<(), ()> {
+               if !*self.available.lock().unwrap() {
+                       return Err(());
+               }
                let mut state = self.state.lock().unwrap();
                assert!(idx == state.last_counterparty_revoked_commitment || idx == state.last_counterparty_revoked_commitment - 1, "expecting to validate the current or next counterparty revocation - trying {}, current {}", idx, state.last_counterparty_revoked_commitment);
                state.last_counterparty_revoked_commitment = idx;
@@ -156,6 +177,9 @@ impl EcdsaChannelSigner for TestChannelSigner {
        }
 
        fn sign_holder_commitment(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+               if !*self.available.lock().unwrap() {
+                       return Err(());
+               }
                let trusted_tx = self.verify_holder_commitment_tx(commitment_tx, secp_ctx);
                let state = self.state.lock().unwrap();
                let commitment_number = trusted_tx.commitment_number();