From 5f3a7f2e4b96082b3c151e64c746849ec1d30e02 Mon Sep 17 00:00:00 2001 From: Chris Waterson Date: Wed, 6 Sep 2023 11:38:34 -0700 Subject: [PATCH] Add support for retrieving and suspending the channel signer 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 | 1 + lightning/src/ln/channel.rs | 6 +++++ lightning/src/ln/functional_test_utils.rs | 21 +++++++++++++++++ lightning/src/util/test_channel_signer.rs | 28 +++++++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index dddd97cae..ea0b78dc8 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -267,6 +267,7 @@ impl SignerProvider for KeyProvider { inner, state, disable_revocation_policy_check: false, + available: Arc::new(Mutex::new(true)), }) } diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 9c984c8e2..ceee6ca3b 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -1077,6 +1077,12 @@ impl ChannelContext where SP::Target: SignerProvider { self.outbound_scid_alias } + /// Returns the holder signer for this channel. + #[cfg(test)] + pub fn get_signer(&self) -> &ChannelSignerType<::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. diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index 2992f207d..b35c49321 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -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 diff --git a/lightning/src/util/test_channel_signer.rs b/lightning/src/util/test_channel_signer.rs index 942671cf4..ab576fee9 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -56,6 +56,9 @@ pub struct TestChannelSigner { /// Channel state used for policy enforcement pub state: Arc>, 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>, } 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 { 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) -> Result { + 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(); -- 2.39.5