-
Notifications
You must be signed in to change notification settings - Fork 471
Refresh async static invoices after channel changes #4753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
94d4bc3
d32002a
10b1988
f3c61ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3006,6 +3006,8 @@ pub struct ChannelManager< | |
| funding_batch_states: Mutex<BTreeMap<Txid, Vec<(ChannelId, PublicKey, bool)>>>, | ||
|
|
||
| background_events_processed_since_startup: AtomicBool, | ||
| /// Set when a channel change may have made cached async receive static invoices stale. | ||
| async_receive_static_invoice_refresh_pending: AtomicBool, | ||
|
|
||
| event_persist_notifier: Notifier, | ||
| needs_persist_flag: AtomicBool, | ||
|
|
@@ -3766,6 +3768,7 @@ impl< | |
| pending_background_events: Mutex::new(Vec::new()), | ||
| total_consistency_lock: RwLock::new(()), | ||
| background_events_processed_since_startup: AtomicBool::new(false), | ||
| async_receive_static_invoice_refresh_pending: AtomicBool::new(false), | ||
| event_persist_notifier: Notifier::new(), | ||
| needs_persist_flag: AtomicBool::new(false), | ||
| funding_batch_states: Mutex::new(BTreeMap::new()), | ||
|
|
@@ -4564,6 +4567,8 @@ impl< | |
| )); | ||
| } | ||
| } | ||
| self.mark_async_receive_static_invoice_refresh_pending(); | ||
|
|
||
| for (err, counterparty_node_id) in shutdown_results.drain(..) { | ||
| let _ = self.handle_error(err, counterparty_node_id); | ||
| } | ||
|
|
@@ -4693,6 +4698,7 @@ impl< | |
| log_error!(logger, "Closing channel: {}", err_internal.err.err); | ||
|
|
||
| self.finish_close_channel(shutdown_res); | ||
| self.process_pending_async_receive_static_invoice_refresh(); | ||
| if let Some((update, node_id_1, node_id_2)) = update_option { | ||
| let mut pending_broadcast_messages = | ||
| self.pending_broadcast_messages.lock().unwrap(); | ||
|
|
@@ -5905,6 +5911,30 @@ impl< | |
| } | ||
| } | ||
|
|
||
| fn force_refresh_async_receive_static_invoices(&self) { | ||
| let router = &self.router; | ||
|
|
||
| // Only collect peers and usable channels when async receiving is configured. This avoids reading | ||
| // channels during state transitions when there is no static invoice to refresh. | ||
| self.flow.force_refresh_async_receive_static_invoices( | ||
| || (self.get_peers_for_blinded_path(), self.list_usable_channels()), | ||
| router, | ||
| ); | ||
| } | ||
|
|
||
| fn mark_async_receive_static_invoice_refresh_pending(&self) { | ||
| self.async_receive_static_invoice_refresh_pending.store(true, Ordering::Release); | ||
| } | ||
|
|
||
| fn process_pending_async_receive_static_invoice_refresh(&self) { | ||
| // Channel state transitions often happen while a peer's channel lock is held. Defer the | ||
| // actual refresh until after those locks are released, because rebuilding static invoices | ||
| // needs a fresh snapshot of usable channels. | ||
| if self.async_receive_static_invoice_refresh_pending.swap(false, Ordering::AcqRel) { | ||
| self.force_refresh_async_receive_static_invoices(); | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| pub(crate) fn test_check_refresh_async_receive_offers(&self) { | ||
| self.check_refresh_async_receive_offer_cache(false); | ||
|
|
@@ -9130,6 +9160,7 @@ impl< | |
| .remove_stale_payments(duration_since_epoch, &self.pending_events); | ||
|
|
||
| self.check_refresh_async_receive_offer_cache(true); | ||
| self.process_pending_async_receive_static_invoice_refresh(); | ||
|
|
||
| if self.check_free_holding_cells() { | ||
| // While we try to ensure we clear holding cells immediately, its possible we miss | ||
|
|
@@ -13750,6 +13781,7 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |
| }, | ||
| None, | ||
| )); | ||
| self.mark_async_receive_static_invoice_refresh_pending(); | ||
| splice_promotion.discarded_funding.into_iter().for_each(|funding_info| { | ||
| let event = Event::DiscardFunding { | ||
| channel_id: chan.context.channel_id(), | ||
|
|
@@ -16453,6 +16485,7 @@ impl< | |
| funding_txo: Some(funding_txo.into_bitcoin_outpoint()), | ||
| channel_type: funded_channel.funding.get_channel_type().clone(), | ||
| }, None)); | ||
| self.mark_async_receive_static_invoice_refresh_pending(); | ||
| discarded_funding.into_iter().for_each(|funding_info| { | ||
| let event = Event::DiscardFunding { | ||
| channel_id: funded_channel.context.channel_id(), | ||
|
|
@@ -16873,16 +16906,19 @@ impl< | |
|
|
||
| #[rustfmt::skip] | ||
| fn handle_splice_locked(&self, counterparty_node_id: PublicKey, msg: &msgs::SpliceLocked) { | ||
| let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || { | ||
| let res = self.internal_splice_locked(&counterparty_node_id, msg); | ||
| let persist = match &res { | ||
| Err(e) if e.closes_channel() => NotifyOption::DoPersist, | ||
| Err(_) => NotifyOption::SkipPersistHandleEvents, | ||
| Ok(()) => NotifyOption::DoPersist, | ||
| }; | ||
| let _ = self.handle_error(res, counterparty_node_id); | ||
| persist | ||
| }); | ||
| { | ||
| let _persistence_guard = PersistenceNotifierGuard::optionally_notify(self, || { | ||
| let res = self.internal_splice_locked(&counterparty_node_id, msg); | ||
| let persist = match &res { | ||
| Err(e) if e.closes_channel() => NotifyOption::DoPersist, | ||
| Err(_) => NotifyOption::SkipPersistHandleEvents, | ||
| Ok(()) => NotifyOption::DoPersist, | ||
| }; | ||
| let _ = self.handle_error(res, counterparty_node_id); | ||
| persist | ||
| }); | ||
| } | ||
| self.process_pending_async_receive_static_invoice_refresh(); | ||
| } | ||
|
|
||
| fn handle_shutdown(&self, counterparty_node_id: PublicKey, msg: &msgs::Shutdown) { | ||
|
|
@@ -17017,14 +17053,22 @@ impl< | |
| } | ||
|
|
||
| fn handle_channel_update(&self, counterparty_node_id: PublicKey, msg: &msgs::ChannelUpdate) { | ||
| PersistenceNotifierGuard::optionally_notify(self, || { | ||
| let res = self.internal_channel_update(&counterparty_node_id, msg); | ||
| if let Ok(persist) = self.handle_error(res, counterparty_node_id) { | ||
| persist | ||
| } else { | ||
| NotifyOption::DoPersist | ||
| } | ||
| }); | ||
| { | ||
| PersistenceNotifierGuard::optionally_notify(self, || { | ||
| let res = self.internal_channel_update(&counterparty_node_id, msg); | ||
| if let Ok(persist) = self.handle_error(res, counterparty_node_id) { | ||
| if persist == NotifyOption::DoPersist { | ||
| // Static invoices encode the counterparty's forwarding parameters. Refresh | ||
| // them when an update changes those parameters for a local channel. | ||
| self.mark_async_receive_static_invoice_refresh_pending(); | ||
|
Comment on lines
+17060
to
+17063
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This forced-refresh trigger is remote-peer-controlled, which makes it a stronger amplification concern than the local fee-change trigger. Consider only marking the refresh pending when the update actually changes a forwarding parameter encoded in the invoice paths (fees / cltv / htlc bounds), and/or debouncing so a chatty peer can't drive unbounded onion traffic. |
||
| } | ||
| persist | ||
| } else { | ||
| NotifyOption::DoPersist | ||
| } | ||
| }); | ||
| } | ||
| self.process_pending_async_receive_static_invoice_refresh(); | ||
| } | ||
|
|
||
| fn handle_channel_reestablish( | ||
|
|
@@ -20513,6 +20557,7 @@ impl< | |
| pending_background_events: Mutex::new(pending_background_events), | ||
| total_consistency_lock: RwLock::new(()), | ||
| background_events_processed_since_startup: AtomicBool::new(false), | ||
| async_receive_static_invoice_refresh_pending: AtomicBool::new(false), | ||
|
|
||
| event_persist_notifier: Notifier::new(), | ||
| needs_persist_flag: AtomicBool::new(false), | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.