-
Notifications
You must be signed in to change notification settings - Fork 119
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
rfqmsg: request fields blip align #1157
Conversation
Pull Request Test Coverage Report for Build 11660754444Details
💛 - Coveralls |
We should also make sure the min+max fields are set properly, along with the transfer type. |
We should probably also remove the |
@guggero I would prefer to do that in a future PR. I want to prioritize BLIP alignment and address any bugs arising from misalignment issues. |
2507bcd
to
3fe960a
Compare
rfq/order.go
Outdated
// Convert the inbound asset amount to millisatoshis and ensure that the | ||
// outgoing HTLC amount is not more than the inbound asset amount. | ||
assetAmt := rfqmath.NewBigIntFixedPoint(c.AssetAmount, 0) | ||
assetAmtFp := rfqmath.BigIntFixedPoint{ |
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 think the constructor can still be used here?
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.
In the latest commits, I've introduced a new fixed-point method, SetIntValue
, which enables constructing a fixed-point instance from any integer value of the same type as the fixed-point coefficient's type:
assetAmtFp := new(rfqmath.BigIntFixedPoint).SetIntValue(assetAmt)
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.
Very nice improvements! Did a quick pass, will do a full review once ready.
0a96c9a
to
e1e8268
Compare
I think we should drop the What do you think @guggero ? |
I've added the And we need to reject based on the dust limit in relation to Any other use of these fields will probably be rewritten by the MPP OKR. |
I think we should merge this PR as is, subject to reviews. |
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.
Thanks for all the updates. I think we are fairly close here, but I have a couple of things that we should try to get in with this PR, as IMO the changes are within the scope of the PR:
- Rename the
SellRequest
'sAssetAmount
either toMaxInAsset
withuint64
or toMaxInboundAmount
withlnwire.MilliSatoshi
(depending on the decision where to make the switch, see inline comment). - Enforce the above max amount in the asset purchase policy.
- Since we don't have the "straightening out" of the buy/ask/purchase terminology I had in my WIP branch, and we still turn a buy request into an ask request for the oracle (which internally is then called a sale transaction), we should add that to the RFQ doc, best with a sequence diagram. Otherwise it will endlessly confuse me, and probably any authors of oracle logic as well.
We currently decide whether it's a buy or sell request based on which asset is BTC here: taproot-assets/rfqmsg/request.go Line 503 in e1e8268
Once we support asset-to-asset conversion directly, how would we distinguish between buy and sell? Just the direction of the in vs. out asset? |
744f051
to
35f89bd
Compare
IMO we should also drop the buy/sell types when we move to support asset-to-asset. So the transfer type is not needed then either. |
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.
Almost there, thanks a lot for sticking to this huge undertaking 💯
@@ -106,7 +106,7 @@ func NewNegotiator(cfg NegotiatorCfg) (*Negotiator, error) { | |||
// queryBidFromPriceOracle queries the price oracle for a bid price. It returns | |||
// an appropriate outgoing response message which should be sent to the peer. | |||
func (n *Negotiator) queryBidFromPriceOracle(peer route.Vertex, | |||
assetSpecifier asset.Specifier, assetAmount uint64, | |||
assetSpecifier asset.Specifier, assetMaxAmt fn.Option[uint64], |
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.
Is the assetMaxAmount
used anywhere anymore after this series of commits? Can't we just remove it, and make the paymentMaxAmount
a non-option and just use that everywhere?
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.
Is the assetMaxAmount used anywhere anymore after this series of commits?
assetMaxAmt
is passed on to n.cfg.PriceOracle.QueryBidPrice
below. It gets passed to the price oracle service.
Can't we just remove it, and make the
paymentMaxAmount
a non-option and just use that everywhere?
These two fields relate to two different assets.
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.
Okay, I see. Confusion on my part, see comment below with a request for documenting this a bit more.
// payment_asset_max_amount is the maximum amount of the payment asset that | ||
// could be involved in the transaction. This field is optional. If set to | ||
// zero, it is considered unset. | ||
uint64 payment_asset_max_amount = 5; |
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.
Do we need this field for an actual use case today? I think we should just remove it to make things easier to understand... See my other comments as well.
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.
MaxInAsset
sometimes relates to BTC and sometimes to a tap asset.
The two different fields in the price oracle relate to the two different assets that we're tying to find an exchange rate for. Instead of a single field that is sometimes the upper bound on the tap asset and sometimes an upper bound on the BTC side, I've got two different fields.
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.
Okay, I guess I see where my confusion is coming from. It's because we still do the "flip" of the transaction type for the oracle (which I eliminated in my WIP refactor PR).
What I mean is:
- On the wallet end user side, a
SellOrder
goes into the oracle as anask
(transaction typesale
withpayment_assset_max_amount
set) - On the edge node, that same
SellOrder
goes into the oracle as abid
(transaction typepurchase
withpayment_max_amount
set)
And then for a BuyOrder
it would be kind of the other way around.
Can we please add all of this to the RFQ documentation?
And perhaps update the comments in this proto file to make it more clear to the developer of an oracle when what field is set? For example that never both _max_amount
fields are set at the same time?
@@ -177,9 +177,12 @@ func (n *Negotiator) HandleOutgoingBuyOrder(buyOrder BuyOrder) error { | |||
|
|||
if n.cfg.PriceOracle != nil && assetSpecifier.IsSome() { | |||
// Query the price oracle for a bid price. | |||
// | |||
// TODO(ffranr): Add assetMaxAmt to BuyOrder and use as |
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.
This TODO remains unresolved and we don't pass any amount to the oracle.
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.
Adding and populating that field in the BuyOrder
would require an RPC change. I mean to do that in a future PR. I think this PR is getting too long.
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.
But at this specific call site, we only pass in fn.None
for all the values. So we're not passing any amount when requesting a rate for the hint. Is that an allowed way to call into the oracle, with none of the _max_amount
fields set?
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 think it should technically be a valid way of calling the price oracle. If upper bounds aren't given then the price would just be less accurate.
But we should complete the TODO. I'll add the correct field to BuyOrder
in a separate PR.
Rearranged the fields in the requestWireMsgData struct to match the order of TLV record integers, improving code readability and clarity.
This change aligns the field name with the Tap BLIP.
In this context, "selling" refers to divesting a tap asset in exchange for BTC, meaning the tap asset is outgoing from the RFQ request initiator's perspective. The `SellRequest` struct now uses the OutAssetRateHint field as the suggested asset rate.
Renamed the field to MaxInAsset to better represent the maximum quantity of in-asset that the counterparty is expected to divest, whether it's BTC or another asset. This change aligns the field name with the TLV type specified in the BLIP.
This commit adds a new constructor, `NewSpecifier`, for creating an `asset.Specifier`. The constructor accepts an asset ID, a group public key, or a group key to identify the asset. It includes a `mustBeSpecified` parameter, which, when set to true, enforces that at least one of the asset ID, group public key, or group key must be provided. If none are specified, an error is returned.
Add a helper function to create a BigInt from a `uint64`.
Refactor the unit test to make it easier to extend. A subsequent commit will add tests for a new method.
Add the SumAssetBalance method to the Htlc record, which represents capacity changes for an in-flight HTLC. This method returns the total asset balance for the specified asset.
Introduce a general method for constructing a fixed-point value from an integer. For example: ``` assetAmtFp := new(rfqmath.BigIntFixedPoint).SetIntValue(assetAmt) ```
Update `AssetPurchasePolicy` to include an asset specifier, which is used to select and sum asset balances in an in-flight HTLC. This ensures the outgoing msat amount of the HTLC is not less than the incoming Tap asset amount in msat. This commit fixes an issue where the asset amount used was incorrectly set on the policy instead of being derived from the in-flight HTLC. The erroneous asset amount field in the policy has been removed.
Rename field to better reflect its purpose.
Replace the asset ID and group key fields in the BuyRequest structure with a single AssetSpecifier field. This change simplifies how asset specifier information is handled.
35f89bd
to
98080aa
Compare
Add a new `payment_asset_max_amount` field to the `QueryAssetRatesRequest` RPC message in the RFQ price oracle. This field optionally allows callers to specify an upper limit for the payment asset amount in their requests. A future commit will populate and utilize this field in the RFQ package.
Rename the `assetAmount` argument to `assetMaxAmt` to better reflect its purpose as an upper bound on the asset amount. This change improves clarity in the codebase.
This commit introduces a new field, `SellRequest.PaymentMaxAmt`, replacing the previous `AssetAmount` field with a different type and purpose. This change allows us to populate `PaymentMaxAmt` with `MaxInAsset` from request wire messages, reflecting the maximum payment amount more accurately.
This commit removes the `max_asset_amount` and `min_ask` fields from the `SellOrder` type and introduces a new `payment_max_amt` field. The removed fields were not used correctly. The new `payment_max_amt` field allows us to leverage the `MaxInAmount` from the RFQ request wire message.
This commit adds a check to ensure that the outgoing HTLC `msat` amount does not exceed the `PaymentMaxAmt` specified in the RFQ quote.
98080aa
to
c5d356e
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.
Okay, I think I finally understand all the changes and aligned my own mental model to them. Would love to have a bit more documentation around that, but not blocking this PR on it. So LGTM, nice work! 🎉
// payment_asset_max_amount is the maximum amount of the payment asset that | ||
// could be involved in the transaction. This field is optional. If set to | ||
// zero, it is considered unset. | ||
uint64 payment_asset_max_amount = 5; |
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.
Okay, I guess I see where my confusion is coming from. It's because we still do the "flip" of the transaction type for the oracle (which I eliminated in my WIP refactor PR).
What I mean is:
- On the wallet end user side, a
SellOrder
goes into the oracle as anask
(transaction typesale
withpayment_assset_max_amount
set) - On the edge node, that same
SellOrder
goes into the oracle as abid
(transaction typepurchase
withpayment_max_amount
set)
And then for a BuyOrder
it would be kind of the other way around.
Can we please add all of this to the RFQ documentation?
And perhaps update the comments in this proto file to make it more clear to the developer of an oracle when what field is set? For example that never both _max_amount
fields are set at the same time?
@@ -106,7 +106,7 @@ func NewNegotiator(cfg NegotiatorCfg) (*Negotiator, error) { | |||
// queryBidFromPriceOracle queries the price oracle for a bid price. It returns | |||
// an appropriate outgoing response message which should be sent to the peer. | |||
func (n *Negotiator) queryBidFromPriceOracle(peer route.Vertex, | |||
assetSpecifier asset.Specifier, assetAmount uint64, | |||
assetSpecifier asset.Specifier, assetMaxAmt fn.Option[uint64], |
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.
Okay, I see. Confusion on my part, see comment below with a request for documenting this a bit more.
@@ -177,9 +177,12 @@ func (n *Negotiator) HandleOutgoingBuyOrder(buyOrder BuyOrder) error { | |||
|
|||
if n.cfg.PriceOracle != nil && assetSpecifier.IsSome() { | |||
// Query the price oracle for a bid price. | |||
// | |||
// TODO(ffranr): Add assetMaxAmt to BuyOrder and use as |
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.
But at this specific call site, we only pass in fn.None
for all the values. So we're not passing any amount when requesting a rate for the hint. Is that an allowed way to call into the oracle, with none of the _max_amount
fields set?
Further work to be included in a separate PR:
@guggero are you also ok with the following:
|
Sure, let's do all of that in a follow-up PR, then we can discuss there.
I still don't fully see how that would work, given that we'd still need to know whether to ask the oracle for an ask or bid. Or would we also remove that directionality there and only have a single RPC method? |
@guggero Yes, I think we only need a single oracle interface method. The oracle needs to know what asset is inbound and which is outbound and then it can return the ask/bid based on that. We can do that now with the oracle RPC endpoint by setting payment asset, subject asset, and the transfer type. But we can simplify the RPC endpoint's messages further also. |
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.
Very clean commits 💯
LGTM, straight forward changes ✅
have some minor Qs/nits, non-blocking
@@ -417,6 +417,12 @@ func (s *Specifier) UnwrapGroupKeyToPtr() *btcec.PublicKey { | |||
return s.groupKey.UnwrapToPtr() | |||
} | |||
|
|||
// UnwrapToPtr unwraps the asset ID and asset group public key fields, |
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.
nit: could squash this and 2 previous commits to something like "asset: add helper methods"
In this PR, we align the request RFQ wire message fields with the tap BLIP.
All TLV record values are already aligned with the BLIP due to previous PRs. This PR introduces internal changes to
tapd
to ensure therequest
message fields are handled, named, and interpreted correctly. These changes are non-interface and non-breaking.This will be the last RFQ BLIP alignment PR.