From 90621cfc3c0cd2403dea71ea43d4532dc922c735 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Fri, 22 May 2026 14:49:58 -0700 Subject: [PATCH 1/3] Manually exit quiescence in fuzzing upon disconnect In certain cases, we may need to terminate quiescence as a result of some error via a `ChannelError::WarnAndDisconnect`. We don't need to necessarily reconnect the peers, so we choose to manually terminate quiescence via the existing `ChannelManager::exit_quiescence` test helper. --- fuzz/src/chanmon_consistency.rs | 40 ++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 4af1d1301b0..532d4fc3068 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -769,19 +769,19 @@ type ChanMan<'a> = ChannelManager< >; #[inline] -fn assert_action_timeout_awaiting_response(action: &msgs::ErrorAction) { +fn assert_disconnect_action(action: &msgs::ErrorAction) -> (&msgs::WarningMessage, bool) { // Since sending/receiving messages may be delayed, `timer_tick_occurred` may cause a node to // disconnect their counterparty if they're expecting a timely response. - assert!( - matches!( - action, - msgs::ErrorAction::DisconnectPeerWithWarning { msg } - if msg.data.contains("Disconnecting due to timeout awaiting response") - || msg.data.contains("already sent splice_locked, cannot RBF") - ), - "Expected timeout disconnect, got: {:?}", - action, - ); + if let msgs::ErrorAction::DisconnectPeerWithWarning { ref msg } = action { + let is_quiescent_msg = msg.data.contains("already sent splice_locked, cannot RBF"); + if !msg.data.contains("Disconnecting due to timeout awaiting response") && !is_quiescent_msg + { + panic!("Unexpected disconnect case: {}", msg.data); + } + (msg, is_quiescent_msg) + } else { + panic!("Expected disconnect, got: {:?}", action); + } } #[derive(Clone, Copy, PartialEq)] @@ -1286,7 +1286,7 @@ impl EventQueues { *node_id == a_id }, MessageSendEvent::HandleError { ref action, ref node_id } => { - assert_action_timeout_awaiting_response(action); + assert_disconnect_action(action); if Some(*node_id) == expect_drop_id { panic!( "peer_disconnected should drop msgs bound for the disconnected peer" @@ -1335,7 +1335,7 @@ impl EventQueues { MessageSendEvent::BroadcastChannelUpdate { .. } => {}, MessageSendEvent::SendChannelUpdate { .. } => {}, MessageSendEvent::HandleError { ref action, .. } => { - assert_action_timeout_awaiting_response(action); + assert_disconnect_action(action); }, _ => panic!("Unhandled message event"), } @@ -1354,7 +1354,7 @@ impl EventQueues { MessageSendEvent::BroadcastChannelUpdate { .. } => {}, MessageSendEvent::SendChannelUpdate { .. } => {}, MessageSendEvent::HandleError { ref action, .. } => { - assert_action_timeout_awaiting_response(action); + assert_disconnect_action(action); }, _ => panic!("Unhandled message event"), } @@ -2645,8 +2645,16 @@ impl<'a, Out: Output + MaybeSend + MaybeSync> Harness<'a, Out> { nodes[dest_idx].handle_splice_locked(source_node_id, msg); None }, - MessageSendEvent::HandleError { ref action, .. } => { - assert_action_timeout_awaiting_response(action); + MessageSendEvent::HandleError { ref action, ref node_id, .. } => { + let (msg, is_quiescent) = assert_disconnect_action(action); + let dest_idx = log_peer_message(node_idx, node_id, nodes, out, "warning"); + if is_quiescent { + nodes[node_idx].node.exit_quiescence(node_id, &msg.channel_id).unwrap(); + nodes[dest_idx] + .node + .exit_quiescence(&source_node_id, &msg.channel_id) + .unwrap(); + } None }, MessageSendEvent::SendChannelReady { .. } From 836bc386bdbbbf03dd81cf064e12021c93893b40 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Fri, 22 May 2026 14:49:59 -0700 Subject: [PATCH 2/3] Restore splice fuzzing by default This removes the temporary cfg flag that was added while the splice fuzzer was broken. We also include coverage for the newly supported async signing of a splice's shared input. --- fuzz/Cargo.toml | 1 - fuzz/src/chanmon_consistency.rs | 53 +++++++++++++++++---------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 76b4968f043..bf0d463f0fe 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -43,6 +43,5 @@ check-cfg = [ "cfg(fuzzing)", "cfg(secp256k1_fuzz)", "cfg(hashes_fuzz)", - "cfg(splicing)", "cfg(chacha20_poly1305_fuzz)" ] diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs index 532d4fc3068..ea2b93e89fc 100644 --- a/fuzz/src/chanmon_consistency.rs +++ b/fuzz/src/chanmon_consistency.rs @@ -720,10 +720,11 @@ impl SignerProvider for KeyProvider { // Since this fuzzer is only concerned with live-channel operations, we don't need to worry about // any signer operations that come after a force close. -const SUPPORTED_SIGNER_OPS: [SignerOp; 3] = [ +const SUPPORTED_SIGNER_OPS: [SignerOp; 4] = [ SignerOp::SignCounterpartyCommitment, SignerOp::GetPerCommitmentPoint, SignerOp::ReleaseCommitmentSecret, + SignerOp::SignSpliceSharedInput, ]; impl KeyProvider { @@ -3125,59 +3126,35 @@ pub fn do_test(data: &[u8], out: Out) { 0x89 => harness.nodes[2].reset_fee_estimate(), 0xa0 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[1].get_our_node_id(); harness.nodes[0].splice_in(&cp_node_id, &harness.chan_a_id()); }, 0xa1 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[0].get_our_node_id(); harness.nodes[1].splice_in(&cp_node_id, &harness.chan_a_id()); }, 0xa2 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[2].get_our_node_id(); harness.nodes[1].splice_in(&cp_node_id, &harness.chan_b_id()); }, 0xa3 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[1].get_our_node_id(); harness.nodes[2].splice_in(&cp_node_id, &harness.chan_b_id()); }, 0xa4 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[1].get_our_node_id(); harness.nodes[0].splice_out(&cp_node_id, &harness.chan_a_id()); }, 0xa5 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[0].get_our_node_id(); harness.nodes[1].splice_out(&cp_node_id, &harness.chan_a_id()); }, 0xa6 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[2].get_our_node_id(); harness.nodes[1].splice_out(&cp_node_id, &harness.chan_b_id()); }, 0xa7 => { - if !cfg!(splicing) { - break 'fuzz_loop; - } let cp_node_id = harness.nodes[1].get_our_node_id(); harness.nodes[2].splice_out(&cp_node_id, &harness.chan_b_id()); }, @@ -3306,6 +3283,32 @@ pub fn do_test(data: &[u8], out: Out) { .enable_op_for_all_signers(SignerOp::ReleaseCommitmentSecret); harness.nodes[2].signer_unblocked(None); }, + 0xcf => { + harness.nodes[0] + .keys_manager + .enable_op_for_all_signers(SignerOp::SignSpliceSharedInput); + harness.nodes[0].signer_unblocked(None); + }, + 0xd0 => { + harness.nodes[1] + .keys_manager + .enable_op_for_all_signers(SignerOp::SignSpliceSharedInput); + let filter = Some((harness.nodes[0].get_our_node_id(), harness.chan_a_id())); + harness.nodes[1].signer_unblocked(filter); + }, + 0xd1 => { + harness.nodes[1] + .keys_manager + .enable_op_for_all_signers(SignerOp::SignSpliceSharedInput); + let filter = Some((harness.nodes[2].get_our_node_id(), harness.chan_b_id())); + harness.nodes[1].signer_unblocked(filter); + }, + 0xd2 => { + harness.nodes[2] + .keys_manager + .enable_op_for_all_signers(SignerOp::SignSpliceSharedInput); + harness.nodes[2].signer_unblocked(None); + }, 0xf0 => harness.ab_link.complete_monitor_updates_for_node( 0, From f0ce340c6096c04d47ba663d2e6754e804022f3c Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Fri, 22 May 2026 14:50:02 -0700 Subject: [PATCH 3/3] Fix fuzz build warnings --- fuzz/src/lsps_message.rs | 1 - lightning/src/ln/outbound_payment.rs | 2 +- lightning/src/ln/peer_channel_encryptor.rs | 2 +- lightning/src/routing/gossip.rs | 2 +- lightning/src/util/time.rs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/fuzz/src/lsps_message.rs b/fuzz/src/lsps_message.rs index 83fa5ddab6d..7a3cb0cf7e0 100644 --- a/fuzz/src/lsps_message.rs +++ b/fuzz/src/lsps_message.rs @@ -5,7 +5,6 @@ use bitcoin::hashes::{sha256, Hash}; use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use bitcoin::Network; -use lightning::chain::Filter; use lightning::chain::{chainmonitor, BlockLocator}; use lightning::ln::channelmanager::{ChainParameters, ChannelManager}; use lightning::ln::peer_handler::CustomMessageHandler; diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 7259f60796f..273ed4ec1d2 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -38,7 +38,7 @@ use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use crate::util::errors::APIError; use crate::util::logger::{Logger, WithContext}; use crate::util::ser::ReadableArgs; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", not(fuzzing)))] use crate::util::time::Instant; use core::fmt::{self, Display, Formatter}; diff --git a/lightning/src/ln/peer_channel_encryptor.rs b/lightning/src/ln/peer_channel_encryptor.rs index d9fc6dd2c6a..5f461315712 100644 --- a/lightning/src/ln/peer_channel_encryptor.rs +++ b/lightning/src/ln/peer_channel_encryptor.rs @@ -556,7 +556,7 @@ impl PeerChannelEncryptor { /// Encrypts the given message, returning the encrypted version. /// panics if the length of `message`, once encoded, is greater than 65535 or if the Noise /// handshake has not finished. - pub fn encrypt_message(&mut self, message: wire::Message) -> Vec { + pub(crate) fn encrypt_message(&mut self, message: wire::Message) -> Vec { // Allocate a buffer with 2KB, fitting most common messages. Reserve the first 16+2 bytes // for the 2-byte message type prefix and its MAC. let mut res = VecWriter(Vec::with_capacity(MSG_BUF_ALLOC_SIZE)); diff --git a/lightning/src/routing/gossip.rs b/lightning/src/routing/gossip.rs index adeb67a9e6c..7688db15311 100644 --- a/lightning/src/routing/gossip.rs +++ b/lightning/src/routing/gossip.rs @@ -57,7 +57,7 @@ use core::{cmp, fmt}; pub use lightning_types::routing::RoutingFees; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", not(fuzzing)))] use std::time::{SystemTime, UNIX_EPOCH}; /// We remove stale channel directional info two weeks after the last update, per BOLT 7's diff --git a/lightning/src/util/time.rs b/lightning/src/util/time.rs index c6041543572..626e96e1350 100644 --- a/lightning/src/util/time.rs +++ b/lightning/src/util/time.rs @@ -7,7 +7,7 @@ //! A simple module which either re-exports [`std::time::Instant`] or a mocked version of it for //! tests. -#[cfg(not(test))] +#[cfg(all(not(test), not(fuzzing)))] pub use std::time::Instant; #[cfg(test)] pub use test::Instant;