What changed, and why it matters
This commit is a code cleanup inside a fuzz test file. It moves the creation of test-only helpers (loggers, keys, monitors, broadcasters, wallets, fee estimators) into a single struct method and removes duplicated local closures. There is no change to production code, no change to cryptographic behavior, and no fix for a security bug.
No security action needed. Treat as normal test refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff refactors fuzz/src/chanmon_consistency.rs, a Lightning Dev Kit fuzzing harness. It introduces HarnessNode::new, build_loggers, build_chain_monitor, and assert_test_invariants, and replaces an inline make_node! macro and reload_node closure with these helpers. The refactor also stores logger, broadcaster, fee_estimator, and wallet inside HarnessNode so closures like splice_in/splice_out can access them without capturing external variables. The deterministic test inputs (hard-coded secrets, fixed fee estimator values, fixed monitor persistence styles) are preserved. No production library code is touched.
Changed components
fuzz/src/chanmon_consistency.rsInspect captured patch +345 / −324
diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs
index 742135f..c149c86 100644
--- a/fuzz/src/chanmon_consistency.rs
+++ b/fuzz/src/chanmon_consistency.rs
@@ -948,6 +948,10 @@ struct HarnessNode<'a> {
node: ChanMan<'a>,
monitor: Arc<TestChainMonitor>,
keys_manager: Arc<KeyProvider>,
+ logger: Arc<dyn Logger + MaybeSend + MaybeSync>,
+ broadcaster: Arc<TestBroadcaster>,
+ fee_estimator: Arc<FuzzEstimator>,
+ wallet: TestWalletSource,
}
impl<'a> std::ops::Deref for HarnessNode<'a> {
@@ -959,6 +963,72 @@ impl<'a> std::ops::Deref for HarnessNode<'a> {
}
impl<'a> HarnessNode<'a> {
+ fn build_loggers<Out: Output + MaybeSend + MaybeSync>(
+ node_id: u8, out: &Out,
+ ) -> (Arc<dyn Logger>, Arc<dyn Logger + MaybeSend + MaybeSync>) {
+ let raw_logger = Arc::new(test_logger::TestLogger::new(node_id.to_string(), out.clone()));
+ let logger_for_monitor: Arc<dyn Logger> = raw_logger.clone();
+ let logger: Arc<dyn Logger + MaybeSend + MaybeSync> = raw_logger;
+ (logger_for_monitor, logger)
+ }
+
+ fn build_chain_monitor(
+ broadcaster: &Arc<TestBroadcaster>, fee_estimator: &Arc<FuzzEstimator>,
+ keys_manager: &Arc<KeyProvider>, logger_for_monitor: Arc<dyn Logger>,
+ persistence_style: ChannelMonitorUpdateStatus,
+ ) -> Arc<TestChainMonitor> {
+ Arc::new(TestChainMonitor::new(
+ Arc::clone(broadcaster),
+ logger_for_monitor,
+ Arc::clone(fee_estimator),
+ Arc::new(TestPersister { update_ret: Mutex::new(persistence_style) }),
+ Arc::clone(keys_manager),
+ ))
+ }
+
+ fn new<Out: Output + MaybeSend + MaybeSync>(
+ node_id: u8, wallet: TestWalletSource, fee_estimator: Arc<FuzzEstimator>,
+ broadcaster: Arc<TestBroadcaster>, persistence_style: ChannelMonitorUpdateStatus,
+ out: &Out, router: &'a FuzzRouter, chan_type: ChanType,
+ ) -> Self {
+ let (logger_for_monitor, logger) = Self::build_loggers(node_id, out);
+ let node_secret = SecretKey::from_slice(&[
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, node_id,
+ ])
+ .unwrap();
+ let keys_manager = Arc::new(KeyProvider {
+ node_secret,
+ rand_bytes_id: atomic::AtomicU32::new(0),
+ enforcement_states: Mutex::new(new_hash_map()),
+ });
+ let monitor = Self::build_chain_monitor(
+ &broadcaster,
+ &fee_estimator,
+ &keys_manager,
+ logger_for_monitor,
+ persistence_style,
+ );
+ let network = Network::Bitcoin;
+ let best_block_timestamp = genesis_block(network).header.time;
+ let params = ChainParameters { network, best_block: BlockLocator::from_network(network) };
+ let node = ChannelManager::new(
+ Arc::clone(&fee_estimator),
+ Arc::clone(&monitor),
+ Arc::clone(&broadcaster),
+ router,
+ router,
+ Arc::clone(&logger),
+ Arc::clone(&keys_manager),
+ Arc::clone(&keys_manager),
+ Arc::clone(&keys_manager),
+ build_node_config(chan_type),
+ params,
+ best_block_timestamp,
+ );
+ Self { node, monitor, keys_manager, logger, broadcaster, fee_estimator, wallet }
+ }
+
fn complete_all_pending_monitor_updates(&self) {
for (channel_id, state) in self.monitor.latest_monitors.lock().unwrap().iter_mut() {
for (id, data) in state.pending_monitors.drain(..) {
@@ -994,6 +1064,17 @@ fn build_node_config(chan_type: ChanType) -> UserConfig {
config
}
+fn assert_test_invariants(nodes: &[HarnessNode<'_>; 3]) {
+ assert_eq!(nodes[0].list_channels().len(), 3);
+ assert_eq!(nodes[1].list_channels().len(), 6);
+ assert_eq!(nodes[2].list_channels().len(), 3);
+
+ // All broadcasters should be empty. Broadcast transactions are handled explicitly.
+ assert!(nodes[0].broadcaster.txn_broadcasted.borrow().is_empty());
+ assert!(nodes[1].broadcaster.txn_broadcasted.borrow().is_empty());
+ assert!(nodes[2].broadcaster.txn_broadcasted.borrow().is_empty());
+}
+
fn connect_peers(source: &ChanMan<'_>, dest: &ChanMan<'_>) {
let init_dest =
Init { features: dest.init_features(), networks: None, remote_network_address: None };
@@ -1188,9 +1269,6 @@ fn lock_fundings(nodes: &[HarnessNode<'_>; 3]) {
#[inline]
pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
- let broadcast_a = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
- let broadcast_b = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
- let broadcast_c = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
let router = FuzzRouter {};
// Read initial monitor styles and channel type from fuzz input byte 0:
@@ -1224,163 +1302,26 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
let mut node_height_a: u32 = 0;
let mut node_height_b: u32 = 0;
let mut node_height_c: u32 = 0;
-
- macro_rules! make_node {
- ($node_id: expr, $fee_estimator: expr, $broadcaster: expr) => {{
- let logger: Arc<dyn Logger + MaybeSend + MaybeSync> =
- Arc::new(test_logger::TestLogger::new($node_id.to_string(), out.clone()));
- let node_secret = SecretKey::from_slice(&[
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 1, $node_id,
- ])
- .unwrap();
- let keys_manager = Arc::new(KeyProvider {
- node_secret,
- rand_bytes_id: atomic::AtomicU32::new(0),
- enforcement_states: Mutex::new(new_hash_map()),
- });
- let monitor = Arc::new(TestChainMonitor::new(
- $broadcaster.clone(),
- logger.clone(),
- $fee_estimator.clone(),
- Arc::new(TestPersister {
- update_ret: Mutex::new(mon_style[$node_id as usize].borrow().clone()),
- }),
- Arc::clone(&keys_manager),
- ));
-
- let network = Network::Bitcoin;
- let best_block_timestamp = genesis_block(network).header.time;
- let params =
- ChainParameters { network, best_block: BlockLocator::from_network(network) };
- (
- ChannelManager::new(
- $fee_estimator.clone(),
- monitor.clone(),
- $broadcaster.clone(),
- &router,
- &router,
- Arc::clone(&logger),
- keys_manager.clone(),
- keys_manager.clone(),
- keys_manager.clone(),
- build_node_config(chan_type),
- params,
- best_block_timestamp,
- ),
- monitor,
- keys_manager,
- logger,
- )
- }};
- }
-
- let reload_node = |ser: &Vec<u8>,
- node_id: u8,
- old_monitors: &TestChainMonitor,
- mut use_old_mons,
- keys,
- fee_estimator,
- broadcaster: Arc<TestBroadcaster>| {
- let keys_manager = Arc::clone(keys);
- let logger: Arc<dyn Logger + MaybeSend + MaybeSync> =
- Arc::new(test_logger::TestLogger::new(node_id.to_string(), out.clone()));
- let chain_monitor = Arc::new(TestChainMonitor::new(
- broadcaster.clone(),
- logger.clone(),
- Arc::clone(fee_estimator),
- Arc::new(TestPersister {
- update_ret: Mutex::new(ChannelMonitorUpdateStatus::Completed),
- }),
- Arc::clone(keys),
- ));
-
- let mut monitors = new_hash_map();
- let mut old_monitors = old_monitors.latest_monitors.lock().unwrap();
- for (channel_id, mut prev_state) in old_monitors.drain() {
- let (mon_id, serialized_mon) = if use_old_mons % 3 == 0 {
- // Reload with the oldest `ChannelMonitor` (the one that we already told
- // `ChannelManager` we finished persisting).
- (prev_state.persisted_monitor_id, prev_state.persisted_monitor)
- } else if use_old_mons % 3 == 1 {
- // Reload with the second-oldest `ChannelMonitor`
- let old_mon = (prev_state.persisted_monitor_id, prev_state.persisted_monitor);
- prev_state.pending_monitors.drain(..).next().unwrap_or(old_mon)
- } else {
- // Reload with the newest `ChannelMonitor`
- let old_mon = (prev_state.persisted_monitor_id, prev_state.persisted_monitor);
- prev_state.pending_monitors.pop().unwrap_or(old_mon)
- };
- // Use a different value of `use_old_mons` if we have another monitor (only for node B)
- // by shifting `use_old_mons` one in base-3.
- use_old_mons /= 3;
- let mon = <(BlockLocator, ChannelMonitor<TestChannelSigner>)>::read(
- &mut &serialized_mon[..],
- (&**keys, &**keys),
- )
- .expect("Failed to read monitor");
- monitors.insert(channel_id, mon.1);
- // Update the latest `ChannelMonitor` state to match what we just told LDK.
- prev_state.persisted_monitor = serialized_mon;
- prev_state.persisted_monitor_id = mon_id;
- // Wipe any `ChannelMonitor`s which we never told LDK we finished persisting,
- // considering them discarded. LDK should replay these for us as they're stored in
- // the `ChannelManager`.
- prev_state.pending_monitors.clear();
- chain_monitor.latest_monitors.lock().unwrap().insert(channel_id, prev_state);
- }
- let mut monitor_refs = new_hash_map();
- for (channel_id, monitor) in monitors.iter() {
- monitor_refs.insert(*channel_id, monitor);
- }
-
- let read_args = ChannelManagerReadArgs {
- entropy_source: Arc::clone(&keys_manager),
- node_signer: Arc::clone(&keys_manager),
- signer_provider: keys_manager,
- fee_estimator: Arc::clone(fee_estimator),
- chain_monitor: chain_monitor.clone(),
- tx_broadcaster: broadcaster,
- router: &router,
- message_router: &router,
- logger,
- config: build_node_config(chan_type),
- channel_monitors: monitor_refs,
- };
-
- let manager = <(BlockLocator, ChanMan)>::read(&mut &ser[..], read_args)
- .expect("Failed to read manager");
- let res = (manager.1, chain_monitor.clone());
- for (channel_id, mon) in monitors.drain() {
- assert_eq!(
- chain_monitor.chain_monitor.watch_channel(channel_id, mon),
- Ok(ChannelMonitorUpdateStatus::Completed)
- );
- }
- *chain_monitor.persister.update_ret.lock().unwrap() = *mon_style[node_id as usize].borrow();
- res
- };
-
let wallet_a = TestWalletSource::new(SecretKey::from_slice(&[1; 32]).unwrap());
let wallet_b = TestWalletSource::new(SecretKey::from_slice(&[2; 32]).unwrap());
let wallet_c = TestWalletSource::new(SecretKey::from_slice(&[3; 32]).unwrap());
- let wallets = vec![wallet_a, wallet_b, wallet_c];
+ let wallets = [&wallet_a, &wallet_b, &wallet_c];
let coinbase_tx = bitcoin::Transaction {
version: bitcoin::transaction::Version::TWO,
lock_time: bitcoin::absolute::LockTime::ZERO,
input: vec![bitcoin::TxIn { ..Default::default() }],
output: wallets
.iter()
- .map(|w| TxOut {
+ .map(|wallet| TxOut {
value: Amount::from_sat(100_000),
- script_pubkey: w.get_change_script().unwrap(),
+ script_pubkey: wallet.get_change_script().unwrap(),
})
.collect(),
};
- wallets.iter().enumerate().for_each(|(i, w)| {
- w.add_utxo(coinbase_tx.clone(), i as u32);
- });
+ for (idx, wallet) in wallets.iter().enumerate() {
+ wallet.add_utxo(coinbase_tx.clone(), idx as u32);
+ }
let fee_est_a = Arc::new(FuzzEstimator { ret_val: atomic::AtomicU32::new(253) });
let mut last_htlc_clear_fee_a = 253;
@@ -1388,34 +1329,50 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
let mut last_htlc_clear_fee_b = 253;
let fee_est_c = Arc::new(FuzzEstimator { ret_val: atomic::AtomicU32::new(253) });
let mut last_htlc_clear_fee_c = 253;
+ let broadcast_a = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
+ let broadcast_b = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
+ let broadcast_c = Arc::new(TestBroadcaster { txn_broadcasted: RefCell::new(Vec::new()) });
// 3 nodes is enough to hit all the possible cases, notably unknown-source-unknown-dest
// forwarding.
- let (node_a, mut monitor_a, keys_manager_a, logger_a) = make_node!(0, fee_est_a, broadcast_a);
- let (node_b, mut monitor_b, keys_manager_b, logger_b) = make_node!(1, fee_est_b, broadcast_b);
- let (node_c, mut monitor_c, keys_manager_c, logger_c) = make_node!(2, fee_est_c, broadcast_c);
-
let mut nodes = [
- HarnessNode {
- node: node_a,
- monitor: Arc::clone(&monitor_a),
- keys_manager: Arc::clone(&keys_manager_a),
- },
- HarnessNode {
- node: node_b,
- monitor: Arc::clone(&monitor_b),
- keys_manager: Arc::clone(&keys_manager_b),
- },
- HarnessNode {
- node: node_c,
- monitor: Arc::clone(&monitor_c),
- keys_manager: Arc::clone(&keys_manager_c),
- },
+ HarnessNode::new(
+ 0,
+ wallet_a,
+ Arc::clone(&fee_est_a),
+ Arc::clone(&broadcast_a),
+ mon_style[0].borrow().clone(),
+ &out,
+ &router,
+ chan_type,
+ ),
+ HarnessNode::new(
+ 1,
+ wallet_b,
+ Arc::clone(&fee_est_b),
+ Arc::clone(&broadcast_b),
+ mon_style[1].borrow().clone(),
+ &out,
+ &router,
+ chan_type,
+ ),
+ HarnessNode::new(
+ 2,
+ wallet_c,
+ Arc::clone(&fee_est_c),
+ Arc::clone(&broadcast_c),
+ mon_style[2].borrow().clone(),
+ &out,
+ &router,
+ chan_type,
+ ),
];
- #[allow(unused_variables)]
- let loggers = [logger_a, logger_b, logger_c];
- #[allow(unused_variables)]
- let fee_estimators = [Arc::clone(&fee_est_a), Arc::clone(&fee_est_b), Arc::clone(&fee_est_c)];
+ let mut monitor_a = Arc::clone(&nodes[0].monitor);
+ let mut monitor_b = Arc::clone(&nodes[1].monitor);
+ let mut monitor_c = Arc::clone(&nodes[2].monitor);
+ let keys_manager_a = Arc::clone(&nodes[0].keys_manager);
+ let keys_manager_b = Arc::clone(&nodes[1].keys_manager);
+ let keys_manager_c = Arc::clone(&nodes[2].keys_manager);
// Connect peers first, then create channels
connect_peers(&nodes[0], &nodes[1]);
@@ -1438,12 +1395,12 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
// Wipe the transactions-broadcasted set to make sure we don't broadcast any transactions
// during normal operation in `test_return`.
- broadcast_a.txn_broadcasted.borrow_mut().clear();
- broadcast_b.txn_broadcasted.borrow_mut().clear();
- broadcast_c.txn_broadcasted.borrow_mut().clear();
+ nodes[0].broadcaster.txn_broadcasted.borrow_mut().clear();
+ nodes[1].broadcaster.txn_broadcasted.borrow_mut().clear();
+ nodes[2].broadcaster.txn_broadcasted.borrow_mut().clear();
let sync_with_chain_state = |chain_state: &ChainState,
- node: &ChannelManager<_, _, _, _, _, _, _, _, _>,
+ node: &HarnessNode<'_>,
node_height: &mut u32,
num_blocks: Option<u32>| {
let target_height = if let Some(num_blocks) = num_blocks {
@@ -1451,7 +1408,6 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
} else {
chain_state.tip_height()
};
-
while *node_height < target_height {
*node_height += 1;
let (header, txn) = chain_state.block_at(*node_height);
@@ -1464,9 +1420,9 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
};
// Sync all nodes to tip to lock the funding.
- sync_with_chain_state(&mut chain_state, &nodes[0], &mut node_height_a, None);
- sync_with_chain_state(&mut chain_state, &nodes[1], &mut node_height_b, None);
- sync_with_chain_state(&mut chain_state, &nodes[2], &mut node_height_c, None);
+ sync_with_chain_state(&chain_state, &nodes[0], &mut node_height_a, None);
+ sync_with_chain_state(&chain_state, &nodes[1], &mut node_height_b, None);
+ sync_with_chain_state(&chain_state, &nodes[2], &mut node_height_c, None);
lock_fundings(&nodes);
@@ -1506,20 +1462,93 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
macro_rules! test_return {
() => {{
- assert_eq!(nodes[0].list_channels().len(), 3);
- assert_eq!(nodes[1].list_channels().len(), 6);
- assert_eq!(nodes[2].list_channels().len(), 3);
-
- // All broadcasters should be empty (all broadcast transactions should be handled
- // explicitly).
- assert!(broadcast_a.txn_broadcasted.borrow().is_empty());
- assert!(broadcast_b.txn_broadcasted.borrow().is_empty());
- assert!(broadcast_c.txn_broadcasted.borrow().is_empty());
-
+ assert_test_invariants(&nodes);
return;
}};
}
+ let reload_node = |ser: &Vec<u8>,
+ node_id: u8,
+ old_monitors: &TestChainMonitor,
+ mut use_old_mons,
+ keys: &Arc<KeyProvider>,
+ fee_estimator: &Arc<FuzzEstimator>,
+ broadcaster: Arc<TestBroadcaster>| {
+ let keys_manager = Arc::clone(keys);
+ let (logger_for_monitor, logger) = HarnessNode::build_loggers(node_id, &out);
+ let chain_monitor = HarnessNode::build_chain_monitor(
+ &broadcaster,
+ fee_estimator,
+ &keys_manager,
+ logger_for_monitor,
+ ChannelMonitorUpdateStatus::Completed,
+ );
+
+ let mut monitors = new_hash_map();
+ let mut old_monitors = old_monitors.latest_monitors.lock().unwrap();
+ for (channel_id, mut prev_state) in old_monitors.drain() {
+ let (mon_id, serialized_mon) = if use_old_mons % 3 == 0 {
+ // Reload with the oldest `ChannelMonitor` (the one that we already told
+ // `ChannelManager` we finished persisting).
+ (prev_state.persisted_monitor_id, prev_state.persisted_monitor)
+ } else if use_old_mons % 3 == 1 {
+ // Reload with the second-oldest `ChannelMonitor`
+ let old_mon = (prev_state.persisted_monitor_id, prev_state.persisted_monitor);
+ prev_state.pending_monitors.drain(..).next().unwrap_or(old_mon)
+ } else {
+ // Reload with the newest `ChannelMonitor`
+ let old_mon = (prev_state.persisted_monitor_id, prev_state.persisted_monitor);
+ prev_state.pending_monitors.pop().unwrap_or(old_mon)
+ };
+ // Use a different value of `use_old_mons` if we have another monitor (only for node B)
+ // by shifting `use_old_mons` one in base-3.
+ use_old_mons /= 3;
+ let mon = <(BlockLocator, ChannelMonitor<TestChannelSigner>)>::read(
+ &mut &serialized_mon[..],
+ (&*keys_manager, &*keys_manager),
+ )
+ .expect("Failed to read monitor");
+ monitors.insert(channel_id, mon.1);
+ // Update the latest `ChannelMonitor` state to match what we just told LDK.
+ prev_state.persisted_monitor = serialized_mon;
+ prev_state.persisted_monitor_id = mon_id;
+ // Wipe any `ChannelMonitor`s which we never told LDK we finished persisting,
+ // considering them discarded. LDK should replay these for us as they're stored in
+ // the `ChannelManager`.
+ prev_state.pending_monitors.clear();
+ chain_monitor.latest_monitors.lock().unwrap().insert(channel_id, prev_state);
+ }
+ let mut monitor_refs = new_hash_map();
+ for (channel_id, monitor) in monitors.iter() {
+ monitor_refs.insert(*channel_id, monitor);
+ }
+
+ let read_args = ChannelManagerReadArgs {
+ entropy_source: Arc::clone(&keys_manager),
+ node_signer: Arc::clone(&keys_manager),
+ signer_provider: Arc::clone(&keys_manager),
+ fee_estimator: Arc::clone(fee_estimator),
+ chain_monitor: chain_monitor.clone(),
+ tx_broadcaster: broadcaster,
+ router: &router,
+ message_router: &router,
+ logger: Arc::clone(&logger),
+ config: build_node_config(chan_type),
+ channel_monitors: monitor_refs,
+ };
+
+ let manager = <(BlockLocator, ChanMan)>::read(&mut &ser[..], read_args)
+ .expect("Failed to read manager");
+ for (channel_id, mon) in monitors.drain() {
+ assert_eq!(
+ chain_monitor.chain_monitor.watch_channel(channel_id, mon),
+ Ok(ChannelMonitorUpdateStatus::Completed)
+ );
+ }
+ *chain_monitor.persister.update_ret.lock().unwrap() = *mon_style[node_id as usize].borrow();
+ (manager.1, chain_monitor, logger)
+ };
+
let mut read_pos = 1; // First byte was consumed for initial config (mon_style + chan_type)
macro_rules! get_slice {
($len: expr) => {{
@@ -1532,82 +1561,6 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
}};
}
- let splice_channel =
- |node: &ChanMan,
- counterparty_node_id: &PublicKey,
- channel_id: &ChannelId,
- f: &dyn Fn(FundingTemplate) -> Result<FundingContribution, FundingContributionError>| {
- match node.splice_channel(channel_id, counterparty_node_id) {
- Ok(funding_template) => {
- if let Ok(contribution) = f(funding_template) {
- let _ = node.funding_contributed(
- channel_id,
- counterparty_node_id,
- contribution,
- None,
- );
- }
- },
- Err(e) => {
- assert!(
- matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice")),
- "{:?}",
- e
- );
- },
- }
- };
-
- let splice_in =
- |node: &ChanMan,
- counterparty_node_id: &PublicKey,
- channel_id: &ChannelId,
- wallet: &WalletSync<&TestWalletSource, Arc<dyn Logger + MaybeSend + MaybeSync>>,
- funding_feerate_sat_per_kw: FeeRate| {
- splice_channel(
- node,
- counterparty_node_id,
- channel_id,
- &move |funding_template: FundingTemplate| {
- let feerate =
- funding_template.min_rbf_feerate().unwrap_or(funding_feerate_sat_per_kw);
- funding_template.splice_in_sync(
- Amount::from_sat(10_000),
- feerate,
- FeeRate::MAX,
- wallet,
- )
- },
- );
- };
-
- let splice_out = |node: &ChanMan,
- counterparty_node_id: &PublicKey,
- channel_id: &ChannelId,
- wallet: &TestWalletSource,
- funding_feerate_sat_per_kw: FeeRate| {
- // We conditionally splice out `MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS` only when the node
- // has double the balance required to send a payment upon a `0xff` byte. We do this to
- // ensure there's always liquidity available for a payment to succeed then.
- let outbound_capacity_msat = node
- .list_channels()
- .iter()
- .find(|chan| chan.channel_id == *channel_id)
- .map(|chan| chan.outbound_capacity_msat)
- .unwrap();
- if outbound_capacity_msat < 20_000_000 {
- return;
- }
- splice_channel(node, counterparty_node_id, channel_id, &move |funding_template| {
- let feerate = funding_template.min_rbf_feerate().unwrap_or(funding_feerate_sat_per_kw);
- let outputs = vec![TxOut {
- value: Amount::from_sat(MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS),
- script_pubkey: wallet.get_change_script().unwrap(),
- }];
- funding_template.splice_out(outputs, feerate, FeeRate::MAX)
- });
- };
-
loop {
// Push any events from Node B onto ba_events and bc_events
macro_rules! push_excess_b_events {
@@ -2087,7 +2040,8 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
unsigned_transaction,
..
} => {
- let signed_tx = wallets[$node].sign_tx(unsigned_transaction).unwrap();
+ let signed_tx =
+ nodes[$node].wallet.sign_tx(unsigned_transaction).unwrap();
nodes[$node]
.funding_transaction_signed(
&channel_id,
@@ -2097,12 +2051,7 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
.unwrap();
},
events::Event::SpliceNegotiated { new_funding_txo, .. } => {
- let broadcaster = match $node {
- 0 => &broadcast_a,
- 1 => &broadcast_b,
- _ => &broadcast_c,
- };
- let mut txs = broadcaster.txn_broadcasted.borrow_mut();
+ let mut txs = nodes[$node].broadcaster.txn_broadcasted.borrow_mut();
assert!(txs.len() >= 1);
let splice_tx = txs.remove(0);
assert_eq!(new_funding_txo.txid, splice_tx.compute_txid());
@@ -2152,7 +2101,6 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
}
}
};
-
let complete_all_monitor_updates = |monitor: &Arc<TestChainMonitor>, chan_id| {
if let Some(state) = monitor.latest_monitors.lock().unwrap().get_mut(chan_id) {
assert!(
@@ -2169,6 +2117,85 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
}
};
+ let splice_channel =
+ |node: &HarnessNode<'_>,
+ counterparty_node_id: &PublicKey,
+ channel_id: &ChannelId,
+ f: &dyn Fn(
+ FundingTemplate,
+ ) -> Result<FundingContribution, FundingContributionError>| {
+ match node.splice_channel(channel_id, counterparty_node_id) {
+ Ok(funding_template) => {
+ if let Ok(contribution) = f(funding_template) {
+ let _ = node.funding_contributed(
+ channel_id,
+ counterparty_node_id,
+ contribution,
+ None,
+ );
+ }
+ },
+ Err(e) => {
+ assert!(
+ matches!(e, APIError::APIMisuseError { ref err } if err.contains("splice")),
+ "{:?}",
+ e
+ );
+ },
+ }
+ };
+
+ let splice_in = |node: &HarnessNode<'_>,
+ counterparty_node_id: &PublicKey,
+ channel_id: &ChannelId| {
+ let wallet = WalletSync::new(&node.wallet, Arc::clone(&node.logger));
+ let funding_feerate_sat_per_kw = node.fee_estimator.feerate_sat_per_kw();
+ splice_channel(
+ node,
+ counterparty_node_id,
+ channel_id,
+ &move |funding_template: FundingTemplate| {
+ let feerate =
+ funding_template.min_rbf_feerate().unwrap_or(funding_feerate_sat_per_kw);
+ funding_template.splice_in_sync(
+ Amount::from_sat(10_000),
+ feerate,
+ FeeRate::MAX,
+ &wallet,
+ )
+ },
+ );
+ };
+
+ let splice_out = |node: &HarnessNode<'_>,
+ counterparty_node_id: &PublicKey,
+ channel_id: &ChannelId| {
+ let outbound_capacity_msat = node
+ .list_channels()
+ .iter()
+ .find(|chan| chan.channel_id == *channel_id)
+ .map(|chan| chan.outbound_capacity_msat)
+ .unwrap();
+ if outbound_capacity_msat < 20_000_000 {
+ return;
+ }
+ let funding_feerate_sat_per_kw = node.fee_estimator.feerate_sat_per_kw();
+ splice_channel(
+ node,
+ counterparty_node_id,
+ channel_id,
+ &move |funding_template: FundingTemplate| {
+ let feerate =
+ funding_template.min_rbf_feerate().unwrap_or(funding_feerate_sat_per_kw);
+ let outputs = vec![TxOut {
+ value: Amount::from_sat(MAX_STD_OUTPUT_DUST_LIMIT_SATOSHIS),
+ script_pubkey: node.wallet.get_change_script().unwrap(),
+ }];
+ funding_template.splice_out(outputs, feerate, FeeRate::MAX)
+ },
+ );
+ };
+
let send =
|source_idx: usize, dest_idx: usize, dest_chan_id, amt, payment_ctr: &mut u64| {
let source = &nodes[source_idx];
@@ -2465,43 +2492,47 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
if matches!(chan_type, ChanType::Legacy) {
max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
}
- if fee_est_a.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
- fee_est_a.ret_val.store(max_feerate, atomic::Ordering::Release);
+ if nodes[0].fee_estimator.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250
+ > max_feerate
+ {
+ nodes[0].fee_estimator.ret_val.store(max_feerate, atomic::Ordering::Release);
}
nodes[0].timer_tick_occurred();
},
0x81 => {
- fee_est_a.ret_val.store(253, atomic::Ordering::Release);
+ nodes[0].fee_estimator.ret_val.store(253, atomic::Ordering::Release);
nodes[0].timer_tick_occurred();
},
-
0x84 => {
let mut max_feerate = last_htlc_clear_fee_b;
if matches!(chan_type, ChanType::Legacy) {
max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
}
- if fee_est_b.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
- fee_est_b.ret_val.store(max_feerate, atomic::Ordering::Release);
+ if nodes[1].fee_estimator.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250
+ > max_feerate
+ {
+ nodes[1].fee_estimator.ret_val.store(max_feerate, atomic::Ordering::Release);
}
nodes[1].timer_tick_occurred();
},
0x85 => {
- fee_est_b.ret_val.store(253, atomic::Ordering::Release);
+ nodes[1].fee_estimator.ret_val.store(253, atomic::Ordering::Release);
nodes[1].timer_tick_occurred();
},
-
0x88 => {
let mut max_feerate = last_htlc_clear_fee_c;
if matches!(chan_type, ChanType::Legacy) {
max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
}
- if fee_est_c.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
- fee_est_c.ret_val.store(max_feerate, atomic::Ordering::Release);
+ if nodes[2].fee_estimator.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250
+ > max_feerate
+ {
+ nodes[2].fee_estimator.ret_val.store(max_feerate, atomic::Ordering::Release);
}
nodes[2].timer_tick_occurred();
},
0x89 => {
- fee_est_c.ret_val.store(253, atomic::Ordering::Release);
+ nodes[2].fee_estimator.ret_val.store(253, atomic::Ordering::Release);
nodes[2].timer_tick_occurred();
},
@@ -2510,36 +2541,28 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
test_return!();
}
let cp_node_id = nodes[1].get_our_node_id();
- let wallet = WalletSync::new(&wallets[0], Arc::clone(&loggers[0]));
- let feerate_sat_per_kw = fee_estimators[0].feerate_sat_per_kw();
- splice_in(&nodes[0], &cp_node_id, &chan_a_id, &wallet, feerate_sat_per_kw);
+ splice_in(&nodes[0], &cp_node_id, &chan_a_id);
},
0xa1 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[0].get_our_node_id();
- let wallet = WalletSync::new(&wallets[1], Arc::clone(&loggers[1]));
- let feerate_sat_per_kw = fee_estimators[1].feerate_sat_per_kw();
- splice_in(&nodes[1], &cp_node_id, &chan_a_id, &wallet, feerate_sat_per_kw);
+ splice_in(&nodes[1], &cp_node_id, &chan_a_id);
},
0xa2 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[2].get_our_node_id();
- let wallet = WalletSync::new(&wallets[1], Arc::clone(&loggers[1]));
- let feerate_sat_per_kw = fee_estimators[1].feerate_sat_per_kw();
- splice_in(&nodes[1], &cp_node_id, &chan_b_id, &wallet, feerate_sat_per_kw);
+ splice_in(&nodes[1], &cp_node_id, &chan_b_id);
},
0xa3 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[1].get_our_node_id();
- let wallet = WalletSync::new(&wallets[2], Arc::clone(&loggers[2]));
- let feerate_sat_per_kw = fee_estimators[2].feerate_sat_per_kw();
- splice_in(&nodes[2], &cp_node_id, &chan_b_id, &wallet, feerate_sat_per_kw);
+ splice_in(&nodes[2], &cp_node_id, &chan_b_id);
},
0xa4 => {
@@ -2547,63 +2570,55 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
test_return!();
}
let cp_node_id = nodes[1].get_our_node_id();
- let wallet = &wallets[0];
- let feerate_sat_per_kw = fee_estimators[0].feerate_sat_per_kw();
- splice_out(&nodes[0], &cp_node_id, &chan_a_id, wallet, feerate_sat_per_kw);
+ splice_out(&nodes[0], &cp_node_id, &chan_a_id);
},
0xa5 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[0].get_our_node_id();
- let wallet = &wallets[1];
- let feerate_sat_per_kw = fee_estimators[1].feerate_sat_per_kw();
- splice_out(&nodes[1], &cp_node_id, &chan_a_id, wallet, feerate_sat_per_kw);
+ splice_out(&nodes[1], &cp_node_id, &chan_a_id);
},
0xa6 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[2].get_our_node_id();
- let wallet = &wallets[1];
- let feerate_sat_per_kw = fee_estimators[1].feerate_sat_per_kw();
- splice_out(&nodes[1], &cp_node_id, &chan_b_id, wallet, feerate_sat_per_kw);
+ splice_out(&nodes[1], &cp_node_id, &chan_b_id);
},
0xa7 => {
if !cfg!(splicing) {
test_return!();
}
let cp_node_id = nodes[1].get_our_node_id();
- let wallet = &wallets[2];
- let feerate_sat_per_kw = fee_estimators[2].feerate_sat_per_kw();
- splice_out(&nodes[2], &cp_node_id, &chan_b_id, wallet, feerate_sat_per_kw);
+ splice_out(&nodes[2], &cp_node_id, &chan_b_id);
},
// Sync node by 1 block to cover confirmation of a transaction.
0xa8 => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[0], &mut node_height_a, Some(1));
+ sync_with_chain_state(&chain_state, &nodes[0], &mut node_height_a, Some(1));
},
0xa9 => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[1], &mut node_height_b, Some(1));
+ sync_with_chain_state(&chain_state, &nodes[1], &mut node_height_b, Some(1));
},
0xaa => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[2], &mut node_height_c, Some(1));
+ sync_with_chain_state(&chain_state, &nodes[2], &mut node_height_c, Some(1));
},
// Sync node to chain tip to cover confirmation of a transaction post-reorg-risk.
0xab => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[0], &mut node_height_a, None);
+ sync_with_chain_state(&chain_state, &nodes[0], &mut node_height_a, None);
},
0xac => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[1], &mut node_height_b, None);
+ sync_with_chain_state(&chain_state, &nodes[1], &mut node_height_b, None);
},
0xad => {
chain_state.confirm_pending_txs();
- sync_with_chain_state(&mut chain_state, &nodes[2], &mut node_height_c, None);
+ sync_with_chain_state(&chain_state, &nodes[2], &mut node_height_c, None);
},
0xb0 | 0xb1 | 0xb2 => {
@@ -2619,18 +2634,19 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
ab_events.clear();
ba_events.clear();
}
- let (new_node_a, new_monitor_a) = reload_node(
+ let (new_node_a, new_monitor_a, new_logger_a) = reload_node(
&node_a_ser,
0,
&monitor_a,
v,
&keys_manager_a,
&fee_est_a,
- broadcast_a.clone(),
+ Arc::clone(&broadcast_a),
);
nodes[0].node = new_node_a;
monitor_a = Arc::clone(&new_monitor_a);
nodes[0].monitor = new_monitor_a;
+ nodes[0].logger = new_logger_a;
},
0xb3..=0xbb => {
// Restart node B, picking among the in-flight `ChannelMonitor`s to use based on
@@ -2649,18 +2665,19 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
bc_events.clear();
cb_events.clear();
}
- let (new_node_b, new_monitor_b) = reload_node(
+ let (new_node_b, new_monitor_b, new_logger_b) = reload_node(
&node_b_ser,
1,
&monitor_b,
v,
&keys_manager_b,
&fee_est_b,
- broadcast_b.clone(),
+ Arc::clone(&broadcast_b),
);
nodes[1].node = new_node_b;
monitor_b = Arc::clone(&new_monitor_b);
nodes[1].monitor = new_monitor_b;
+ nodes[1].logger = new_logger_b;
},
0xbc | 0xbd | 0xbe => {
// Restart node C, picking among the in-flight `ChannelMonitor`s to use based on
@@ -2675,18 +2692,19 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
bc_events.clear();
cb_events.clear();
}
- let (new_node_c, new_monitor_c) = reload_node(
+ let (new_node_c, new_monitor_c, new_logger_c) = reload_node(
&node_c_ser,
2,
&monitor_c,
v,
&keys_manager_c,
&fee_est_c,
- broadcast_c.clone(),
+ Arc::clone(&broadcast_c),
);
nodes[2].node = new_node_c;
monitor_c = Arc::clone(&new_monitor_c);
nodes[2].monitor = new_monitor_c;
+ nodes[2].logger = new_logger_c;
},
0xc0 => keys_manager_a.disable_supported_ops_for_all_signers(),
@@ -2961,20 +2979,23 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(data: &[u8], out: Out) {
);
}
- last_htlc_clear_fee_a = fee_est_a.ret_val.load(atomic::Ordering::Acquire);
- last_htlc_clear_fee_b = fee_est_b.ret_val.load(atomic::Ordering::Acquire);
- last_htlc_clear_fee_c = fee_est_c.ret_val.load(atomic::Ordering::Acquire);
+ last_htlc_clear_fee_a =
+ nodes[0].fee_estimator.ret_val.load(atomic::Ordering::Acquire);
+ last_htlc_clear_fee_b =
+ nodes[1].fee_estimator.ret_val.load(atomic::Ordering::Acquire);
+ last_htlc_clear_fee_c =
+ nodes[2].fee_estimator.ret_val.load(atomic::Ordering::Acquire);
},
_ => test_return!(),
}
- if nodes[0].get_and_clear_needs_persistence() == true {
+ if nodes[0].get_and_clear_needs_persistence() {
node_a_ser = nodes[0].encode();
}
- if nodes[1].get_and_clear_needs_persistence() == true {
+ if nodes[1].get_and_clear_needs_persistence() {
node_b_ser = nodes[1].encode();
}
- if nodes[2].get_and_clear_needs_persistence() == true {
+ if nodes[2].get_and_clear_needs_persistence() {
node_c_ser = nodes[2].encode();
}
}
Why this scored 14/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.