Skip to content

Commit d007a61

Browse files
Gather to-decode HTLC fwds from channels on manager read
We have an overarching goal of (mostly) getting rid of ChannelManager persistence and rebuilding the ChannelManager's state from existing ChannelMonitors, due to issues when the two structs are out-of-sync on restart. The main issue that can arise is channel force closure. Here we start this process by rebuilding ChannelManager::decode_update_add_htlcs from the Channels, which will soon be included in the ChannelMonitors as part of a different series of PRs. The newly built map is not yet used but will be in the next commit.
1 parent 5537b9e commit d007a61

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

lightning/src/ln/channel.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7778,6 +7778,20 @@ where
77787778
Ok(())
77797779
}
77807780

7781+
/// Useful for reconstructing the set of pending HTLCs when deserializing the `ChannelManager`.
7782+
pub(super) fn get_inbound_committed_update_adds(&self) -> Vec<msgs::UpdateAddHTLC> {
7783+
self.context
7784+
.pending_inbound_htlcs
7785+
.iter()
7786+
.filter_map(|htlc| match htlc.state {
7787+
InboundHTLCState::Committed { ref update_add_htlc_opt } => {
7788+
update_add_htlc_opt.clone()
7789+
},
7790+
_ => None,
7791+
})
7792+
.collect()
7793+
}
7794+
77817795
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
77827796
#[inline]
77837797
fn mark_outbound_htlc_removed(

lightning/src/ln/channelmanager.rs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17353,6 +17353,7 @@ where
1735317353
decode_update_add_htlcs_legacy.unwrap_or_else(|| new_hash_map());
1735417354
let mut pending_intercepted_htlcs_legacy =
1735517355
pending_intercepted_htlcs_legacy.unwrap_or_else(|| new_hash_map());
17356+
let mut decode_update_add_htlcs = new_hash_map();
1735617357
let peer_storage_dir: Vec<(PublicKey, Vec<u8>)> = peer_storage_dir.unwrap_or_else(Vec::new);
1735717358
if fake_scid_rand_bytes.is_none() {
1735817359
fake_scid_rand_bytes = Some(args.entropy_source.get_secure_random_bytes());
@@ -17664,6 +17665,21 @@ where
1766417665
let mut peer_state_lock = peer_state_mtx.lock().unwrap();
1766517666
let peer_state = &mut *peer_state_lock;
1766617667
is_channel_closed = !peer_state.channel_by_id.contains_key(channel_id);
17668+
if let Some(chan) = peer_state.channel_by_id.get(channel_id) {
17669+
if let Some(funded_chan) = chan.as_funded() {
17670+
let inbound_committed_update_adds =
17671+
funded_chan.get_inbound_committed_update_adds();
17672+
if !inbound_committed_update_adds.is_empty() {
17673+
// Reconstruct `ChannelManager::decode_update_add_htlcs` from the serialized
17674+
// `Channel`, as part of removing the requirement to regularly persist the
17675+
// `ChannelManager`.
17676+
decode_update_add_htlcs.insert(
17677+
funded_chan.context.outbound_scid_alias(),
17678+
inbound_committed_update_adds,
17679+
);
17680+
}
17681+
}
17682+
}
1766717683
}
1766817684

1766917685
if is_channel_closed {
@@ -17722,9 +17738,15 @@ where
1772217738
};
1772317739
// The ChannelMonitor is now responsible for this HTLC's
1772417740
// failure/success and will let us know what its outcome is. If we
17725-
// still have an entry for this HTLC in `forward_htlcs` or
17726-
// `pending_intercepted_htlcs`, we were apparently not persisted after
17727-
// the monitor was when forwarding the payment.
17741+
// still have an entry for this HTLC in `forward_htlcs`,
17742+
// `pending_intercepted_htlcs`, or `decode_update_add_htlcs`, we were apparently not
17743+
// persisted after the monitor was when forwarding the payment.
17744+
dedup_decode_update_add_htlcs(
17745+
&mut decode_update_add_htlcs,
17746+
&prev_hop_data,
17747+
"HTLC was forwarded to the closed channel",
17748+
&args.logger,
17749+
);
1772817750
dedup_decode_update_add_htlcs(
1772917751
&mut decode_update_add_htlcs_legacy,
1773017752
&prev_hop_data,
@@ -18215,6 +18237,35 @@ where
1821518237
}
1821618238
}
1821718239

18240+
// HTLCs that need to be failed due to a closed channel should not be put in
18241+
// `ChannelManager::decode_update_add_htlcs`, which would cause them to be re-decoded
18242+
// post-restart. Instead just fail them back as part of deserialization.
18243+
for (src, _, _, _, _, _) in failed_htlcs.iter() {
18244+
if let HTLCSource::PreviousHopData(prev_hop_data) = src {
18245+
dedup_decode_update_add_htlcs(
18246+
&mut decode_update_add_htlcs,
18247+
prev_hop_data,
18248+
"HTLC was failed backwards during manager read",
18249+
&args.logger,
18250+
);
18251+
}
18252+
}
18253+
18254+
// In the future, `claimable_payments` should be rebuilt from `Channel{Monitor}` data as part of
18255+
// removing the requirement to regularly persist the `ChannelManager`. In the meantime, if an
18256+
// HTLC is present in the claimables map, it should not be duplicated in
18257+
// `decode_update_add_htlcs`.
18258+
for htlcs in claimable_payments.values().map(|pmt| &pmt.htlcs) {
18259+
for prev_hop_data in htlcs.iter().map(|h| &h.prev_hop) {
18260+
dedup_decode_update_add_htlcs(
18261+
&mut decode_update_add_htlcs,
18262+
prev_hop_data,
18263+
"HTLC was already decoded and marked as a claimable payment",
18264+
&args.logger,
18265+
);
18266+
}
18267+
}
18268+
1821818269
let best_block = BestBlock::new(best_block_hash, best_block_height);
1821918270
let flow = OffersMessageFlow::new(
1822018271
chain_hash,

0 commit comments

Comments
 (0)