Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

invoice+rpc: add exit hop InvoiceAcceptor sub-systems and RPC calls #8669

Open
wants to merge 35 commits into
base: custom-channels-integration
Choose a base branch
from

Conversation

ffranr
Copy link
Collaborator

@ffranr ffranr commented Apr 19, 2024

Change Description

Closes #8616

This pull request introduces the following components:

  1. An interceptor service for invoice settlements.
  2. An invoice acceptor RPC server and endpoint which makes use of the invoice settlement interceptor.
  3. An integration test demonstrating the settlement of an invoice through the invoice acceptor, using a HTLC with an amount smaller than the specified invoice amount.

Steps to Test

Steps for reviewers to follow to test the change.

Pull Request Checklist

Testing

  • Your PR passes all CI checks.
  • Tests covering the positive and negative (error paths) are included.
  • Bug fixes contain tests triggering the bug to prevent regressions.

Code Style and Documentation

📝 Please see our Contribution Guidelines for further guidance.

Copy link

coderabbitai bot commented Apr 19, 2024

Important

Auto Review Skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch 7 times, most recently from c3f4d8a to dee0336 Compare April 23, 2024 15:49
@ffranr ffranr marked this pull request as ready for review April 23, 2024 15:49
@dstadulis
Copy link
Collaborator

@jharveyb and @GeorgeTsagk will review

invoices/update.go Outdated Show resolved Hide resolved
// SafeCallback is a thread safe wrapper around an InterceptorClientCallback.
type SafeCallback struct {
// mu is a mutex that protects the callback field.
mu sync.Mutex
Copy link
Member

Choose a reason for hiding this comment

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

Could also maybe use: https://pkg.go.dev/sync/atomic#Pointer?

Not sure it fits better tho. Still getting through the diff.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Would you like me to change this?

invoices/settlement_interceptor.go Outdated Show resolved Hide resolved
"github.com/lightningnetwork/lnd/lntypes"
)

// SafeCallback is a thread safe wrapper around an InterceptorClientCallback.
Copy link
Member

Choose a reason for hiding this comment

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

Where's InterceptorClientCallback defined?

Copy link
Member

Choose a reason for hiding this comment

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

Note that for this we also need to be able to feed in the wire HTLC TLV blobs. Today we don't store those on disk for accepted invoice HTLCs:

lnd/channeldb/invoices.go

Lines 1981 to 2002 in aee0294

tlvStream, err := tlv.NewStream(
tlv.MakePrimitiveRecord(chanIDType, &chanID),
tlv.MakePrimitiveRecord(htlcIDType, &key.HtlcID),
tlv.MakePrimitiveRecord(amtType, &amt),
tlv.MakePrimitiveRecord(
acceptHeightType, &htlc.AcceptHeight,
),
tlv.MakePrimitiveRecord(acceptTimeType, &acceptTime),
tlv.MakePrimitiveRecord(resolveTimeType, &resolveTime),
tlv.MakePrimitiveRecord(expiryHeightType, &htlc.Expiry),
tlv.MakePrimitiveRecord(htlcStateType, &state),
tlv.MakePrimitiveRecord(mppTotalAmtType, &mppTotalAmt),
tlv.MakeDynamicRecord(
htlcAMPType, amp, amp.PayloadSize,
record.AMPEncoder, record.AMPDecoder,
),
tlv.MakePrimitiveRecord(htlcHashType, hash32),
tlv.MakePrimitiveRecord(htlcPreimageType, preimage32),
)
if err != nil {
return nil, err
}

Copy link
Collaborator Author

@ffranr ffranr Apr 24, 2024

Choose a reason for hiding this comment

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

Where's InterceptorClientCallback defined?

In the same package. I think it might be introduced in a later commit. I'll see if I can change that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The invoice settlement interceptor and interceptor interface are now in the same commit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note that for this we also need to be able to feed in the wire HTLC TLV blobs. Today we don't store those on disk for accepted invoice HTLCs

Feed the wire HTLC TLV blobs into the interceptor? Or do you mean that we should store them to disk?

invoices/invoiceregistry.go Show resolved Hide resolved
invoices/invoiceregistry.go Outdated Show resolved Hide resolved
invoices/invoiceregistry.go Outdated Show resolved Hide resolved
server.go Show resolved Hide resolved
// request the client to accept an invoice.
message InvoiceAcceptorRequest {
// invoice is the invoice that the client should consider accepting.
lnrpc.Invoice invoice = 1;
Copy link
Member

Choose a reason for hiding this comment

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

Cool, so this will have InvoiceHTLC which is really what the interceptor wants.

Copy link
Collaborator

Choose a reason for hiding this comment

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

we might wanna add the wire level custom records on InvoiceHTLC too, currently we only have

    // Custom tlv records.
    map<uint64, bytes> custom_records = 9;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes. I think that's still needed. Not sure if in this PR.

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from dee0336 to 6cadf00 Compare April 24, 2024 15:28
@ffranr
Copy link
Collaborator Author

ffranr commented Apr 24, 2024

I think I also need to pass circuit key for latest HTLC along with invoice to settlement interceptor client. This way the client can inspect the correct HTLC.

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 6cadf00 to 48c11c5 Compare April 24, 2024 15:39
@ffranr ffranr requested a review from Roasbeef April 24, 2024 19:22
@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 48c11c5 to 550d8ce Compare April 25, 2024 11:42
@ffranr
Copy link
Collaborator Author

ffranr commented Apr 25, 2024

I think I also need to pass circuit key for latest HTLC along with invoice to settlement interceptor client. This way the client can inspect the correct HTLC.

This is included in the latest commits.

@ffranr
Copy link
Collaborator Author

ffranr commented Apr 25, 2024

I'm in the process of writing an itest for this PR. I noticed that providing the exit HTLC circuit key to the acceptor interceptor client is insufficient. The invoice will only include the exit HTLC if it is a replayed HTLC.

I will therefore need to provide fields from the invoice update context to the interceptor client. I will make changes to that effect.

Copy link
Contributor

@jharveyb jharveyb left a comment

Choose a reason for hiding this comment

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

Looks good! Can re-review once the itest is up.

invoices/settlement_interceptor.go Outdated Show resolved Hide resolved
}

// Exec calls the client callback function.
func (sc *SafeCallback) Exec(req InterceptClientRequest) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want a separate context or timeout wrapping this callback? IIUC this would block notifyExitHopHtlcLocked indefinitely if the interceptor goes offline, crashes, etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In the latest commits, Exec runs in a separate goroutine. And we wait for quit or a client response in notifyExitHopHtlcLocked.

There shouldn't be a chance of it blocking if the invoice acceptor not engaged. It's not perfect. But it might do for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree its fine for now & that there's no extra risk if there is no interceptor - I meant if we want to enforce a deadline on the interceptor response. Definitely non-blocking.

lnrpc/invoicesrpc/invoices_server.go Outdated Show resolved Hide resolved

// Invoice is the invoice that is being intercepted.
Invoice Invoice
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

don't we want to add more data here? I'd imagine that for example in the tap channels case, you'd want to provide any custom blobs that would need to be checked here

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think what precicely we need here will become more clear once we actually start using the InvoiceAcceptor from tapd.

invoices/settlement_interceptor.go Outdated Show resolved Hide resolved
invoices/settlement_interceptor.go Outdated Show resolved Hide resolved
invoices/invoiceregistry.go Show resolved Hide resolved
// request the client to accept an invoice.
message InvoiceAcceptorRequest {
// invoice is the invoice that the client should consider accepting.
lnrpc.Invoice invoice = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

we might wanna add the wire level custom records on InvoiceHTLC too, currently we only have

    // Custom tlv records.
    map<uint64, bytes> custom_records = 9;

@GeorgeTsagk
Copy link
Collaborator

reviewed diff, left out the WIP commits
also haven't locally tested anything

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 2f1825d to 86d0c02 Compare April 30, 2024 17:28
@ffranr
Copy link
Collaborator Author

ffranr commented Apr 30, 2024

Latest state:

  1. New integration test which shows that an invoice can be forced into the settled state (using the new interceptor) when the exit HTLC amount is less than the invoice amount.

  2. The latest modifications account for the MPP invoice settlement flow.

  3. I've simplified and reduced the necessary code changes.

Things to consider:

  1. The smallest exit HTLC payment amount I could send was 1000 msat. I'm not sure if we're hopping to send 0.

  2. We may need to add additional fields to the interceptor client request (provide more data to the interceptor client). In particular the custom records that are sent along with the p2p messages (UpdateAddHtlc etc.). That is not currently implemented.

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 86d0c02 to 0373b8d Compare April 30, 2024 18:20
In this commit, we add a new abstract message router. Over time, the
goal is that this message router replaces the logic we currently have in
the readHandler (the giant switch for each message).

With this new abstraction, can reduce the responsibilities of the
readHandler to *just* reading messages off the wire and handing them off
to the msg router. The readHandler no longer needs to know *where* the
messages should go, or how they should be dispatched.

This will be used in tandem with the new `protofsm` module in an
upcoming PR implementing the new rbf-coop close.
Roasbeef and others added 10 commits May 1, 2024 15:53
For the initiator, once we get the signal that the PSBT has been
finalized, we'll call into the aux funder to get the funding desc. For
the responder, once we receive the funding_created message, we'll do the
same.

We now also have local+remote aux leaves for the commitment transaction.
In this commit, we add a new aux signer interface that's meant to mirror the SigPool. If present, this'll be used to (maybe) obtain signatures for second level HTLCs for certain classes of custom channels.
In this commit, we start to use the new AuxSigner to obtain+verify aux sigs for all second level HTLCs. This is similar to the existing SigPool, but we'll only attempt to do this if the AuxSigner is present (won't be for most channels).
As is, we can't use PackRecords when some data may already be stored in the ExtraData field. To allow this use case, we add a failing test, then modify `PackRecords` to append the existing raw bytes (prepend the new records).

Note that this doesn't 100% solve the problem, as for the stream to be cannonical we need unique IDs/types and also for them to be in ascending order.
@jharveyb
Copy link
Contributor

jharveyb commented May 1, 2024

1. The smallest exit HTLC payment amount I could send was 1000 msat. I'm not sure if we're hopping to send 0.

IIRC the plan was for the HTLC to be >= dust threshold / 330ish sats, so that seems fine.

@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 0373b8d to 7d9d5cd Compare May 2, 2024 15:06
@ffranr ffranr changed the base branch from master to custom-channels-integration-rpc-channel May 2, 2024 15:07
ffranr added 8 commits May 3, 2024 12:59
This commit introduces a new invoice settlement interceptor service
that intercepts invoices during their settlement phase. It forwards
invoices to subscribed clients to determine their settlement outcomes.

This commit also introduces an interface to facilitate integrating the
interceptor with other packages.
This commit introduces the `SkipAmountCheck` field to the
`invoiceUpdateCtx` type. This field serves as a flag to determine
whether to bypass the amount verification during the invoice settlement
process. It is set based on the client's input, allowing the invoice to
be settled even if the HTLC amount is less than the stated invoice
amount.
This commit updates the invoice registry to utilize the settlement
interceptor during the invoice settlement routine. It allows the
interceptor to capture the invoice, providing interception clients an
opportunity to determine the settlement outcome.
This commit initiates the invoice settlement interceptor during the
main server startup, assigning it a handle within the server.
This commit integrates the settlement interceptor service into the
invoices RPC server.
This commit introduces a singleton invoice acceptor RPC server and
an endpoint to activate it. The server interfaces with the internal
invoice settlement interpreter, handling the marshalling between RPC
types and internal formats.

Named "acceptor," it allows clients to accept invoice settlements, but
not to reject them.
This commit enhances the itest LND node harness to include support for
the new `InvoiceAcceptor` RPC endpoint.
This commit introduces a basic integration test for the invoice
acceptor. The test covers scenarios where an invoice is settled with a
payment that is less than the invoice amount, facilitated by the invoice
settlement acceptor.
@ffranr ffranr force-pushed the 8616-invoice+rpc-add-exit-hop-invoiceacceptor-sub-systems-and-rpc-calls branch from 7d9d5cd to 989cc26 Compare May 3, 2024 11:59
@ffranr ffranr changed the base branch from custom-channels-integration-rpc-channel to custom-channels-integration May 3, 2024 12:00
@guggero guggero force-pushed the custom-channels-integration branch from 15abb14 to 09b34f1 Compare May 14, 2024 07:49
@lightninglabs-deploy
Copy link

@Roasbeef: review reminder
@jharveyb: review reminder
@GeorgeTsagk: review reminder

@guggero guggero force-pushed the custom-channels-integration branch 3 times, most recently from f9b366e to 5a834c1 Compare May 17, 2024 14:15
@ffranr
Copy link
Collaborator Author

ffranr commented May 17, 2024

I need to double check whether there are modifications in the PoC branch which relate to this PR.

Sign up for free to 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.

invoice+rpc: add exit hop InvoiceAcceptor sub-systems and RPC calls
7 participants