-
Notifications
You must be signed in to change notification settings - Fork 132
Multi rfq receive (AddInvoice
multiple hop hints)
#1457
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
Conversation
99e85a7
to
e2e8e47
Compare
rpcserver.go
Outdated
// invoice amount. Since peers have varying prices for the assets, we | ||
// pick the most expensive rate in order to allow for any combination of | ||
// MPP shards through our set of chosen peers. | ||
expensiveRate := acquiredQuotes[0].rate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should we pick the most expensive quote? This is effectively the same as giving the sender an option for the most expensive route in terms of fees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the invoice creator's PoV we only really see assets, but at the end of the day we have to direct the sender into sending some msat value.
If we pick any other than the most expensive quote (of the "rfq handpicks set") we may run into the case where the sender believes they pushed the full amt to satisfy the invoice, but because some shards went through the "expensive" link, the receiver actually sees less on their end, so they never settle the invoice and we end up in a stalemate.
rpcserver.go
Outdated
|
||
// Convert the asset amount into a fixed-point. | ||
assetAmount := rfqmath.NewBigIntFixedPoint(req.AssetAmount, 0) | ||
|
||
// Calculate the invoice amount in msat. | ||
valMsat := rfqmath.UnitsToMilliSatoshi(assetAmount, *askAssetRate) | ||
valMsat := rfqmath.UnitsToMilliSatoshi(assetAmount, *expensiveRate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, I think this is making a bit more sense now: ultimately we need to put some value into the invoice. I think we'll want to do another level of filtering here to ensure that we don't force the user to overpay. Or will the acceptance deviation check handle that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah since the RFQ quotes have been processed by our RFQ manager, we know that they respect the user configured tolerance.
We never do violate the configured tolerance, but we do lean towards higher values here. Which translates into more expensive invoices that the sender has to pay.
e2e8e47
to
e9e1de8
Compare
c5d02b1
to
204964f
Compare
Pull Request Test Coverage Report for Build 15121932291Details
💛 - Coveralls |
204964f
to
2a288fb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes are looking really good!
Only blocking factor now IMO is the addition of unit test coverage for the new helper functions that were moved from the rpcserver to the rfq package (which are also expanded in this PR).
@@ -7785,6 +7790,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context, | |||
return nil, fmt.Errorf("invoice request must be specified") | |||
} | |||
iReq := req.InvoiceRequest | |||
existingQuotes := iReq.RouteHints != nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, perhaps we should actually make validation stricter here and disallow adding extra route hints? Otherwise a user can add hop hints that affect our reachability, or encodes the wrong details, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered this to be the case where the user probably knows what they're doing. This carve-out was made for cases where quotes are negotiated externally, and the user provides them manually via the route hints.
IIUC what you're saying is that the user could actually get confused with the field and provide normal route hints for btc channels? In that case they're simply bypassing all asset related fields, as the invoice amt and route hints will persist as is, making this effectively a btc invoice.
If we want to block the above case, which doesn't sound unreasonable, I'll export/reuse the IsAssetInvoice
helper we added to the AuxInvoiceManager a while ago. If the route hints don't correspond to valid & accepted quotes, we'll return an error on the RPC.
acquiredQuotes = acquiredQuotes[:maxRfqHopHints] | ||
} | ||
|
||
// TODO(george): We want to cancel back quotes that didn't make it into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's spin this into an issue post merge so it doesn't get lost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder here.
rate: rate, | ||
// Since the channels are sorted, we now the value with | ||
// the greatest remote balance is at index 0. | ||
channel: channels[0], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something to keep in mind here is that when receiving shards our peer does non-strict forwarding on the LND level. The channel we pick here does not bind the actual link that will be used for the HTLC.
With the recent set of liquidity fixes, IIUC we're much stricter here now (being too lax was itself a bug).
Looks like actually just the channel ID and pubkey are explicitly needed, and we fetch the policy using one of lnd's RPC calls.
@ffranr: review reminder |
2a288fb
to
77dd73f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🦮
acquiredQuotes = acquiredQuotes[:maxRfqHopHints] | ||
} | ||
|
||
// TODO(george): We want to cancel back quotes that didn't make it into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder here.
There's an itest failure. Not sure if it's a flake though, can't view the logs from the UI as they're truncated. |
48f9b0d
to
58f862e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added naming suggestions.
proof3 = proof.Proof{ | ||
Asset: asset3, | ||
} | ||
testGroupKey = pubKeyFromUint64(2121) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should just set this to a random group key? I think we have functions for generating random group keys. And then we don't need pubKeyFromUint64
. Although, I do like pubKeyFromUint64
, random key is better practice for more coverage IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't really find the random generator you're referring to
Generally we do lean towards having simple "mock" values for most of our fields in unit tests, where everything is deterministic. Could consider a prop test but not sure if this code is worth that coverage?
Instead of using the wallet anchor channel lister we now use the lndclient one, as it offers a few more arguments that we want to use in the rfq manager. This is done as a preparatory step as we're going to push more responsibilities around channel picking to the rfq package.
We now move some of the rfq channel helpers that previously lived in rpcserver to the rfq package. We also use the new channel lister we introduced previously.
58f862e
to
33b78d9
Compare
Since this is a backwards-compatible change, I thought it might be worth noting that current LiT tests fail simply because of a returned error change |
We now extend our rfq channel related helpers in order to return a map of results instead of singular results. Since multi-rfq requires handling quotes with multiple peers, we don't care about handpicking a single best result but instead return all valid candidates.
Since we introduced the RfqChannel helper to the rfq package we take this opportunity to simplify the codebase of the addScidAlias helper.
As another preparatory step before introducing multiple quotes to the invoices, we add a simple parser that converts an rfq quote along with some other crucial information to a valid bolt11 hop hint.
We extract away another piece of logic that will be re-used in the new reformed AddInvoice endpoint. Since we will be querying multiple peers for quotes, we need to abstract this away into a function.
We now change the quote related flow of AddInvoice to instead query all of our peers for valid quotes. We keep the best subset of accepted quotes up to the max allowed number of hop hints and place it into the bolt11 invoice.
We rename some of the existing structures or helper methods to be shorter and more directly indicate their purpose.
33b78d9
to
8abf803
Compare
Description
This PR introduces the ability for tapd nodes to create invoices which involve multiple peer quotes. When the
peerPubKey
is left unspecified in theAddInvoice
RPC we no longer return an error, but instead acquire quotes with all peers that have a valid asset channel with us.Within this PR we also extract some rfq/liquidity related functions to the
rfq
package to keeprpcserver.go
more clean.Closes #1359
Based on #1423 (using that as base branch for now to avoid bloated diff)