Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Handle network updates from failed payments in BackgroundProcessor - #1043

Merged
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler
Sep 15, 2021
Merged

Handle network updates from failed payments in BackgroundProcessor#1043
TheBlueMatt merged 10 commits into
lightningdevkit:mainfrom
jkczyz:2021-07-network-update-handler

Conversation

@jkczyz

@jkczyzjkczyz commented Aug 14, 2021

Copy link
Copy Markdown
Contributor

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) into Event::PaymentFailed to be processed by an event handler.

This requires making NetGraphMsgHandler an EventHandler, which is used in BackgroundProcessor to decorate the user-provided EventHandler with the ability to update the NetworkGraph accordingly.

The general concept of intercepting events via a decorator will be used in a follow-up PR for scoring channels and retrying failed payments.

@codecov

codecovBot commented Aug 14, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1043 (992df51) into main (423f1b1) will increase coverage by 0.21%.
The diff coverage is 91.04%.

Impacted file tree graph

@@ Coverage Diff @@## main #1043 +/- ##
==========================================
+ Coverage 90.83% 91.04% +0.21% 
==========================================
Files 65 65 Lines 33281 33762 +481 ==========================================
+ Hits 30232 30740 +508 + Misses 3049 3022 -27 
Impacted FilesCoverage Δ
lightning-net-tokio/src/lib.rs77.27% <ø> (+0.29%)⬆️
lightning/src/ln/msgs.rs89.50% <ø> (+0.39%)⬆️
lightning/src/ln/peer_handler.rs45.94% <ø> (+0.16%)⬆️
lightning/src/util/test_utils.rs82.55% <ø> (+0.21%)⬆️
lightning/src/util/events.rs27.54% <40.00%> (+13.60%)⬆️
lightning/src/ln/onion_utils.rs94.91% <75.00%> (ø)
lightning/src/ln/channelmanager.rs85.90% <84.61%> (+0.05%)⬆️
lightning-background-processor/src/lib.rs94.20% <85.71%> (+0.26%)⬆️
lightning/src/routing/network_graph.rs92.12% <87.13%> (+0.31%)⬆️
lightning/src/routing/router.rs96.25% <92.00%> (-0.10%)⬇️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 423f1b1...992df51. Read the comment docs.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from f5e9985 to bec75d7CompareAugust 14, 2021 03:11

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK. Only did a high-level skim but I like it.

Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/routing/network_graph.rs Outdated
}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmmm, have we considered maybe just allowing events to be handled by multiple handlers? Passing them off to an inner handler works, I suppose, but its kinda awkward. I guess I'm not a huge fan of passing the events by reference instead of value, but most of them have no heap allocation, and eg SpendableOutputs should be pretty rare (requires on-chain txn).

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator approach here is nice because it allows us to short-circuit handling. This is needed for payment retries where the inner handler is only invoked after the final failure.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yea, good point. This isn't a trait change, though, so we could do different things for different handlers, right?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite sure I follow what you mean by do "different things for different handlers". Do you have a concrete example that may clarify?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, there's nothing here that requires that we handle ignoring/passing down events the same for NetworkGraph as we do for PaymentRetryer, or whatever, is there?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

(see also other comment about removing NetworkGraph from BackgroundProcessor)

We'd have to parameterize NetworkGraph by a Logger if we did that. 😕 How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Using references allows a bit more flexibility like post-handling event as mentioned above.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to parameterize NetworkGraph by a Logger if we did that.

Eh, we basically already do that for everything, not a big deal IMO.

How about implementing EventHandler for NetGraphMsgHandler? I've been toying around with renaming that NetworkGossip, making the naming less specific about how and that it updates NetworkGraph.

No strong feelings, do what you want for both.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't pushed it yet, but I went with implementing it on NetGraphMsgHandler. It's also nice to not have to know about a decorated EventHandler when deserializing a NetworkGraph.

That said, just want to point out a drawback with either is that the guide for setting up a node will need to change. Now event handling (step 16) can no longer be discussed independently of initializing NetGraphMsgHandler (optional step 12).

cc: @valentinewallace Any opinions on this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. That is a slight drawback but not a deal breaker IMO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ok. Indeed, ideally we'd have everything be nice and separate, but if we're gonna have something related to the network graph handle events, that's the outcome we're gonna have. It is nice that all the bindings have moved towards using the lightning-background-processor crate, though, so it'll all be common infrastructure in any case.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 99c2337 to de7cb1dCompareAugust 17, 2021 16:40
@TheBlueMattTheBlueMatt added this to the 0.0.101 milestone Aug 17, 2021
@valentinewallace
valentinewallace self-requested a review August 17, 2021 21:36

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General call for more docs on the usage of this new API, but this is looking good to me!

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/util/events.rs
@valentinewallace

Copy link
Copy Markdown
Contributor

Implemented in the sample: valentinewallace/ldk-sample@ddc9414
I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Implemented in the sample: valentinewallace/ldk-sample@ddc9414 I think it looks good, though it does add a clone to each Event and a few more clones of NetworkGraph.

Note that you don't actually need the clone - https://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commit;h=bc119d8fb23d8e55c91df002ca490062f9cc72a6

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from de7cb1d to 4e2f2a4CompareAugust 19, 2021 18:00
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Updated the docs!

There's a couple open issues with testing:

  • test_invalid_channel_announcement seems non-sensical, so maybe just drop it as discussed on Slack
  • htlc_fail_async_shutdown fails when performing checks in expect_payment_failed_with_update

For the latter, I commented out the assertion in expect_payment_failed_with_update, which I won't want to merge. So need to figure a workaround. Perhaps just not use the macro. 😕

Comment threadlightning/src/routing/network_graph.rs Outdated
Comment threadlightning/src/routing/network_graph.rs
Comment threadlightning/src/ln/functional_tests.rs Outdated
Comment threadlightning/src/ln/onion_route_tests.rs Outdated
Comment threadlightning/src/ln/functional_tests.rs Outdated
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);

expect_payment_failed!(nodes[0], our_payment_hash, false);
expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1.0.contents.short_channel_id, false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd argue the issue here is on the channel end - we fail with chan_disabled instead of permanent_channel_failure. I think we should swap the shutdown checks in Channel::update_add_htlc to instead use 0x4000|8 and then fix channelmanager to handle it (I haven't reviewed this, just hacked it up, so may need more thought):

@@ -3461,33 +3464,32 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to successfully get a
+ // channel update is if the channel isn't in the fully-funded
+ // state yet, implying our counterparty is trying to route
+ // payments over the channel back to themselves (cause no one
+ // else should know the short_id is a lightning channel yet).
+ // We should have no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work though I had to change which channel is used in the failing test otherwise the short channel id wouldn't match. Take a look at d5ea8e8. I'll properly backport the fixup if all looks well.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 4e2f2a4 to d5ea8e8CompareAugust 25, 2021 01:32
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from d5ea8e8 to 9828dccCompareAugust 25, 2021 22:12

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One material comment, but once we get resolution on that I'm ACK.

Comment threadlightning/src/routing/network_graph.rs Outdated
/// # Example
///
/// ```
/// # extern crate bitcoin;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does # do?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hides it from showing up in the documentation. Useful if you have an elaborate setup that may distract from the example. Could leave some of these lines in if you'd like. Need to fix the indentation anyhow.

}
}

self.inner.handle_event(event)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you also want to update to just implement EventHandler on the NetworkGraph? Given the background processor already explicitly takes a NetworkGraph, it wouldn't complicate the background processor much at all (and maybe simplify it if we make the network graph there optional).

If we're gonna go down the path of decorators eating and passing events onto inner handlers, I dunno if we need to bother passing events by reference.

Comment threadlightning-background-processor/src/lib.rs Outdated
G: 'static + Deref<Target = NetworkGraph> + Send + Sync,
>
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
(persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, network_graph: G, logger: L) -> Self

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

network_graph needs to be Optional.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may actually want to remove this entirely from BackgroundProcessor. With #1059, users will want to decorate InvoicePayer with NetworkUpdateHandler so that updates are applied prior to retrying. Given they'll want to use the InvoicePayer to make payments, we can't construct that here without returning it back to them somehow.

So instead we may want to leave it to the user to set up the event handler to pass to BackgroundProcessor. But we could perhaps provide a utility for decorating their event handler with standard decorators.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I guess that makes sense - you only get those errors if you send a payment and it fails, so requiring a payment retryer in order to handle those errors isn't crazy.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the changes to BackgroundProcessor.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from e0aff9c to f9bee36CompareSeptember 8, 2021 23:55
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I removed the commit that made NetworkGraph have shared ownership since it is no longer necessary now that NetGraphMsgHandler is responsible for event handling.

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Comment threadlightning/src/ln/channelmanager.rs Outdated
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
let _ = self.network_graph.update_channel(msg, &self.secp_ctx);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should there be a TODO to handle the error?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The code that produces them is reused in other places but likely isn't actionable here. Might be able to log one marked IgnoreAndLog.

Comment threadlightning/src/util/test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Needs rebase.

@jkczyz

Copy link
Copy Markdown
ContributorAuthor

I think the docs could be a little more explicit that you're expected to nest EventHandlers for BackgroundProcessor::start now but this is looking slick overall!

Added a short note about decorators in start. Otherwise, we'll likely want some better documentation which could go on a utility used to wrap an event handler with common ones.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from f9bee36 to 1997e75CompareSeptember 10, 2021 03:56
@TheBlueMattTheBlueMatt modified the milestones: 0.0.101, 0.0.102Sep 10, 2021
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 1997e75 to 69fc1f9CompareSeptember 10, 2021 03:57
Passing an Event by reference rather and by move gives more flexibility
for composing event handlers without needing to clone events.
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Moving this (and related stuff) to 0.0.102 because it looks like we may want to ship an 0.0.101 to get some fixes out.

In preparation for giving NetworkGraph shared ownership, wrap individual
fields in RwLock. This allows removing the outer RwLock used in
NetGraphMsgHandler.
Now that NetworkGraph uses interior mutability, the RwLock used around
it in NetGraphMsgHandler is no longer needed. This allows for shared
ownership without a lock.
Hide the internal locking of NetworkGraph by providing a read-only
view. This way the locking order is handled internally.
It doesn't seem to be testing anything useful that isn't covered
elsewhere.
This affects the htlc_fail_async_shutdown test.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 69fc1f9 to 4455d5fCompareSeptember 10, 2021 04:18
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch 2 times, most recently from 1bda8c4 to 8362eadCompareSeptember 15, 2021 02:39
@jkczyz

Copy link
Copy Markdown
ContributorAuthor

Based on an offline conversation with @TheBlueMatt, I removed the decorated EventHandler from NetGraphMsgHandler. Instead, BackgroundProcessor now takes an optional NetGraphMsgHandler which it delegates to before letting the user's event handler run.

This approach is a bit cleaner since it simplifies NetGraphMsgHandler and makes it such that BackgroundProcessor does the right thing for the user.

Comment threadlightning-background-processor/src/lib.rs Outdated
Comment threadlightning/src/ln/functional_test_utils.rs Outdated
@TheBlueMatt

Copy link
Copy Markdown
Collaborator

ACK aside from the two comments above.

@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 8362ead to 0b452b7CompareSeptember 15, 2021 15:27

@valentinewallacevalentinewallace left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM

Comment on lines +40 to +41
/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
/// upon as doing so may result in high latency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, so I checked lightningdevkit.org guides, the sample, process_events docs and I don't think we indicate anywhere that the user should be calling this function perioically (i.e., users do currently rely on BackgroundProcessor to call process_events atm).

Should we update some other docs/the sample, or open an issue to do so?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its primarily called by lightning-net-tokio or by the NioPeerHandler in Java, so I don't think users need to care, at least not normal users.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did open an issue at #1075.

MessageSendEvent::PaymentFailureNetworkUpdate served as a hack to pass
an HTLCFailChannelUpdate from ChannelManager to NetGraphMsgHandler via
PeerManager. Instead, remove the event entirely and move the contained
data (renamed NetworkUpdate) to Event::PaymentFailed to be processed by
an event handler.
PaymentFailed events contain an optional NetworkUpdate describing
changes to the NetworkGraph as conveyed by a node along a failed payment
path according to BOLT 4. An EventHandler should apply the update to the
graph so that future routing decisions can account for it.
Implement EventHandler for NetGraphMsgHandler to update NetworkGraph.
Previously, NetGraphMsgHandler::handle_htlc_fail_channel_update
implemented this behavior.
Decorate the user-supplied EventHandler with NetGraphMsgHandler in
the BackgroundProcessor. The resulting handler will intercept
PaymentFailed events in order to update the NetworkGraph in the
background before delegating to the user's event handler.
@jkczyz
jkczyzforce-pushed the 2021-07-network-update-handler branch from 0b452b7 to 992df51CompareSeptember 15, 2021 16:53
@TheBlueMatt
TheBlueMatt merged commit bb9df69 into lightningdevkit:mainSep 15, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jkczyz@valentinewallace@TheBlueMatt