Support group keys on SendPayment & AddInvoice#1423
Conversation
Pull Request Test Coverage Report for Build 14266166576Details
💛 - Coveralls |
|
The current LiT itests are going to pass, as they will keep using a single asset ID for payments and invoices. This LiT PR uses the new group key arguments for creating/paying invoices. |
7c660bd to
6cf7190
Compare
9c9de09 to
b53b3a2
Compare
|
Rebased on |
b53b3a2 to
f13f4a0
Compare
f13f4a0 to
27faee9
Compare
guggero
left a comment
There was a problem hiding this comment.
Did a first pass.
Concerning the discussion around group key in the asset sum entry.
I failed to consider the group key send case when designing the p2p messages.
Once an HTLC is locked in, the allocation code makes sure each HTLC has a specific, clearly defined set of asset pieces, all with IDs defined.
But as long as the HTLC is just transmitted (e.g. on the SendPayment RPC, in ProduceHtlcExtraData function of the aux traffic shaper, or even in the UpdateAddHtlc wire message), we don't actually know the ID of the piece(s) that are going to be used for the HTLC.
So in those scenarios we only really check two things in the policies:
- The sum of the assets being transferred in the HTLC message
- Whether the assets match the policy
For the second check, I think it makes sense to indirectly put the group key in there. Whether we hash it first (as we do for the universe key) or whether we just use the x-coordinate of the group key doesn't matter too much IMO. But I think we need to be very clear wherever we do that and definitely add a comment on why we're doing that (feel free to copy my explanation above).
27faee9 to
99e85a7
Compare
|
Changed base to |
guggero
left a comment
There was a problem hiding this comment.
Very nice!
Super super close, just a couple of nits and improvement requests.
| // Perform the DB query. | ||
| group, err := m.cfg.GroupLookup.QueryAssetGroup(ctx, id) | ||
| if err != nil { | ||
| if errors.Is(err, address.ErrAssetGroupUnknown) { |
There was a problem hiding this comment.
Do we still need this commit, given that we now check whether a given asset ID is a group key before querying the DB?
It just feels weird to return fn.None[btcec.PublicKey](). To me that means: the given asset ID is _not_ part of a group.
There was a problem hiding this comment.
Yes this is still valid for cases where AssetMatchesSpecifier is called with an asset ID and a specifier which includes a group key
An actual group lookup would have to be performed for that set of inputs. If that asset doesn't belong to a group we would run into the above line.
There was a problem hiding this comment.
An actual group lookup would have to be performed for that set of inputs. If that asset doesn't belong to a group we would run into the above line.
Seems related to the first comment I left in this round of review. Looks like we would do a DB look up to make sure that we didn't accidentally interpret an asset ID as a group key.
| return true, nil | ||
| } | ||
|
|
||
| // Now let's make an actual query to find this assetID's group, |
There was a problem hiding this comment.
Yeah, given that we don't call getAssetGroupKey with a "fake" asset ID anymore, I think we can potentially remove the previous commit.
rpcserver.go
Outdated
| return err | ||
| } | ||
|
|
||
| switch { |
There was a problem hiding this comment.
Should we add this switch and the error check above as a new function asset.NewExclusiveSpecifier()?
To make sure we use the same semantics everywhere.
There was a problem hiding this comment.
The above error comes from the rpc marshaller, we could make such a func for the switch itself (i.e make a specifier that only contains one of assetID/groupKey) but I'm not sure if there's a clear way of saying which one to keep? Would we default to keeping the groupKey over the assetID or vice versa?
| func (r *rpcServer) AddInvoice(ctx context.Context, | ||
| req *tchrpc.AddInvoiceRequest) (*tchrpc.AddInvoiceResponse, error) { | ||
|
|
||
| if len(req.AssetId) > 0 && len(req.GroupKey) > 0 { |
There was a problem hiding this comment.
Re-use potential, see my previous comment for NewExclusiveSpecifier.
There was a problem hiding this comment.
ah, since there's a new occurrence here it makes sense to add smth for this
I guess can add a simple NewExclusiveSpecifier that accepts both assetID/groupKey and prioritizes groupKey over assetID, given that it's more generic
There was a problem hiding this comment.
This check can be moved as well.
99e85a7 to
e2e8e47
Compare
Roasbeef
left a comment
There was a problem hiding this comment.
LGTM ❄️
I dig the change to move to using the x-coord instead of the hash.
Changes look good pending the final comments from the other reviewer.
Will check out the corresponding itest changes now.
| groupKey := c.AssetSpecifier.UnwrapGroupKeyToPtr() | ||
| groupKeyX := schnorr.SerializePubKey(groupKey) | ||
|
|
||
| assetID = asset.ID(groupKeyX) |
There was a problem hiding this comment.
This is nice compared to the hash version, as this way we aren't actually losing any information.
However, it's possible that a normal asset ID, can actually be interpreted as a valid x coordinate. To avoid this confusion, when we decode on the other side, and conclude it might be a pubkey, we should check our local db to make sure it's actually a group key that we've validated.
There was a problem hiding this comment.
I think this is the receiver's part that you're describing
Lines 984 to 990 in e2e8e47
a receiver is always checking this "assetID" against a specifier. If that specifier contains a groupkey we'll immediately check if the X coordinates match before doing any DB lookups
we should check our local db to make sure it's actually a group key that we've validated.
That's an interesting point. Do we really care if the group key is validated?
If that group key made it into a channel or RFQ quote, then it can't be a fake or un-validated group key. If you have DoS vectors in mind, I haven't digged a lot into that.
| // Perform the DB query. | ||
| group, err := m.cfg.GroupLookup.QueryAssetGroup(ctx, id) | ||
| if err != nil { | ||
| if errors.Is(err, address.ErrAssetGroupUnknown) { |
There was a problem hiding this comment.
An actual group lookup would have to be performed for that set of inputs. If that asset doesn't belong to a group we would run into the above line.
Seems related to the first comment I left in this round of review. Looks like we would do a DB look up to make sure that we didn't accidentally interpret an asset ID as a group key.
| func (r *rpcServer) AddInvoice(ctx context.Context, | ||
| req *tchrpc.AddInvoiceRequest) (*tchrpc.AddInvoiceResponse, error) { | ||
|
|
||
| if len(req.AssetId) > 0 && len(req.GroupKey) > 0 { |
There was a problem hiding this comment.
This check can be moved as well.
We add a new interface to the HTLC SumAssetBalance method, which helps check the identifier of the asset against a specifier. This allows for checking asset inclusion in a group, which is a bit involved and not the responsibility of the HTLC model.
We extend the interface of the rfq Policy in order to allow the specifier checker to be involved. This extends certain checks, and allows us to use asset specifiers that only have a group key.
When intercepting an HTLC which is incoming from a btc channel and outgoing towards an asset channel, we need to create an HTLC record which reflects the asset related balance change. What really matter in these records is the total asset amount, but we also set the assetID field to be either equal to the specific asset we're sending, or the group of assets. This field does not dictate what the actual assets being sent are going to be, that will be decided later in the asset allocation process.
This allows us to get rid of a circular dependency issue that would occur in a follow up commit, where we import the adress package in the rfq package. Co-authored-by: Oliver Gugger <gugger@gmail.com>
We add the specifier checker interface to the AuxInvoiceManager too, as it is needed to validate incoming HTLCs which may use asset IDs that belong to a group, while the RFQ is based on a group key.
Adds some coverage to the invoice manager unit tests, which involve an RFQ quote over a group key, plus an HTLC with multiple asset balances, which may belong or not to the group.
We may be performing a group lookup on an asset that doesn't belong to a group. Instead of returning the error that originates from the ErrNoRows of sql we instead return a nil result, signalling that no group was found and no error occurred.
In some cases the sender of an asset HTLC may just encode the x part of the group key as the assetID of the balance in the custom records. This is done as a way to signal that any asset ID that belongs to this group may be picked to carry out the payment. This commit makes the specifier matcher aware of that case.
As mentioned in the previous commit, we occasionally want to just encode the hash of the group key as the asset ID of the balance that is encoded in the custom record. We make the ProduceHtlcExtraData hook aware of that case, which is triggered when the quote is made upon a group key and not a specific asset ID.
We add this new specifier creator which may accept both an asset id and group key. This is needed for callers who may have both fields available and simply want to derive an asset specifier from them. By default a specifier over the group key is prioritized as that's considered more generic over the specific asset id. This is used in a later commit.
We have taken care of the groupkey RFQ negotiation in previous commits. All we need now to support sending a payment over a group of assets, is to propagate the user specifier groupkey to the corresponding fields.
In this commit we take the user defined group key and allow the asset specifier to be created over it. All the calls that accept it as an argument are already groupkey aware.
e2e8e47 to
e9e1de8
Compare
Description
This PR takes care of the rest of the work required to create and pay invoices by using group keys.
group_keyonSendPaymentmeans that only channels that contain assets which belong to that group are going to be considered for sending this payment.group_keyonAddInvoicemeans that only channels that contain assets which belong to that group may be considered for generating an RFQ quote and encoding it in the invoice.LiT PR for itest