Skip to content

Commit 0695da9

Browse files
authored
Merge pull request #4389 from TheBlueMatt/2026-02-0.2.2
Cut 0.2.2
2 parents c7c955c + df44c3b commit 0695da9

File tree

9 files changed

+225
-93
lines changed

9 files changed

+225
-93
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
# 0.2.2 - Feb 6, 2025 - "An Async Splicing Production"
2+
3+
## API Updates
4+
* The `SplicePrototype` feature flag has been updated to refer to feature bit
5+
63 - the same as `SpliceProduction`. This resolves a compatibility issue with
6+
eclair nodes due to the use of the same splicing feature flag (155) they were
7+
using for a pre-standardization version of splicing (#4387).
8+
9+
## Bug Fixes
10+
* Async `ChannelMonitorUpdate` persistence operations which complete, but are
11+
not marked as complete in a persisted `ChannelManager` prior to restart,
12+
followed immediately by a block connection and then another restart could
13+
result in some channel operations hanging leading for force-closures (#4377).
14+
* A debug assertion failure reachable when receiving an invalid splicing
15+
message from a peer was fixed (#4383).
16+
17+
118
# 0.2.1 - Jan 29, 2025 - "Electrum Confirmations Logged"
219

320
## API Updates

lightning-types/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "lightning-types"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
authors = ["Matt Corallo"]
55
license = "MIT OR Apache-2.0"
66
repository = "https://github.com/lightningdevkit/rust-lightning/"

lightning-types/src/features.rs

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,15 @@ mod sealed {
166166
// Byte 6
167167
ZeroConf,
168168
// Byte 7
169-
Trampoline | SimpleClose | SpliceProduction,
169+
Trampoline | SimpleClose | SpliceProduction | SplicePrototype,
170170
// Byte 8 - 16
171171
,,,,,,,,,
172172
// Byte 17
173173
AnchorZeroFeeCommitmentsStaging,
174174
// Byte 18
175175
,
176176
// Byte 19
177-
HtlcHold | SplicePrototype,
177+
HtlcHold,
178178
]
179179
);
180180
define_context!(
@@ -195,15 +195,15 @@ mod sealed {
195195
// Byte 6
196196
ZeroConf | Keysend,
197197
// Byte 7
198-
Trampoline | SimpleClose | SpliceProduction,
198+
Trampoline | SimpleClose | SpliceProduction | SplicePrototype,
199199
// Byte 8 - 16
200200
,,,,,,,,,
201201
// Byte 17
202202
AnchorZeroFeeCommitmentsStaging,
203203
// Byte 18
204204
,
205205
// Byte 19
206-
HtlcHold | SplicePrototype,
206+
HtlcHold,
207207
// Byte 20 - 31
208208
,,,,,,,,,,,,
209209
// Byte 32
@@ -722,7 +722,7 @@ mod sealed {
722722
requires_htlc_hold
723723
);
724724
define_feature!(
725-
155, // Splice prototype feature bit as listed in https://github.com/lightning/bolts/issues/605#issuecomment-877237519.
725+
63, // Actually the SpliceProduction feature
726726
SplicePrototype,
727727
[InitContext, NodeContext],
728728
"Feature flags for channel splicing.",
@@ -1441,28 +1441,16 @@ mod tests {
14411441
// - onion_messages
14421442
// - option_channel_type | option_scid_alias
14431443
// - option_zeroconf
1444-
// - option_simple_close | option_splice
1445-
assert_eq!(node_features.flags.len(), 20);
1444+
// - option_simple_close
1445+
assert_eq!(node_features.flags.len(), 8);
14461446
assert_eq!(node_features.flags[0], 0b00000001);
14471447
assert_eq!(node_features.flags[1], 0b01010001);
14481448
assert_eq!(node_features.flags[2], 0b10001010);
14491449
assert_eq!(node_features.flags[3], 0b00001010);
14501450
assert_eq!(node_features.flags[4], 0b10001000);
14511451
assert_eq!(node_features.flags[5], 0b10100000);
14521452
assert_eq!(node_features.flags[6], 0b00001000);
1453-
assert_eq!(node_features.flags[7], 0b00100000);
1454-
assert_eq!(node_features.flags[8], 0b00000000);
1455-
assert_eq!(node_features.flags[9], 0b00000000);
1456-
assert_eq!(node_features.flags[10], 0b00000000);
1457-
assert_eq!(node_features.flags[11], 0b00000000);
1458-
assert_eq!(node_features.flags[12], 0b00000000);
1459-
assert_eq!(node_features.flags[13], 0b00000000);
1460-
assert_eq!(node_features.flags[14], 0b00000000);
1461-
assert_eq!(node_features.flags[15], 0b00000000);
1462-
assert_eq!(node_features.flags[16], 0b00000000);
1463-
assert_eq!(node_features.flags[17], 0b00000000);
1464-
assert_eq!(node_features.flags[18], 0b00000000);
1465-
assert_eq!(node_features.flags[19], 0b00001000);
1453+
assert_eq!(node_features.flags[7], 0b10100000);
14661454
}
14671455

14681456
// Check that cleared flags are kept blank when converting back:

lightning/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "lightning"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
authors = ["Matt Corallo"]
55
license = "MIT OR Apache-2.0"
66
repository = "https://github.com/lightningdevkit/rust-lightning/"

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 33 additions & 40 deletions
Large diffs are not rendered by default.

lightning/src/ln/channel.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,8 +2521,8 @@ impl FundingScope {
25212521
where
25222522
SP::Target: SignerProvider,
25232523
{
2524-
debug_assert!(our_funding_contribution.abs() <= SignedAmount::MAX_MONEY);
2525-
debug_assert!(their_funding_contribution.abs() <= SignedAmount::MAX_MONEY);
2524+
debug_assert!(our_funding_contribution.unsigned_abs() <= Amount::MAX_MONEY);
2525+
debug_assert!(their_funding_contribution.unsigned_abs() <= Amount::MAX_MONEY);
25262526

25272527
let post_channel_value = prev_funding.compute_post_splice_value(
25282528
our_funding_contribution.to_sat(),
@@ -12137,15 +12137,15 @@ where
1213712137
fn validate_splice_contributions(
1213812138
&self, our_funding_contribution: SignedAmount, their_funding_contribution: SignedAmount,
1213912139
) -> Result<(), String> {
12140-
if our_funding_contribution.abs() > SignedAmount::MAX_MONEY {
12140+
if our_funding_contribution.unsigned_abs() > Amount::MAX_MONEY {
1214112141
return Err(format!(
1214212142
"Channel {} cannot be spliced; our {} contribution exceeds the total bitcoin supply",
1214312143
self.context.channel_id(),
1214412144
our_funding_contribution,
1214512145
));
1214612146
}
1214712147

12148-
if their_funding_contribution.abs() > SignedAmount::MAX_MONEY {
12148+
if their_funding_contribution.unsigned_abs() > Amount::MAX_MONEY {
1214912149
return Err(format!(
1215012150
"Channel {} cannot be spliced; their {} contribution exceeds the total bitcoin supply",
1215112151
self.context.channel_id(),

lightning/src/ln/channelmanager.rs

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,11 @@ enum BackgroundEvent {
12751275
/// Some [`ChannelMonitorUpdate`] (s) completed before we were serialized but we still have
12761276
/// them marked pending, thus we need to run any [`MonitorUpdateCompletionAction`] (s) pending
12771277
/// on a channel.
1278-
MonitorUpdatesComplete { counterparty_node_id: PublicKey, channel_id: ChannelId },
1278+
MonitorUpdatesComplete {
1279+
counterparty_node_id: PublicKey,
1280+
channel_id: ChannelId,
1281+
highest_update_id_completed: u64,
1282+
},
12791283
}
12801284

12811285
/// A pointer to a channel that is unblocked when an event is surfaced
@@ -8025,9 +8029,11 @@ where
80258029
/// Free the background events, generally called from [`PersistenceNotifierGuard`] constructors.
80268030
///
80278031
/// Expects the caller to have a total_consistency_lock read lock.
8028-
#[rustfmt::skip]
80298032
fn process_background_events(&self) -> NotifyOption {
8030-
debug_assert_ne!(self.total_consistency_lock.held_by_thread(), LockHeldState::NotHeldByThread);
8033+
debug_assert_ne!(
8034+
self.total_consistency_lock.held_by_thread(),
8035+
LockHeldState::NotHeldByThread
8036+
);
80318037

80328038
self.background_events_processed_since_startup.store(true, Ordering::Release);
80338039

@@ -8039,11 +8045,34 @@ where
80398045

80408046
for event in background_events.drain(..) {
80418047
match event {
8042-
BackgroundEvent::MonitorUpdateRegeneratedOnStartup { counterparty_node_id, funding_txo, channel_id, update } => {
8043-
self.apply_post_close_monitor_update(counterparty_node_id, channel_id, funding_txo, update);
8048+
BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
8049+
counterparty_node_id,
8050+
funding_txo,
8051+
channel_id,
8052+
update,
8053+
} => {
8054+
self.apply_post_close_monitor_update(
8055+
counterparty_node_id,
8056+
channel_id,
8057+
funding_txo,
8058+
update,
8059+
);
80448060
},
8045-
BackgroundEvent::MonitorUpdatesComplete { counterparty_node_id, channel_id } => {
8046-
self.channel_monitor_updated(&channel_id, None, &counterparty_node_id);
8061+
BackgroundEvent::MonitorUpdatesComplete {
8062+
counterparty_node_id,
8063+
channel_id,
8064+
highest_update_id_completed,
8065+
} => {
8066+
// Now that we can finally handle the background event, remove all in-flight
8067+
// monitor updates for this channel that we've known to complete, as they have
8068+
// already been persisted to the monitor and can be applied to our internal
8069+
// state such that the channel resumes operation if no new updates have been
8070+
// made since.
8071+
self.channel_monitor_updated(
8072+
&channel_id,
8073+
Some(highest_update_id_completed),
8074+
&counterparty_node_id,
8075+
);
80478076
},
80488077
}
80498078
}
@@ -17224,39 +17253,58 @@ where
1722417253
($counterparty_node_id: expr, $chan_in_flight_upds: expr, $monitor: expr,
1722517254
$peer_state: expr, $logger: expr, $channel_info_log: expr
1722617255
) => { {
17256+
// When all in-flight updates have completed after we were last serialized, we
17257+
// need to remove them. However, we can't guarantee that the next serialization
17258+
// will have happened after processing the
17259+
// `BackgroundEvent::MonitorUpdatesComplete`, so removing them now could lead to the
17260+
// channel never being resumed as the event would not be regenerated after another
17261+
// reload. At the same time, we don't want to resume the channel now because there
17262+
// may be post-update actions to handle. Therefore, we're forced to keep tracking
17263+
// the completed in-flight updates (but only when they have all completed) until we
17264+
// are processing the `BackgroundEvent::MonitorUpdatesComplete`.
1722717265
let mut max_in_flight_update_id = 0;
17228-
let starting_len = $chan_in_flight_upds.len();
17229-
$chan_in_flight_upds.retain(|upd| upd.update_id > $monitor.get_latest_update_id());
17230-
if $chan_in_flight_upds.len() < starting_len {
17266+
let num_updates_completed = $chan_in_flight_upds
17267+
.iter()
17268+
.filter(|update| {
17269+
max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id);
17270+
update.update_id <= $monitor.get_latest_update_id()
17271+
})
17272+
.count();
17273+
if num_updates_completed > 0 {
1723117274
log_debug!(
1723217275
$logger,
1723317276
"{} ChannelMonitorUpdates completed after ChannelManager was last serialized",
17234-
starting_len - $chan_in_flight_upds.len()
17277+
num_updates_completed,
1723517278
);
1723617279
}
17280+
let all_updates_completed = num_updates_completed == $chan_in_flight_upds.len();
17281+
1723717282
let funding_txo = $monitor.get_funding_txo();
17238-
for update in $chan_in_flight_upds.iter() {
17239-
log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}",
17240-
update.update_id, $channel_info_log, &$monitor.channel_id());
17241-
max_in_flight_update_id = cmp::max(max_in_flight_update_id, update.update_id);
17242-
pending_background_events.push(
17243-
BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
17244-
counterparty_node_id: $counterparty_node_id,
17245-
funding_txo: funding_txo,
17246-
channel_id: $monitor.channel_id(),
17247-
update: update.clone(),
17248-
});
17249-
}
17250-
if $chan_in_flight_upds.is_empty() {
17251-
// We had some updates to apply, but it turns out they had completed before we
17252-
// were serialized, we just weren't notified of that. Thus, we may have to run
17253-
// the completion actions for any monitor updates, but otherwise are done.
17283+
if all_updates_completed {
17284+
log_debug!($logger, "All monitor updates completed since the ChannelManager was last serialized");
1725417285
pending_background_events.push(
1725517286
BackgroundEvent::MonitorUpdatesComplete {
1725617287
counterparty_node_id: $counterparty_node_id,
1725717288
channel_id: $monitor.channel_id(),
17289+
highest_update_id_completed: max_in_flight_update_id,
1725817290
});
1725917291
} else {
17292+
$chan_in_flight_upds.retain(|update| {
17293+
let replay = update.update_id > $monitor.get_latest_update_id();
17294+
if replay {
17295+
log_debug!($logger, "Replaying ChannelMonitorUpdate {} for {}channel {}",
17296+
update.update_id, $channel_info_log, &$monitor.channel_id());
17297+
pending_background_events.push(
17298+
BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
17299+
counterparty_node_id: $counterparty_node_id,
17300+
funding_txo: funding_txo,
17301+
channel_id: $monitor.channel_id(),
17302+
update: update.clone(),
17303+
}
17304+
);
17305+
}
17306+
replay
17307+
});
1726017308
$peer_state.closed_channel_monitor_update_ids.entry($monitor.channel_id())
1726117309
.and_modify(|v| *v = cmp::max(max_in_flight_update_id, *v))
1726217310
.or_insert(max_in_flight_update_id);

lightning/src/ln/reload_tests.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,3 +1420,84 @@ fn test_peer_storage() {
14201420
assert!(res.is_err());
14211421
}
14221422

1423+
#[test]
1424+
fn test_hold_completed_inflight_monitor_updates_upon_manager_reload() {
1425+
// Test that if a `ChannelMonitorUpdate` completes after the `ChannelManager` is serialized,
1426+
// but before it is deserialized, we hold any completed in-flight updates until background event
1427+
// processing. Previously, we would remove completed monitor updates from
1428+
// `in_flight_monitor_updates` during deserialization, relying on
1429+
// [`ChannelManager::process_background_events`] to eventually be called before the
1430+
// `ChannelManager` is serialized again such that the channel is resumed and further updates can
1431+
// be made.
1432+
let chanmon_cfgs = create_chanmon_cfgs(2);
1433+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
1434+
let (persister_a, persister_b);
1435+
let (chain_monitor_a, chain_monitor_b);
1436+
1437+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
1438+
let nodes_0_deserialized_a;
1439+
let nodes_0_deserialized_b;
1440+
1441+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
1442+
let chan_id = create_announced_chan_between_nodes(&nodes, 0, 1).2;
1443+
1444+
send_payment(&nodes[0], &[&nodes[1]], 1_000_000);
1445+
1446+
chanmon_cfgs[0].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
1447+
1448+
// Send a payment that will be pending due to an async monitor update.
1449+
let (route, payment_hash, _, payment_secret) =
1450+
get_route_and_payment_hash!(nodes[0], nodes[1], 1_000_000);
1451+
let payment_id = PaymentId(payment_hash.0);
1452+
let onion = RecipientOnionFields::secret_only(payment_secret);
1453+
nodes[0].node.send_payment_with_route(route, payment_hash, onion, payment_id).unwrap();
1454+
check_added_monitors(&nodes[0], 1);
1455+
1456+
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
1457+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
1458+
1459+
// Serialize the ChannelManager while the monitor update is still in-flight.
1460+
let node_0_serialized = nodes[0].node.encode();
1461+
1462+
// Now complete the monitor update by calling force_channel_monitor_updated.
1463+
// This updates the monitor's state, but the ChannelManager still thinks it's pending.
1464+
let (_, latest_update_id) = nodes[0].chain_monitor.get_latest_mon_update_id(chan_id);
1465+
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(chan_id, latest_update_id);
1466+
let monitor_serialized_updated = get_monitor!(nodes[0], chan_id).encode();
1467+
1468+
// Reload the node with the updated monitor. Upon deserialization, the ChannelManager will
1469+
// detect that the monitor update completed (monitor's update_id >= the in-flight update_id)
1470+
// and queue a `BackgroundEvent::MonitorUpdatesComplete`.
1471+
nodes[0].node.peer_disconnected(nodes[1].node.get_our_node_id());
1472+
nodes[1].node.peer_disconnected(nodes[0].node.get_our_node_id());
1473+
reload_node!(
1474+
nodes[0],
1475+
test_default_channel_config(),
1476+
&node_0_serialized,
1477+
&[&monitor_serialized_updated[..]],
1478+
persister_a,
1479+
chain_monitor_a,
1480+
nodes_0_deserialized_a
1481+
);
1482+
1483+
// If we serialize again, even though we haven't processed any background events yet, we should
1484+
// still see the `BackgroundEvent::MonitorUpdatesComplete` be regenerated on startup.
1485+
let node_0_serialized = nodes[0].node.encode();
1486+
reload_node!(
1487+
nodes[0],
1488+
test_default_channel_config(),
1489+
&node_0_serialized,
1490+
&[&monitor_serialized_updated[..]],
1491+
persister_b,
1492+
chain_monitor_b,
1493+
nodes_0_deserialized_b
1494+
);
1495+
1496+
// Reconnect the nodes. We should finally see the `update_add_htlc` go out, as the reconnection
1497+
// should first process `BackgroundEvent::MonitorUpdatesComplete, allowing the channel to be
1498+
// resumed.
1499+
let mut reconnect_args = ReconnectArgs::new(&nodes[0], &nodes[1]);
1500+
reconnect_args.pending_htlc_adds = (0, 1);
1501+
reconnect_nodes(reconnect_args);
1502+
}
1503+

lightning/src/util/test_utils.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,11 @@ impl<'a> TestChainMonitor<'a> {
550550
self.added_monitors.lock().unwrap().push((channel_id, monitor));
551551
self.chain_monitor.load_existing_monitor(channel_id, new_monitor)
552552
}
553+
554+
pub fn get_latest_mon_update_id(&self, channel_id: ChannelId) -> (u64, u64) {
555+
let monitor_id_state = self.latest_monitor_update_id.lock().unwrap();
556+
monitor_id_state.get(&channel_id).unwrap().clone()
557+
}
553558
}
554559
impl<'a> chain::Watch<TestChannelSigner> for TestChainMonitor<'a> {
555560
fn watch_channel(

0 commit comments

Comments
 (0)