From e8939e26bc8fdf90a2f7b8aec38e5171a54764b4 Mon Sep 17 00:00:00 2001 From: Alec Chen Date: Mon, 10 Jun 2024 15:01:11 -0700 Subject: [PATCH] Allow toggling specific signing methods in test channel signer --- fuzz/src/chanmon_consistency.rs | 7 +-- lightning/src/chain/channelmonitor.rs | 6 ++ lightning/src/ln/channel.rs | 6 ++ lightning/src/ln/functional_test_utils.rs | 71 +++++++++++++++++++++++ lightning/src/util/test_channel_signer.rs | 56 +++++++++++++++++- lightning/src/util/test_utils.rs | 11 +++- 6 files changed, 149 insertions(+), 8 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index c4d179e74..423e69eb8 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -396,12 +396,7 @@ impl SignerProvider for KeyProvider { let inner: InMemorySigner = ReadableArgs::read(&mut reader, self)?; let state = self.make_enforcement_state_cell(inner.commitment_seed); - Ok(TestChannelSigner { - inner, - state, - disable_revocation_policy_check: false, - available: Arc::new(Mutex::new(true)), - }) + Ok(TestChannelSigner::new_with_revoked(inner, state, false)) } fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result { diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index cd6061a20..5cc8030a6 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -1928,6 +1928,12 @@ impl ChannelMonitor { let inner = self.inner.lock().unwrap(); f(&inner.onchain_tx_handler.signer); } + + #[cfg(test)] + pub fn do_mut_signer_call ()>(&self, mut f: F) { + let mut inner = self.inner.lock().unwrap(); + f(&mut inner.onchain_tx_handler.signer); + } } impl ChannelMonitorImpl { diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 57fbe5162..b6004debd 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2122,6 +2122,12 @@ impl ChannelContext where SP::Target: SignerProvider { return &self.holder_signer } + /// Returns the holder signer for this channel. + #[cfg(test)] + pub fn get_mut_signer(&mut self) -> &mut ChannelSignerType { + return &mut 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 7e57f2059..d46588b10 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -31,6 +31,8 @@ use crate::util::errors::APIError; use crate::util::logger::Logger; use crate::util::scid_utils; use crate::util::test_channel_signer::TestChannelSigner; +#[cfg(test)] +use crate::util::test_channel_signer::SignerOp; use crate::util::test_utils; use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface}; use crate::util::ser::{ReadableArgs, Writeable}; @@ -523,6 +525,75 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> { .insert(channel_keys_id.unwrap()); } } + + /// Toggles this node's signer to be available for the given signer operation. + /// This is useful for testing behavior for restoring an async signer that previously + /// could not return a signature immediately. + #[cfg(test)] + pub fn enable_channel_signer_op(&self, peer_id: &PublicKey, chan_id: &ChannelId, signer_op: SignerOp) { + self.set_channel_signer_ops(peer_id, chan_id, signer_op, true); + } + + /// Toggles this node's signer to be unavailable, returning `Err` for the given signer operation. + /// This is useful for testing behavior for an async signer that cannot return a signature + /// immediately. + #[cfg(test)] + pub fn disable_channel_signer_op(&self, peer_id: &PublicKey, chan_id: &ChannelId, signer_op: SignerOp) { + self.set_channel_signer_ops(peer_id, chan_id, signer_op, false); + } + + /// Changes the channel signer's availability for the specified peer, channel, and signer + /// operation. + /// + /// For the specified signer operation, when `available` is set to `true`, the channel signer + /// will behave normally, returning `Ok`. When set to `false`, and the channel signer will + /// act like an off-line remote signer, returning `Err`. This applies to the signer in all + /// relevant places, i.e. the channel manager, chain monitor, and the keys manager. + #[cfg(test)] + fn set_channel_signer_ops(&self, peer_id: &PublicKey, chan_id: &ChannelId, signer_op: SignerOp, available: bool) { + use crate::sign::ChannelSigner; + log_debug!(self.logger, "Setting channel signer for {} as available={}", chan_id, available); + + let per_peer_state = self.node.per_peer_state.read().unwrap(); + let mut chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap(); + + let mut channel_keys_id = None; + if let Some(chan) = chan_lock.channel_by_id.get_mut(chan_id).map(|phase| phase.context_mut()) { + let signer = chan.get_mut_signer().as_mut_ecdsa().unwrap(); + if available { + signer.enable_op(signer_op); + } else { + signer.disable_op(signer_op); + } + channel_keys_id = Some(chan.channel_keys_id); + } + + let monitor = self.chain_monitor.chain_monitor.list_monitors().into_iter() + .find(|(_, channel_id)| *channel_id == *chan_id) + .and_then(|(funding_txo, _)| self.chain_monitor.chain_monitor.get_monitor(funding_txo).ok()); + if let Some(monitor) = monitor { + monitor.do_mut_signer_call(|signer| { + channel_keys_id = channel_keys_id.or(Some(signer.inner.channel_keys_id())); + if available { + signer.enable_op(signer_op); + } else { + signer.disable_op(signer_op); + } + }); + } + + let channel_keys_id = channel_keys_id.unwrap(); + let mut unavailable_signers_ops = self.keys_manager.unavailable_signers_ops.lock().unwrap(); + let entry = unavailable_signers_ops.entry(channel_keys_id).or_insert(new_hash_set()); + if available { + entry.remove(&signer_op); + if entry.is_empty() { + unavailable_signers_ops.remove(&channel_keys_id); + } + } else { + entry.insert(signer_op); + }; + } } /// 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 2009e4d0b..2ee6a3a68 100644 --- a/lightning/src/util/test_channel_signer.rs +++ b/lightning/src/util/test_channel_signer.rs @@ -18,7 +18,7 @@ use crate::sign::ecdsa::EcdsaChannelSigner; #[allow(unused_imports)] use crate::prelude::*; -use core::cmp; +use core::{cmp, fmt}; use crate::sync::{Mutex, Arc}; #[cfg(test)] use crate::sync::MutexGuard; @@ -74,6 +74,46 @@ pub struct TestChannelSigner { /// 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>, + /// Set of signer operations that are disabled. If an operation is disabled, + /// the signer will return `Err` when the corresponding method is called. + pub disabled_signer_ops: Arc>>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SignerOp { + GetPerCommitmentPoint, + ReleaseCommitmentSecret, + ValidateHolderCommitment, + SignCounterpartyCommitment, + ValidateCounterpartyRevocation, + SignHolderCommitment, + SignJusticeRevokedOutput, + SignJusticeRevokedHtlc, + SignHolderHtlcTransaction, + SignCounterpartyHtlcTransaction, + SignClosingTransaction, + SignHolderAnchorInput, + SignChannelAnnouncementWithFundingKey, +} + +impl fmt::Display for SignerOp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SignerOp::GetPerCommitmentPoint => write!(f, "get_per_commitment_point"), + SignerOp::ReleaseCommitmentSecret => write!(f, "release_commitment_secret"), + SignerOp::ValidateHolderCommitment => write!(f, "validate_holder_commitment"), + SignerOp::SignCounterpartyCommitment => write!(f, "sign_counterparty_commitment"), + SignerOp::ValidateCounterpartyRevocation => write!(f, "validate_counterparty_revocation"), + SignerOp::SignHolderCommitment => write!(f, "sign_holder_commitment"), + SignerOp::SignJusticeRevokedOutput => write!(f, "sign_justice_revoked_output"), + SignerOp::SignJusticeRevokedHtlc => write!(f, "sign_justice_revoked_htlc"), + SignerOp::SignHolderHtlcTransaction => write!(f, "sign_holder_htlc_transaction"), + SignerOp::SignCounterpartyHtlcTransaction => write!(f, "sign_counterparty_htlc_transaction"), + SignerOp::SignClosingTransaction => write!(f, "sign_closing_transaction"), + SignerOp::SignHolderAnchorInput => write!(f, "sign_holder_anchor_input"), + SignerOp::SignChannelAnnouncementWithFundingKey => write!(f, "sign_channel_announcement_with_funding_key"), + } + } } impl PartialEq for TestChannelSigner { @@ -91,6 +131,7 @@ impl TestChannelSigner { state, disable_revocation_policy_check: false, available: Arc::new(Mutex::new(true)), + disabled_signer_ops: Arc::new(Mutex::new(new_hash_set())), } } @@ -105,6 +146,7 @@ impl TestChannelSigner { state, disable_revocation_policy_check, available: Arc::new(Mutex::new(true)), + disabled_signer_ops: Arc::new(Mutex::new(new_hash_set())), } } @@ -123,6 +165,18 @@ impl TestChannelSigner { pub fn set_available(&self, available: bool) { *self.available.lock().unwrap() = available; } + + pub fn enable_op(&mut self, signer_op: SignerOp) { + self.disabled_signer_ops.lock().unwrap().remove(&signer_op); + } + + pub fn disable_op(&mut self, signer_op: SignerOp) { + self.disabled_signer_ops.lock().unwrap().insert(signer_op); + } + + fn is_signer_available(&self, signer_op: SignerOp) -> bool { + !self.disabled_signer_ops.lock().unwrap().contains(&signer_op) + } } impl ChannelSigner for TestChannelSigner { diff --git a/lightning/src/util/test_utils.rs b/lightning/src/util/test_utils.rs index f6616a8e5..afdcbbac3 100644 --- a/lightning/src/util/test_utils.rs +++ b/lightning/src/util/test_utils.rs @@ -79,6 +79,8 @@ use std::time::{SystemTime, UNIX_EPOCH}; use bitcoin::psbt::Psbt; use bitcoin::Sequence; +use super::test_channel_signer::SignerOp; + pub fn pubkey(byte: u8) -> PublicKey { let secp_ctx = Secp256k1::new(); PublicKey::from_secret_key(&secp_ctx, &privkey(byte)) @@ -1215,6 +1217,7 @@ pub struct TestKeysInterface { enforcement_states: Mutex>>>, expectations: Mutex>>, pub unavailable_signers: Mutex>, + pub unavailable_signers_ops: Mutex>>, } impl EntropySource for TestKeysInterface { @@ -1273,10 +1276,15 @@ impl SignerProvider for TestKeysInterface { fn derive_channel_signer(&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32]) -> TestChannelSigner { let keys = self.backing.derive_channel_signer(channel_value_satoshis, channel_keys_id); let state = self.make_enforcement_state_cell(keys.commitment_seed); - let signer = TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check); + let mut signer = TestChannelSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check); if self.unavailable_signers.lock().unwrap().contains(&channel_keys_id) { signer.set_available(false); } + if let Some(ops) = self.unavailable_signers_ops.lock().unwrap().get(&channel_keys_id) { + for &op in ops { + signer.disable_op(op); + } + } signer } @@ -1316,6 +1324,7 @@ impl TestKeysInterface { enforcement_states: Mutex::new(new_hash_map()), expectations: Mutex::new(None), unavailable_signers: Mutex::new(new_hash_set()), + unavailable_signers_ops: Mutex::new(new_hash_map()), } } -- 2.39.5