@@ -2,6 +2,8 @@ package rfqmsg
22
33import (
44 "fmt"
5+ "math"
6+ "time"
57
68 "github.com/btcsuite/btcd/btcec/v2"
79 "github.com/lightninglabs/taproot-assets/asset"
@@ -40,33 +42,39 @@ type BuyRequest struct {
4042 // requesting a quote.
4143 AssetAmount uint64
4244
43- // SuggestedAssetRate represents a proposed conversion rate between the
45+ // AssetRateHint represents a proposed conversion rate between the
4446 // subject asset and BTC. This rate is an initial suggestion intended to
4547 // initiate the RFQ negotiation process and may differ from the final
4648 // agreed rate.
47- SuggestedAssetRate fn.Option [rfqmath. BigIntFixedPoint ]
49+ AssetRateHint fn.Option [AssetRate ]
4850}
4951
5052// NewBuyRequest creates a new asset buy quote request.
5153func NewBuyRequest (peer route.Vertex , assetID * asset.ID ,
5254 assetGroupKey * btcec.PublicKey , assetAmount uint64 ,
53- suggestedAssetRate fn.Option [rfqmath.BigIntFixedPoint ]) (* BuyRequest ,
54- error ) {
55+ rateHint fn.Option [rfqmath.BigIntFixedPoint ]) (* BuyRequest , error ) {
5556
5657 id , err := NewID ()
5758 if err != nil {
5859 return nil , fmt .Errorf ("unable to generate random " +
5960 "quote request id: %w" , err )
6061 }
6162
63+ // Construct a suggested asset rate if a rate hint is provided.
64+ var assetRateHint fn.Option [AssetRate ]
65+ rateHint .WhenSome (func (rate rfqmath.BigIntFixedPoint ) {
66+ expiry := time .Now ().Add (DefaultQuoteLifetime ).UTC ()
67+ assetRateHint = fn .Some (NewAssetRate (rate , expiry ))
68+ })
69+
6270 return & BuyRequest {
63- Peer : peer ,
64- Version : latestBuyRequestVersion ,
65- ID : id ,
66- AssetID : assetID ,
67- AssetGroupKey : assetGroupKey ,
68- AssetAmount : assetAmount ,
69- SuggestedAssetRate : suggestedAssetRate ,
71+ Peer : peer ,
72+ Version : latestBuyRequestVersion ,
73+ ID : id ,
74+ AssetID : assetID ,
75+ AssetGroupKey : assetGroupKey ,
76+ AssetAmount : assetAmount ,
77+ AssetRateHint : assetRateHint ,
7078 }, nil
7179}
7280
@@ -102,24 +110,30 @@ func NewBuyRequestFromWire(wireMsg WireMessage,
102110 "request" )
103111 }
104112
113+ // Convert the wire message expiration time to a time.Time.
114+ if msgData .Expiry .Val > math .MaxInt64 {
115+ return nil , fmt .Errorf ("expiry time exceeds maximum int64" )
116+ }
117+
118+ expiry := time .Unix (int64 (msgData .Expiry .Val ), 0 )
119+
105120 // Extract the suggested asset to BTC rate if provided.
106- var suggestedAssetRate fn.Option [rfqmath. BigIntFixedPoint ]
121+ var assetRateHint fn.Option [AssetRate ]
107122 msgData .SuggestedAssetRate .WhenSome (
108123 func (rate tlv.RecordT [tlv.TlvType19 , TlvFixedPoint ]) {
109124 fp := rate .Val .IntoBigIntFixedPoint ()
110- suggestedAssetRate =
111- fn.Some [rfqmath.BigIntFixedPoint ](fp )
125+ assetRateHint = fn .Some (NewAssetRate (fp , expiry ))
112126 },
113127 )
114128
115129 req := BuyRequest {
116- Peer : wireMsg .Peer ,
117- Version : msgData .Version .Val ,
118- ID : msgData .ID .Val ,
119- AssetID : assetID ,
120- AssetGroupKey : assetGroupKey ,
121- AssetAmount : msgData .AssetMaxAmount .Val ,
122- SuggestedAssetRate : suggestedAssetRate ,
130+ Peer : wireMsg .Peer ,
131+ Version : msgData .Version .Val ,
132+ ID : msgData .ID .Val ,
133+ AssetID : assetID ,
134+ AssetGroupKey : assetGroupKey ,
135+ AssetAmount : msgData .AssetMaxAmount .Val ,
136+ AssetRateHint : assetRateHint ,
123137 }
124138
125139 // Perform basic sanity checks on the quote request.
@@ -148,6 +162,17 @@ func (q *BuyRequest) Validate() error {
148162 q .Version )
149163 }
150164
165+ // Ensure that the suggested asset rate has not expired.
166+ err := fn .MapOptionZ (q .AssetRateHint , func (rate AssetRate ) error {
167+ if rate .Expiry .Before (time .Now ()) {
168+ return fmt .Errorf ("suggested asset rate has expired" )
169+ }
170+ return nil
171+ })
172+ if err != nil {
173+ return err
174+ }
175+
151176 return nil
152177}
153178
@@ -195,10 +220,19 @@ func (q *BuyRequest) String() string {
195220 groupKeyBytes = q .AssetGroupKey .SerializeCompressed ()
196221 }
197222
223+ // Convert the asset rate hint to a string representation. Use empty
224+ // string if the hint is not set.
225+ assetRateHintStr := fn .MapOptionZ (
226+ q .AssetRateHint ,
227+ func (rate AssetRate ) string {
228+ return rate .String ()
229+ },
230+ )
231+
198232 return fmt .Sprintf ("BuyRequest(peer=%x, id=%x, asset_id=%s, " +
199- "asset_group_key=%x, asset_amount=%d, " +
200- "suggested_asset_rate=%v)" , q .Peer [:], q .ID [:], q .AssetID ,
201- groupKeyBytes , q . AssetAmount , q . SuggestedAssetRate )
233+ "asset_group_key=%x, asset_amount=%d, asset_rate_hint=%s)" ,
234+ q .Peer [:], q .ID [:], q .AssetID , groupKeyBytes , q . AssetAmount ,
235+ assetRateHintStr )
202236}
203237
204238// Ensure that the message type implements the OutgoingMsg interface.
0 commit comments