From f3fbd8cf6f3fa6b271153c23b1a1283b273473bf Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Sun, 8 Feb 2026 11:26:19 +0100 Subject: [PATCH 1/9] feat: Upgrade test for MonitorUpdatingPersister --- lightning/src/util/persist.rs | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/lightning/src/util/persist.rs b/lightning/src/util/persist.rs index 1b750c63cd8..510d4151735 100644 --- a/lightning/src/util/persist.rs +++ b/lightning/src/util/persist.rs @@ -1532,6 +1532,7 @@ impl From for UpdateName { #[cfg(test)] mod tests { use super::*; + use crate::chain::channelmonitor::ChannelMonitorUpdateStep; use crate::chain::ChannelMonitorUpdateStatus; use crate::check_closed_broadcast; use crate::events::ClosureReason; @@ -1937,6 +1938,130 @@ mod tests { .is_err()); } + // Test that a monitor with a legacy u64::MAX update_id (from pre-0.1 LDK) can still be read + // and applied correctly on startup. LDK prior to 0.1 used u64::MAX as a sentinel update_id + // for all ChannelMonitorUpdates generated after a channel was closed. We no longer generate + // these, but must still handle them for nodes upgrading from older versions. + #[test] + fn legacy_closed_channel_update_id_upgrade() { + let test_max_pending_updates = 7; + let chanmon_cfgs = create_chanmon_cfgs(3); + let kv_store_0 = TestStore::new(false); + let persister_0 = MonitorUpdatingPersister::new( + &kv_store_0, + &chanmon_cfgs[0].logger, + test_max_pending_updates, + &chanmon_cfgs[0].keys_manager, + &chanmon_cfgs[0].keys_manager, + &chanmon_cfgs[0].tx_broadcaster, + &chanmon_cfgs[0].fee_estimator, + ); + let kv_store_1 = TestStore::new(false); + let persister_1 = MonitorUpdatingPersister::new( + &kv_store_1, + &chanmon_cfgs[1].logger, + test_max_pending_updates, + &chanmon_cfgs[1].keys_manager, + &chanmon_cfgs[1].keys_manager, + &chanmon_cfgs[1].tx_broadcaster, + &chanmon_cfgs[1].fee_estimator, + ); + let mut node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let chain_mon_0 = test_utils::TestChainMonitor::new( + Some(&chanmon_cfgs[0].chain_source), + &chanmon_cfgs[0].tx_broadcaster, + &chanmon_cfgs[0].logger, + &chanmon_cfgs[0].fee_estimator, + &persister_0, + &chanmon_cfgs[0].keys_manager, + ); + let chain_mon_1 = test_utils::TestChainMonitor::new( + Some(&chanmon_cfgs[1].chain_source), + &chanmon_cfgs[1].tx_broadcaster, + &chanmon_cfgs[1].logger, + &chanmon_cfgs[1].fee_estimator, + &persister_1, + &chanmon_cfgs[1].keys_manager, + ); + node_cfgs[0].chain_monitor = chain_mon_0; + node_cfgs[1].chain_monitor = chain_mon_1; + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + // Create a channel and send a payment to build up real monitor data. + let _ = create_announced_chan_between_nodes(&nodes, 0, 1); + send_payment(&nodes[0], &vec![&nodes[1]][..], 8_000_000); + + // Read the current monitor state before we inject the legacy update. + let persisted_chan_data = persister_0.read_all_channel_monitors_with_updates().unwrap(); + assert_eq!(persisted_chan_data.len(), 1); + let (_, monitor) = &persisted_chan_data[0]; + let monitor_name = monitor.persistence_key(); + let pre_legacy_update_id = monitor.get_latest_update_id(); + assert!(pre_legacy_update_id < u64::MAX); + + // Construct a legacy ChannelForceClosed update with update_id = u64::MAX, simulating + // what a pre-0.1 LDK node would have written to the store after force-closing a channel. + let legacy_update = ChannelMonitorUpdate { + update_id: u64::MAX, + updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }], + channel_id: None, // Pre-0.0.121 updates had no channel_id + }; + KVStoreSync::write( + &kv_store_0, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + &monitor_name.to_string(), + UpdateName::from(u64::MAX).as_str(), + legacy_update.encode(), + ) + .unwrap(); + + // Verify the legacy update file is present in the store. + let updates_list = KVStoreSync::list( + &kv_store_0, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + &monitor_name.to_string(), + ) + .unwrap(); + assert!(updates_list.iter().any(|name| name == UpdateName::from(u64::MAX).as_str())); + + // Now read the monitors with updates. The legacy update should be found and applied. + let persisted_chan_data = persister_0.read_all_channel_monitors_with_updates().unwrap(); + assert_eq!(persisted_chan_data.len(), 1); + let (_, upgraded_monitor) = &persisted_chan_data[0]; + + // After applying the legacy force-close update, the monitor's latest_update_id should + // be u64::MAX. + assert_eq!(upgraded_monitor.get_latest_update_id(), u64::MAX); + + // Now simulate a full monitor re-persist (as would happen during normal operation after + // startup, e.g., when a new update triggers a full monitor write). Write the upgraded + // monitor back to the store so the on-disk state reflects the applied legacy update. + KVStoreSync::write( + &kv_store_0, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_name.to_string(), + upgraded_monitor.encode(), + ) + .unwrap(); + + // Now cleanup_stale_updates should remove the legacy update, since the persisted + // monitor's latest_update_id (u64::MAX) >= the update's id (u64::MAX). + persister_0.cleanup_stale_updates(false).unwrap(); + + let updates_list = KVStoreSync::list( + &kv_store_0, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + &monitor_name.to_string(), + ) + .unwrap(); + assert!( + updates_list.is_empty(), + "All updates including the legacy u64::MAX update should have been cleaned up" + ); + } + fn persist_fn(_persist: P) -> bool where P::Target: Persist, From 2a07f69f43cec52d81780c00f21148896b16b726 Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 12 Feb 2026 13:14:48 +0100 Subject: [PATCH 2/9] refactor: move u64::MAX update test to lightning-tests with proper upgrade pattern --- .../src/upgrade_downgrade_tests.rs | 49 +++++++ lightning/src/util/persist.rs | 125 ------------------ 2 files changed, 49 insertions(+), 125 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 14b0a5c5822..9e6d638acba 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -699,3 +699,52 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) { expect_payment_claimable!(nodes[2], pay_hash, pay_secret, 1_000_000); claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], pay_preimage); } + +#[test] +fn test_0_0_125_max_update_id_upgrade() { + let (node_a_ser, node_b_ser, mon_a_ser, mon_b_ser); + { + let chanmon_cfgs = lightning_0_0_125_utils::create_chanmon_cfgs(2); + let node_cfgs = lightning_0_0_125_utils::create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = + lightning_0_0_125_utils::create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let nodes = lightning_0_0_125_utils::create_network(2, &node_cfgs, &node_chanmgrs); + + let node_a_id = nodes[0].node.get_our_node_id(); + let chan_id = lightning_0_0_125_utils::create_announced_chan_between_nodes(&nodes, 0, 1).2; + + lightning_0_0_125_utils::route_payment(&nodes[0], &[&nodes[1]], 1_000_000); + + let err = "".to_owned(); + nodes[1] + .node + .force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err) + .unwrap(); + + lightning_0_0_125_utils::check_added_monitors(&nodes[1], 1); + let reason = ClosureReason_0_0_125::HolderForceClosed { broadcasted_latest_txn: Some(true) }; + lightning_0_0_125_utils::check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 100000); + lightning_0_0_125_utils::check_closed_broadcast(&nodes[1], 1, true); + + node_a_ser = nodes[0].node.encode(); + node_b_ser = nodes[1].node.encode(); + mon_a_ser = get_monitor_0_0_125!(nodes[0], chan_id).encode(); + mon_b_ser = get_monitor_0_0_125!(nodes[1], chan_id).encode(); + } + + let mut chanmon_cfgs = create_chanmon_cfgs(2); + + chanmon_cfgs[0].keys_manager.disable_all_state_policy_checks = true; + chanmon_cfgs[1].keys_manager.disable_all_state_policy_checks = true; + + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let (persister_a, persister_b, chain_mon_a, chain_mon_b); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let (node_a, node_b); + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + let config = test_default_channel_config(); + let a_mons = &[&mon_a_ser[..]]; + reload_node!(nodes[0], config.clone(), &node_a_ser, a_mons, persister_a, chain_mon_a, node_a); + reload_node!(nodes[1], config, &node_b_ser, &[&mon_b_ser], persister_b, chain_mon_b, node_b); +} diff --git a/lightning/src/util/persist.rs b/lightning/src/util/persist.rs index 510d4151735..1b750c63cd8 100644 --- a/lightning/src/util/persist.rs +++ b/lightning/src/util/persist.rs @@ -1532,7 +1532,6 @@ impl From for UpdateName { #[cfg(test)] mod tests { use super::*; - use crate::chain::channelmonitor::ChannelMonitorUpdateStep; use crate::chain::ChannelMonitorUpdateStatus; use crate::check_closed_broadcast; use crate::events::ClosureReason; @@ -1938,130 +1937,6 @@ mod tests { .is_err()); } - // Test that a monitor with a legacy u64::MAX update_id (from pre-0.1 LDK) can still be read - // and applied correctly on startup. LDK prior to 0.1 used u64::MAX as a sentinel update_id - // for all ChannelMonitorUpdates generated after a channel was closed. We no longer generate - // these, but must still handle them for nodes upgrading from older versions. - #[test] - fn legacy_closed_channel_update_id_upgrade() { - let test_max_pending_updates = 7; - let chanmon_cfgs = create_chanmon_cfgs(3); - let kv_store_0 = TestStore::new(false); - let persister_0 = MonitorUpdatingPersister::new( - &kv_store_0, - &chanmon_cfgs[0].logger, - test_max_pending_updates, - &chanmon_cfgs[0].keys_manager, - &chanmon_cfgs[0].keys_manager, - &chanmon_cfgs[0].tx_broadcaster, - &chanmon_cfgs[0].fee_estimator, - ); - let kv_store_1 = TestStore::new(false); - let persister_1 = MonitorUpdatingPersister::new( - &kv_store_1, - &chanmon_cfgs[1].logger, - test_max_pending_updates, - &chanmon_cfgs[1].keys_manager, - &chanmon_cfgs[1].keys_manager, - &chanmon_cfgs[1].tx_broadcaster, - &chanmon_cfgs[1].fee_estimator, - ); - let mut node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let chain_mon_0 = test_utils::TestChainMonitor::new( - Some(&chanmon_cfgs[0].chain_source), - &chanmon_cfgs[0].tx_broadcaster, - &chanmon_cfgs[0].logger, - &chanmon_cfgs[0].fee_estimator, - &persister_0, - &chanmon_cfgs[0].keys_manager, - ); - let chain_mon_1 = test_utils::TestChainMonitor::new( - Some(&chanmon_cfgs[1].chain_source), - &chanmon_cfgs[1].tx_broadcaster, - &chanmon_cfgs[1].logger, - &chanmon_cfgs[1].fee_estimator, - &persister_1, - &chanmon_cfgs[1].keys_manager, - ); - node_cfgs[0].chain_monitor = chain_mon_0; - node_cfgs[1].chain_monitor = chain_mon_1; - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - // Create a channel and send a payment to build up real monitor data. - let _ = create_announced_chan_between_nodes(&nodes, 0, 1); - send_payment(&nodes[0], &vec![&nodes[1]][..], 8_000_000); - - // Read the current monitor state before we inject the legacy update. - let persisted_chan_data = persister_0.read_all_channel_monitors_with_updates().unwrap(); - assert_eq!(persisted_chan_data.len(), 1); - let (_, monitor) = &persisted_chan_data[0]; - let monitor_name = monitor.persistence_key(); - let pre_legacy_update_id = monitor.get_latest_update_id(); - assert!(pre_legacy_update_id < u64::MAX); - - // Construct a legacy ChannelForceClosed update with update_id = u64::MAX, simulating - // what a pre-0.1 LDK node would have written to the store after force-closing a channel. - let legacy_update = ChannelMonitorUpdate { - update_id: u64::MAX, - updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast: true }], - channel_id: None, // Pre-0.0.121 updates had no channel_id - }; - KVStoreSync::write( - &kv_store_0, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, - &monitor_name.to_string(), - UpdateName::from(u64::MAX).as_str(), - legacy_update.encode(), - ) - .unwrap(); - - // Verify the legacy update file is present in the store. - let updates_list = KVStoreSync::list( - &kv_store_0, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, - &monitor_name.to_string(), - ) - .unwrap(); - assert!(updates_list.iter().any(|name| name == UpdateName::from(u64::MAX).as_str())); - - // Now read the monitors with updates. The legacy update should be found and applied. - let persisted_chan_data = persister_0.read_all_channel_monitors_with_updates().unwrap(); - assert_eq!(persisted_chan_data.len(), 1); - let (_, upgraded_monitor) = &persisted_chan_data[0]; - - // After applying the legacy force-close update, the monitor's latest_update_id should - // be u64::MAX. - assert_eq!(upgraded_monitor.get_latest_update_id(), u64::MAX); - - // Now simulate a full monitor re-persist (as would happen during normal operation after - // startup, e.g., when a new update triggers a full monitor write). Write the upgraded - // monitor back to the store so the on-disk state reflects the applied legacy update. - KVStoreSync::write( - &kv_store_0, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &monitor_name.to_string(), - upgraded_monitor.encode(), - ) - .unwrap(); - - // Now cleanup_stale_updates should remove the legacy update, since the persisted - // monitor's latest_update_id (u64::MAX) >= the update's id (u64::MAX). - persister_0.cleanup_stale_updates(false).unwrap(); - - let updates_list = KVStoreSync::list( - &kv_store_0, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, - &monitor_name.to_string(), - ) - .unwrap(); - assert!( - updates_list.is_empty(), - "All updates including the legacy u64::MAX update should have been cleaned up" - ); - } - fn persist_fn(_persist: P) -> bool where P::Target: Persist, From 79e3ee09e634516349e76e2484bebf0e72e1340d Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 12 Feb 2026 13:37:02 +0100 Subject: [PATCH 3/9] fix: ci issues --- lightning-tests/src/upgrade_downgrade_tests.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 9e6d638acba..f655b73ad64 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -716,14 +716,19 @@ fn test_0_0_125_max_update_id_upgrade() { lightning_0_0_125_utils::route_payment(&nodes[0], &[&nodes[1]], 1_000_000); let err = "".to_owned(); - nodes[1] - .node - .force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err) - .unwrap(); + nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err).unwrap(); lightning_0_0_125_utils::check_added_monitors(&nodes[1], 1); - let reason = ClosureReason_0_0_125::HolderForceClosed { broadcasted_latest_txn: Some(true) }; - lightning_0_0_125_utils::check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 100000); + let reason = + ClosureReason_0_0_125::HolderForceClosed { broadcasted_latest_txn: Some(true) }; + lightning_0_0_125_utils::check_closed_event( + &nodes[1], + 1, + reason, + false, + &[node_a_id], + 100000, + ); lightning_0_0_125_utils::check_closed_broadcast(&nodes[1], 1, true); node_a_ser = nodes[0].node.encode(); From bacbf9f80dd72e54504022354c92ce082776d3e1 Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 5 Mar 2026 06:50:38 +0100 Subject: [PATCH 4/9] fix: review improvements implemented --- .../src/upgrade_downgrade_tests.rs | 119 +++++++++++++++--- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 4194607023d..60202a4bd68 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -704,7 +704,18 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) { #[test] fn test_0_0_125_max_update_id_upgrade() { - let (node_a_ser, node_b_ser, mon_a_ser, mon_b_ser); + use lightning::chain::chainmonitor::Persist; + use lightning::util::ser::ReadableArgs; + use lightning::util::persist::{ + KVStoreSync, MonitorUpdatingPersister, CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL, + }; + use lightning::util::test_utils::TestStore; + + // Phase 1: Create old LDK state with u64::MAX update IDs via force-close. + let mon_b_ser; { let chanmon_cfgs = lightning_0_0_125_utils::create_chanmon_cfgs(2); let node_cfgs = lightning_0_0_125_utils::create_node_cfgs(2, &chanmon_cfgs); @@ -733,25 +744,97 @@ fn test_0_0_125_max_update_id_upgrade() { ); lightning_0_0_125_utils::check_closed_broadcast(&nodes[1], 1, true); - node_a_ser = nodes[0].node.encode(); - node_b_ser = nodes[1].node.encode(); - mon_a_ser = get_monitor_0_0_125!(nodes[0], chan_id).encode(); mon_b_ser = get_monitor_0_0_125!(nodes[1], chan_id).encode(); } - let mut chanmon_cfgs = create_chanmon_cfgs(2); - - chanmon_cfgs[0].keys_manager.disable_all_state_policy_checks = true; - chanmon_cfgs[1].keys_manager.disable_all_state_policy_checks = true; - - let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - let (persister_a, persister_b, chain_mon_a, chain_mon_b); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); - let (node_a, node_b); - let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); + // Phase 2: Pre-seed a TestStore with old monitor data (simulating an existing KV store + // from a pre-0.1 LDK install), then verify MonitorUpdatingPersister handles it correctly. + let chanmon_cfgs = create_chanmon_cfgs(2); - let config = test_default_channel_config(); - let a_mons = &[&mon_a_ser[..]]; - reload_node!(nodes[0], config.clone(), &node_a_ser, a_mons, persister_a, chain_mon_a, node_a); - reload_node!(nodes[1], config, &node_b_ser, &[&mon_b_ser], persister_b, chain_mon_b, node_b); + let kv_store = TestStore::new(false); + let max_pending_updates = 5; + let persister = MonitorUpdatingPersister::new( + &kv_store, + &chanmon_cfgs[1].logger, + max_pending_updates, + &chanmon_cfgs[1].keys_manager, + &chanmon_cfgs[1].keys_manager, + &chanmon_cfgs[1].tx_broadcaster, + &chanmon_cfgs[1].fee_estimator, + ); + + // Deserialize node_b's monitor to get its persistence key, then write the raw bytes + // into the store (without the sentinel prefix, as old KVStoreSync-based persist would). + let (_, mon_b) = <( + bitcoin::BlockHash, + lightning::chain::channelmonitor::ChannelMonitor< + lightning::util::test_channel_signer::TestChannelSigner, + >, + )>::read( + &mut &mon_b_ser[..], + (&chanmon_cfgs[1].keys_manager, &chanmon_cfgs[1].keys_manager), + ) + .unwrap(); + let monitor_key = mon_b.persistence_key().to_string(); + assert_eq!(mon_b.get_latest_update_id(), u64::MAX); + + KVStoreSync::write( + &kv_store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + mon_b_ser.clone(), + ) + .unwrap(); + + // Phase 3: Verify MonitorUpdatingPersister can read the old monitor with u64::MAX update ID. + let mons = persister.read_all_channel_monitors_with_updates().unwrap(); + assert_eq!(mons.len(), 1); + assert_eq!(mons[0].1.get_latest_update_id(), u64::MAX); + + // Verify no incremental update files exist yet. + let updates = KVStoreSync::list( + &kv_store, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + &monitor_key, + ) + .unwrap(); + assert!(updates.is_empty()); + + // Phase 4: Verify that persisting a u64::MAX monitor through MonitorUpdatingPersister + // writes a full monitor (not an incremental update). + let persist_res = + persister.persist_new_channel(mon_b.persistence_key(), &mons[0].1); + assert_eq!(persist_res, lightning::chain::ChannelMonitorUpdateStatus::Completed); + + // The full monitor should now be stored with the sentinel prefix. + let stored_bytes = KVStoreSync::read( + &kv_store, + CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, + CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, + &monitor_key, + ) + .unwrap(); + assert!( + stored_bytes.starts_with(MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL), + "Expected sentinel prefix on re-persisted monitor" + ); + + // Re-read after persist to confirm round-trip works with sentinel prefix. + let mons_after = persister.read_all_channel_monitors_with_updates().unwrap(); + assert_eq!(mons_after.len(), 1); + assert_eq!(mons_after[0].1.get_latest_update_id(), u64::MAX); + + // Still no incremental updates should exist for a u64::MAX monitor. + let updates_after = KVStoreSync::list( + &kv_store, + CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, + &monitor_key, + ) + .unwrap(); + assert!( + updates_after.is_empty(), + "Expected no incremental updates for u64::MAX monitor, found {}", + updates_after.len() + ); } From 0a1ac3b7015dd6587cdbac09ed6bcd9a0e36d022 Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 5 Mar 2026 07:13:15 +0100 Subject: [PATCH 5/9] fmt: fix rustfmt nightly formatting for persist_new_channel call --- lightning-tests/src/upgrade_downgrade_tests.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 60202a4bd68..eda85e7b526 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -803,8 +803,7 @@ fn test_0_0_125_max_update_id_upgrade() { // Phase 4: Verify that persisting a u64::MAX monitor through MonitorUpdatingPersister // writes a full monitor (not an incremental update). - let persist_res = - persister.persist_new_channel(mon_b.persistence_key(), &mons[0].1); + let persist_res = persister.persist_new_channel(mon_b.persistence_key(), &mons[0].1); assert_eq!(persist_res, lightning::chain::ChannelMonitorUpdateStatus::Completed); // The full monitor should now be stored with the sentinel prefix. From 628e64d2e20b75cd4556ef32193d0ba7c87f77cf Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 5 Mar 2026 07:20:57 +0100 Subject: [PATCH 6/9] fmt: run cargo +nightly fmt --all to fix CI rustfmt check --- .../src/upgrade_downgrade_tests.rs | 2 +- lightning/src/ln/channelmanager.rs | 6 ++-- lightning/src/ln/functional_tests.rs | 4 +-- lightning/src/ln/funding.rs | 30 +++++++++++++++++-- lightning/src/ln/htlc_reserve_unit_tests.rs | 3 +- lightning/src/ln/msgs.rs | 9 ++++-- 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index eda85e7b526..9a36e5fbcce 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -705,13 +705,13 @@ fn do_upgrade_mid_htlc_forward(test: MidHtlcForwardCase) { #[test] fn test_0_0_125_max_update_id_upgrade() { use lightning::chain::chainmonitor::Persist; - use lightning::util::ser::ReadableArgs; use lightning::util::persist::{ KVStoreSync, MonitorUpdatingPersister, CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL, }; + use lightning::util::ser::ReadableArgs; use lightning::util::test_utils::TestStore; // Phase 1: Create old LDK state with u64::MAX update IDs via force-close. diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index ada27af749f..943bbd3fd8e 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -9268,7 +9268,8 @@ impl< ComplFunc: FnOnce( Option, bool, - ) -> (Option, Option), + ) + -> (Option, Option), >( &self, prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage, payment_info: Option, attribution_data: Option, @@ -9306,7 +9307,8 @@ impl< ComplFunc: FnOnce( Option, bool, - ) -> (Option, Option), + ) + -> (Option, Option), >( &self, prev_hop: HTLCClaimSource, payment_preimage: PaymentPreimage, payment_info: Option, attribution_data: Option, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 09a87d93156..448f0957e7e 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -856,7 +856,7 @@ pub fn test_justice_tx_htlc_timeout() { revoked_local_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT ); // HTLC-Timeout - // Revoke the old state + // Revoke the old state claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_3); { @@ -6153,7 +6153,7 @@ pub fn test_announce_disable_channels() { match e { MessageSendEvent::BroadcastChannelUpdate { ref msg, .. } => { assert_eq!(msg.contents.channel_flags & (1 << 1), 1 << 1); // The "channel disabled" bit should be set - // Check that each channel gets updated exactly once + // Check that each channel gets updated exactly once if chans_disabled .insert(msg.contents.short_channel_id, msg.contents.timestamp) .is_some() diff --git a/lightning/src/ln/funding.rs b/lightning/src/ln/funding.rs index 84c9d4dd343..d703b003932 100644 --- a/lightning/src/ln/funding.rs +++ b/lightning/src/ln/funding.rs @@ -220,7 +220,15 @@ impl FundingTemplate { return Err(()); } let FundingTemplate { shared_input, min_feerate, max_feerate } = self; - build_funding_contribution!(value_added, vec![], shared_input, min_feerate, max_feerate, wallet, await) + build_funding_contribution!( + value_added, + vec![], + shared_input, + min_feerate, + max_feerate, + wallet, + await + ) } /// Creates a [`FundingContribution`] for adding funds to a channel using `wallet` to perform @@ -251,7 +259,15 @@ impl FundingTemplate { return Err(()); } let FundingTemplate { shared_input, min_feerate, max_feerate } = self; - build_funding_contribution!(Amount::ZERO, outputs, shared_input, min_feerate, max_feerate, wallet, await) + build_funding_contribution!( + Amount::ZERO, + outputs, + shared_input, + min_feerate, + max_feerate, + wallet, + await + ) } /// Creates a [`FundingContribution`] for removing funds from a channel using `wallet` to @@ -282,7 +298,15 @@ impl FundingTemplate { return Err(()); } let FundingTemplate { shared_input, min_feerate, max_feerate } = self; - build_funding_contribution!(value_added, outputs, shared_input, min_feerate, max_feerate, wallet, await) + build_funding_contribution!( + value_added, + outputs, + shared_input, + min_feerate, + max_feerate, + wallet, + await + ) } /// Creates a [`FundingContribution`] for both adding and removing funds from a channel using diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index d88b9a2dc3f..d196d3bd77c 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -288,7 +288,8 @@ pub fn test_channel_reserve_holding_cell_htlcs() { stat.value_to_self_msat - (stat.pending_outbound_htlcs_amount_msat + recv_value_21 + recv_value_22 - + total_fee_msat + total_fee_msat + + total_fee_msat + + total_fee_msat + commit_tx_fee_3_htlcs), stat.channel_reserve_msat ); diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index ac549ddd50c..68c035a6cac 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -3942,7 +3942,8 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPaylo used_aad, } => { if amt.is_some() - || cltv_value.is_some() || total_msat.is_some() + || cltv_value.is_some() + || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad != TriPolyAADUsed::None @@ -3964,7 +3965,8 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPaylo used_aad, } => { if amt.is_some() - || cltv_value.is_some() || total_msat.is_some() + || cltv_value.is_some() + || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad == TriPolyAADUsed::None @@ -4112,7 +4114,8 @@ impl ReadableArgs<(Option, NS)> for InboundTrampoline used_aad, } => { if amt.is_some() - || cltv_value.is_some() || total_msat.is_some() + || cltv_value.is_some() + || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad != TriPolyAADUsed::None From ab38d381aa75cd5a92d39d3db936b1120ffb1a97 Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Thu, 5 Mar 2026 07:33:03 +0100 Subject: [PATCH 7/9] fmt: fix formatting with cargo +1.75.0 fmt to match CI The previous commit used nightly rustfmt which formats differently from the 1.75.0 rustfmt that CI actually uses. --- lightning/src/ln/channelmanager.rs | 6 ++---- lightning/src/ln/functional_tests.rs | 4 ++-- lightning/src/ln/htlc_reserve_unit_tests.rs | 3 +-- lightning/src/ln/msgs.rs | 9 +++------ 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 943bbd3fd8e..ada27af749f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -9268,8 +9268,7 @@ impl< ComplFunc: FnOnce( Option, bool, - ) - -> (Option, Option), + ) -> (Option, Option), >( &self, prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage, payment_info: Option, attribution_data: Option, @@ -9307,8 +9306,7 @@ impl< ComplFunc: FnOnce( Option, bool, - ) - -> (Option, Option), + ) -> (Option, Option), >( &self, prev_hop: HTLCClaimSource, payment_preimage: PaymentPreimage, payment_info: Option, attribution_data: Option, diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 448f0957e7e..09a87d93156 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -856,7 +856,7 @@ pub fn test_justice_tx_htlc_timeout() { revoked_local_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT ); // HTLC-Timeout - // Revoke the old state + // Revoke the old state claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_3); { @@ -6153,7 +6153,7 @@ pub fn test_announce_disable_channels() { match e { MessageSendEvent::BroadcastChannelUpdate { ref msg, .. } => { assert_eq!(msg.contents.channel_flags & (1 << 1), 1 << 1); // The "channel disabled" bit should be set - // Check that each channel gets updated exactly once + // Check that each channel gets updated exactly once if chans_disabled .insert(msg.contents.short_channel_id, msg.contents.timestamp) .is_some() diff --git a/lightning/src/ln/htlc_reserve_unit_tests.rs b/lightning/src/ln/htlc_reserve_unit_tests.rs index d196d3bd77c..d88b9a2dc3f 100644 --- a/lightning/src/ln/htlc_reserve_unit_tests.rs +++ b/lightning/src/ln/htlc_reserve_unit_tests.rs @@ -288,8 +288,7 @@ pub fn test_channel_reserve_holding_cell_htlcs() { stat.value_to_self_msat - (stat.pending_outbound_htlcs_amount_msat + recv_value_21 + recv_value_22 - + total_fee_msat - + total_fee_msat + + total_fee_msat + total_fee_msat + commit_tx_fee_3_htlcs), stat.channel_reserve_msat ); diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 68c035a6cac..ac549ddd50c 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -3942,8 +3942,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPaylo used_aad, } => { if amt.is_some() - || cltv_value.is_some() - || total_msat.is_some() + || cltv_value.is_some() || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad != TriPolyAADUsed::None @@ -3965,8 +3964,7 @@ impl ReadableArgs<(Option, NS)> for InboundOnionPaylo used_aad, } => { if amt.is_some() - || cltv_value.is_some() - || total_msat.is_some() + || cltv_value.is_some() || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad == TriPolyAADUsed::None @@ -4114,8 +4112,7 @@ impl ReadableArgs<(Option, NS)> for InboundTrampoline used_aad, } => { if amt.is_some() - || cltv_value.is_some() - || total_msat.is_some() + || cltv_value.is_some() || total_msat.is_some() || keysend_preimage.is_some() || invoice_request.is_some() || used_aad != TriPolyAADUsed::None From c3036b58806ad632bb1d0fb62cc2b6827ddcebac Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Fri, 6 Mar 2026 03:51:22 +0100 Subject: [PATCH 8/9] fix: rewrite upgrade test to use async monitor flow and MonitorUpdatingPersister Addresses reviewer feedback on PR #4397: - Use InProgress persist in the 0.0.125 phase to simulate the async monitor updating flow with a pending u64::MAX monitor update - Reload the ChannelManager from serialized state using a TestChainMonitor backed by MonitorUpdatingPersister - Verify the monitor is persisted through MonitorUpdatingPersister with the sentinel prefix and can be read back correctly --- .../src/upgrade_downgrade_tests.rs | 145 ++++++++---------- 1 file changed, 67 insertions(+), 78 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index 9a36e5fbcce..e265e1eb825 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -708,14 +708,14 @@ fn test_0_0_125_max_update_id_upgrade() { use lightning::util::persist::{ KVStoreSync, MonitorUpdatingPersister, CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL, }; - use lightning::util::ser::ReadableArgs; use lightning::util::test_utils::TestStore; - // Phase 1: Create old LDK state with u64::MAX update IDs via force-close. - let mon_b_ser; + // Phase 1: Create old LDK state with a pending u64::MAX monitor update via force-close. + // We use InProgress persist to simulate the async monitor updating flow, so the u64::MAX + // monitor update is still pending (in-flight) when we serialize. + let (node_b_ser, mon_b_ser, chan_id_bytes); { let chanmon_cfgs = lightning_0_0_125_utils::create_chanmon_cfgs(2); let node_cfgs = lightning_0_0_125_utils::create_node_cfgs(2, &chanmon_cfgs); @@ -725,13 +725,19 @@ fn test_0_0_125_max_update_id_upgrade() { let node_a_id = nodes[0].node.get_our_node_id(); let chan_id = lightning_0_0_125_utils::create_announced_chan_between_nodes(&nodes, 0, 1).2; + chan_id_bytes = chan_id.0; lightning_0_0_125_utils::route_payment(&nodes[0], &[&nodes[1]], 1_000_000); + // Set persist to InProgress before force-close so the u64::MAX update remains pending. + chanmon_cfgs[1] + .persister + .set_update_ret(ChannelMonitorUpdateStatus_0_0_125::InProgress); + let err = "".to_owned(); nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err).unwrap(); - lightning_0_0_125_utils::check_added_monitors(&nodes[1], 1); + check_added_monitors_0_0_125!(nodes[1], 1); let reason = ClosureReason_0_0_125::HolderForceClosed { broadcasted_latest_txn: Some(true) }; lightning_0_0_125_utils::check_closed_event( @@ -744,69 +750,68 @@ fn test_0_0_125_max_update_id_upgrade() { ); lightning_0_0_125_utils::check_closed_broadcast(&nodes[1], 1, true); + // Serialize while the u64::MAX update is still InProgress (pending in-flight). + node_b_ser = nodes[1].node.encode(); mon_b_ser = get_monitor_0_0_125!(nodes[1], chan_id).encode(); } - // Phase 2: Pre-seed a TestStore with old monitor data (simulating an existing KV store - // from a pre-0.1 LDK install), then verify MonitorUpdatingPersister handles it correctly. - let chanmon_cfgs = create_chanmon_cfgs(2); - - let kv_store = TestStore::new(false); - let max_pending_updates = 5; - let persister = MonitorUpdatingPersister::new( - &kv_store, - &chanmon_cfgs[1].logger, - max_pending_updates, - &chanmon_cfgs[1].keys_manager, - &chanmon_cfgs[1].keys_manager, - &chanmon_cfgs[1].tx_broadcaster, - &chanmon_cfgs[1].fee_estimator, - ); + // Phase 2: Reload with current LDK using MonitorUpdatingPersister as the persist backend + // for the ChainMonitor. This verifies that the ChannelManager can load old state with a + // pending u64::MAX monitor update and that MonitorUpdatingPersister handles it correctly. + let mut chanmon_cfgs = create_chanmon_cfgs(2); + chanmon_cfgs[1].keys_manager.disable_all_state_policy_checks = true; - // Deserialize node_b's monitor to get its persistence key, then write the raw bytes - // into the store (without the sentinel prefix, as old KVStoreSync-based persist would). - let (_, mon_b) = <( - bitcoin::BlockHash, - lightning::chain::channelmonitor::ChannelMonitor< - lightning::util::test_channel_signer::TestChannelSigner, - >, - )>::read( - &mut &mon_b_ser[..], - (&chanmon_cfgs[1].keys_manager, &chanmon_cfgs[1].keys_manager), - ) - .unwrap(); - let monitor_key = mon_b.persistence_key().to_string(); - assert_eq!(mon_b.get_latest_update_id(), u64::MAX); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); - KVStoreSync::write( - &kv_store, - CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, - CHANNEL_MONITOR_PERSISTENCE_SECONDARY_NAMESPACE, - &monitor_key, - mon_b_ser.clone(), - ) - .unwrap(); + // Declare before `nodes` so they outlive it (variables are dropped in reverse order). + let kv_store; + let mon_updating_persister; + let chain_mon_b; - // Phase 3: Verify MonitorUpdatingPersister can read the old monitor with u64::MAX update ID. - let mons = persister.read_all_channel_monitors_with_updates().unwrap(); - assert_eq!(mons.len(), 1); - assert_eq!(mons[0].1.get_latest_update_id(), u64::MAX); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); + let node_b; + let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs); - // Verify no incremental update files exist yet. - let updates = KVStoreSync::list( + // Create a MonitorUpdatingPersister backed by TestStore, and a TestChainMonitor using it. + kv_store = TestStore::new(false); + mon_updating_persister = MonitorUpdatingPersister::new( &kv_store, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, - &monitor_key, - ) - .unwrap(); - assert!(updates.is_empty()); + nodes[1].logger, + 5, + nodes[1].keys_manager, + nodes[1].keys_manager, + nodes[1].tx_broadcaster, + nodes[1].fee_estimator, + ); + chain_mon_b = lightning::util::test_utils::TestChainMonitor::new( + Some(nodes[1].chain_source), + nodes[1].tx_broadcaster, + nodes[1].logger, + nodes[1].fee_estimator, + &mon_updating_persister, + nodes[1].keys_manager, + ); + nodes[1].chain_monitor = &chain_mon_b; - // Phase 4: Verify that persisting a u64::MAX monitor through MonitorUpdatingPersister - // writes a full monitor (not an incremental update). - let persist_res = persister.persist_new_channel(mon_b.persistence_key(), &mons[0].1); + // Reload the ChannelManager from serialized state. _reload_node deserializes the + // ChannelManager and monitors, and loads them into the ChainMonitor. + let config = test_default_channel_config(); + node_b = _reload_node(&nodes[1], config, &node_b_ser, &[&mon_b_ser], None); + nodes[1].node = &node_b; + nodes[1].onion_messenger.set_offers_handler(&node_b); + nodes[1].onion_messenger.set_async_payments_handler(&node_b); + + // Verify the monitor was loaded with the u64::MAX update ID. + let channel_id = ChannelId(chan_id_bytes); + let mon = get_monitor!(nodes[1], channel_id); + assert_eq!(mon.get_latest_update_id(), u64::MAX); + + // Persist the monitor through MonitorUpdatingPersister and verify it writes + // the full monitor with the sentinel prefix (not an incremental update). + let monitor_key = mon.persistence_key().to_string(); + let persist_res = mon_updating_persister.persist_new_channel(mon.persistence_key(), &mon); assert_eq!(persist_res, lightning::chain::ChannelMonitorUpdateStatus::Completed); - // The full monitor should now be stored with the sentinel prefix. let stored_bytes = KVStoreSync::read( &kv_store, CHANNEL_MONITOR_PERSISTENCE_PRIMARY_NAMESPACE, @@ -814,26 +819,10 @@ fn test_0_0_125_max_update_id_upgrade() { &monitor_key, ) .unwrap(); - assert!( - stored_bytes.starts_with(MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL), - "Expected sentinel prefix on re-persisted monitor" - ); - - // Re-read after persist to confirm round-trip works with sentinel prefix. - let mons_after = persister.read_all_channel_monitors_with_updates().unwrap(); - assert_eq!(mons_after.len(), 1); - assert_eq!(mons_after[0].1.get_latest_update_id(), u64::MAX); + assert!(stored_bytes.starts_with(MONITOR_UPDATING_PERSISTER_PREPEND_SENTINEL)); - // Still no incremental updates should exist for a u64::MAX monitor. - let updates_after = KVStoreSync::list( - &kv_store, - CHANNEL_MONITOR_UPDATE_PERSISTENCE_PRIMARY_NAMESPACE, - &monitor_key, - ) - .unwrap(); - assert!( - updates_after.is_empty(), - "Expected no incremental updates for u64::MAX monitor, found {}", - updates_after.len() - ); + // Verify MonitorUpdatingPersister can read the persisted monitor back correctly. + let mons = mon_updating_persister.read_all_channel_monitors_with_updates().unwrap(); + assert_eq!(mons.len(), 1); + assert_eq!(mons[0].1.get_latest_update_id(), u64::MAX); } From aafaf6bc185250d9cf44a2c575f2a53cd37e229e Mon Sep 17 00:00:00 2001 From: okekefrancis112 Date: Fri, 6 Mar 2026 10:33:13 +0100 Subject: [PATCH 9/9] fmt: fix lightning-tests rustfmt (cd into dir vs workspace root) --- lightning-tests/src/upgrade_downgrade_tests.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lightning-tests/src/upgrade_downgrade_tests.rs b/lightning-tests/src/upgrade_downgrade_tests.rs index e265e1eb825..604826d2a39 100644 --- a/lightning-tests/src/upgrade_downgrade_tests.rs +++ b/lightning-tests/src/upgrade_downgrade_tests.rs @@ -730,9 +730,7 @@ fn test_0_0_125_max_update_id_upgrade() { lightning_0_0_125_utils::route_payment(&nodes[0], &[&nodes[1]], 1_000_000); // Set persist to InProgress before force-close so the u64::MAX update remains pending. - chanmon_cfgs[1] - .persister - .set_update_ret(ChannelMonitorUpdateStatus_0_0_125::InProgress); + chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus_0_0_125::InProgress); let err = "".to_owned(); nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err).unwrap();