From 02ec9f6c5f852c4539db2fc49c1d8cc230398c29 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 16:42:10 +0800 Subject: [PATCH 01/21] rfqmsg: add AssetRateLimit TLV 29 wire field to request Add a new optional TLV field (Type 29, TlvFixedPoint) to requestWireMsgData for carrying rate limit constraints on quote requests. This field supports both buy requests (minimum acceptable rate) and sell requests (maximum acceptable rate). Wire encode/decode follows the existing InAssetRateHint pattern. --- rfqmsg/request.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rfqmsg/request.go b/rfqmsg/request.go index 477f0a6e2..32c396a27 100644 --- a/rfqmsg/request.go +++ b/rfqmsg/request.go @@ -59,6 +59,13 @@ type ( // the optional metadata that can be included in a quote request to // provide additional context to the price oracle. requestOracleMetadata = tlv.OptionalRecordT[tlv.TlvType27, []byte] + + // requestAssetRateLimit is a type alias for a record that + // represents an optional rate limit constraint on the quote + // request. + requestAssetRateLimit = tlv.OptionalRecordT[ + tlv.TlvType29, TlvFixedPoint, + ] ) // requestWireMsgData is a struct that represents the message data field for @@ -138,6 +145,11 @@ type requestWireMsgData struct { // price oracle can use to give out a more accurate (or discount) asset // rate. The maximum length of this field is 32'768 bytes. PriceOracleMetadata requestOracleMetadata + + // AssetRateLimit is an optional rate limit constraint. For buy + // requests this is the minimum acceptable rate; for sell requests + // this is the maximum acceptable rate. + AssetRateLimit requestAssetRateLimit } // newRequestWireMsgDataFromBuy creates a new requestWireMsgData from a buy @@ -431,6 +443,11 @@ func (m *requestWireMsgData) Encode(w io.Writer) error { records = append(records, r.Record()) }, ) + m.AssetRateLimit.WhenSome( + func(r tlv.RecordT[tlv.TlvType29, TlvFixedPoint]) { + records = append(records, r.Record()) + }, + ) tlv.SortRecords(records) @@ -459,6 +476,7 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { minOutAsset := m.MinOutAsset.Zero() oracleMetadata := m.PriceOracleMetadata.Zero() + assetRateLimit := m.AssetRateLimit.Zero() // Create a tlv stream with all the fields. tlvStream, err := tlv.NewStream( @@ -482,6 +500,7 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { minOutAsset.Record(), oracleMetadata.Record(), + assetRateLimit.Record(), ) if err != nil { return err @@ -524,6 +543,9 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { if _, ok := tlvMap[oracleMetadata.TlvType()]; ok { m.PriceOracleMetadata = tlv.SomeRecordT(oracleMetadata) } + if _, ok := tlvMap[assetRateLimit.TlvType()]; ok { + m.AssetRateLimit = tlv.SomeRecordT(assetRateLimit) + } return nil } From 3220947217e530e9b943fe5564dde835d098da6c Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 16:50:13 +0800 Subject: [PATCH 02/21] rfqmsg+rfq: surface min fill and rate limit fields Add AssetMinAmt and AssetRateLimit to BuyRequest, and PaymentMinAmt and AssetRateLimit to SellRequest. Update constructors, FromWire extraction, Validate (min <= max, rate positive), String output, and wire bridging functions. Add corresponding fields to BuyOrder and SellOrder structs and thread them through the negotiator's outgoing order handlers. Existing callers pass fn.None for both new params. --- rfq/negotiator.go | 6 ++- rfq/order.go | 14 ++++++ rfq/portfolio_pilot_test.go | 31 ++++++++++++- rfqmsg/buy_request.go | 80 ++++++++++++++++++++++++++++++-- rfqmsg/request.go | 42 +++++++++++++++++ rfqmsg/sell_request.go | 92 +++++++++++++++++++++++++++++++++++-- 6 files changed, 255 insertions(+), 10 deletions(-) diff --git a/rfq/negotiator.go b/rfq/negotiator.go index 569bea1fc..97ff70815 100644 --- a/rfq/negotiator.go +++ b/rfq/negotiator.go @@ -236,6 +236,7 @@ func (n *Negotiator) HandleOutgoingBuyOrder(ctx context.Context, // Construct a new buy request to send to the peer. request, err := rfqmsg.NewBuyRequest( peer, buyOrder.AssetSpecifier, buyOrder.AssetMaxAmt, + buyOrder.AssetMinAmt, buyOrder.AssetRateLimit, assetRateHint, buyOrder.PriceOracleMetadata, ) if err != nil { @@ -391,8 +392,9 @@ func (n *Negotiator) HandleOutgoingSellOrder(ctx context.Context, ) request, err := rfqmsg.NewSellRequest( - peer, order.AssetSpecifier, order.PaymentMaxAmt, assetRateHint, - order.PriceOracleMetadata, + peer, order.AssetSpecifier, order.PaymentMaxAmt, + order.PaymentMinAmt, order.AssetRateLimit, + assetRateHint, order.PriceOracleMetadata, ) if err != nil { diff --git a/rfq/order.go b/rfq/order.go index 68783e806..9a2c8c2a5 100644 --- a/rfq/order.go +++ b/rfq/order.go @@ -1733,6 +1733,13 @@ type BuyOrder struct { // be willing to offer. AssetMaxAmt uint64 + // AssetMinAmt is an optional minimum asset amount for the order. + AssetMinAmt fn.Option[uint64] + + // AssetRateLimit is an optional minimum acceptable rate (asset + // units per BTC) for the order. + AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // Expiry is the time at which the order expires. Expiry time.Time @@ -1800,6 +1807,13 @@ type SellOrder struct { // must agree to pay. PaymentMaxAmt lnwire.MilliSatoshi + // PaymentMinAmt is an optional minimum msat amount for the order. + PaymentMinAmt fn.Option[lnwire.MilliSatoshi] + + // AssetRateLimit is an optional maximum acceptable rate (asset + // units per BTC) for the order. + AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // Expiry is the time at which the order expires. Expiry time.Time diff --git a/rfq/portfolio_pilot_test.go b/rfq/portfolio_pilot_test.go index 86774ee46..f1a11d5b1 100644 --- a/rfq/portfolio_pilot_test.go +++ b/rfq/portfolio_pilot_test.go @@ -124,6 +124,8 @@ func TestResolveRequest(t *testing.T) { req, err := rfqmsg.NewBuyRequest( route.Vertex{0x01, 0x02, 0x03}, asset.NewSpecifierFromId(asset.ID{assetID}), 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), rateHint, "order-metadata", ) require.NoError(t, err) @@ -140,7 +142,10 @@ func TestResolveRequest(t *testing.T) { req, err := rfqmsg.NewSellRequest( route.Vertex{0x0A, 0x0B, 0x0C}, asset.NewSpecifierFromId(asset.ID{assetID}), - paymentMax, rateHint, "order-metadata", + paymentMax, + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + rateHint, "order-metadata", ) require.NoError(t, err) return req @@ -546,6 +551,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -574,6 +581,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -599,6 +608,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -629,6 +640,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -655,6 +668,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -680,6 +695,8 @@ func TestVerifyAcceptQuote(t *testing.T) { makeAccept: func(t *testing.T) rfqmsg.Accept { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -708,6 +725,8 @@ func TestVerifyAcceptQuote(t *testing.T) { sellReq, err := rfqmsg.NewSellRequest( peerID, assetSpec, lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -737,6 +756,8 @@ func TestVerifyAcceptQuote(t *testing.T) { sellReq, err := rfqmsg.NewSellRequest( peerID, assetSpec, lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -763,6 +784,8 @@ func TestVerifyAcceptQuote(t *testing.T) { sellReq, err := rfqmsg.NewSellRequest( peerID, assetSpec, lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -789,6 +812,8 @@ func TestVerifyAcceptQuote(t *testing.T) { sellReq, err := rfqmsg.NewSellRequest( peerID, assetSpec, lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -867,6 +892,8 @@ func TestResolveRequestWithoutPriceOracleRejects(t *testing.T) { req, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) @@ -906,6 +933,8 @@ func TestVerifyAcceptQuoteWithoutPriceOracle(t *testing.T) { buyReq, err := rfqmsg.NewBuyRequest( peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", ) diff --git a/rfqmsg/buy_request.go b/rfqmsg/buy_request.go index b61ed89a7..a1aa50304 100644 --- a/rfqmsg/buy_request.go +++ b/rfqmsg/buy_request.go @@ -8,6 +8,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" ) @@ -54,6 +55,16 @@ type BuyRequest struct { // peer must agree to divest. AssetMaxAmt uint64 + // AssetMinAmt is an optional minimum asset amount for the quote. + // When set, the responding peer must be willing to divest at + // least this many units. + AssetMinAmt fn.Option[uint64] + + // AssetRateLimit is an optional minimum acceptable rate (asset + // units per BTC). The buyer sets a floor: "I won't accept fewer + // than X units per BTC." + AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // AssetRateHint represents a proposed conversion rate between the // subject asset and BTC. This rate is an initial suggestion intended to // initiate the RFQ negotiation process and may differ from the final @@ -71,7 +82,9 @@ type BuyRequest struct { // NewBuyRequest creates a new asset buy quote request. func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, - assetMaxAmt uint64, assetRateHint fn.Option[AssetRate], + assetMaxAmt uint64, assetMinAmt fn.Option[uint64], + assetRateLimit fn.Option[rfqmath.BigIntFixedPoint], + assetRateHint fn.Option[AssetRate], oracleMetadata string) (*BuyRequest, error) { id, err := NewID() @@ -92,6 +105,8 @@ func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, ID: id, AssetSpecifier: assetSpecifier, AssetMaxAmt: assetMaxAmt, + AssetMinAmt: assetMinAmt, + AssetRateLimit: assetRateLimit, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, }, nil @@ -153,12 +168,31 @@ func NewBuyRequestFromWire(wireMsg WireMessage, }, ) + // Extract optional min asset amount. + var assetMinAmt fn.Option[uint64] + msgData.MinInAsset.WhenSome( + func(r tlv.RecordT[tlv.TlvType23, uint64]) { + assetMinAmt = fn.Some(r.Val) + }, + ) + + // Extract optional rate limit. + var assetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + msgData.AssetRateLimit.WhenSome( + func(r tlv.RecordT[tlv.TlvType29, TlvFixedPoint]) { + fp := r.Val.IntoBigIntFixedPoint() + assetRateLimit = fn.Some(fp) + }, + ) + req := BuyRequest{ Peer: wireMsg.Peer, Version: msgData.Version.Val, ID: msgData.ID.Val, AssetSpecifier: assetSpecifier, AssetMaxAmt: msgData.MaxInAsset.Val, + AssetMinAmt: assetMinAmt, + AssetRateLimit: assetRateLimit, AssetRateHint: assetRateHint, } @@ -195,6 +229,34 @@ func (q *BuyRequest) Validate() error { "length of %d bytes", MaxOracleMetadataLength) } + // Ensure min <= max when min is set. + err = fn.MapOptionZ(q.AssetMinAmt, func(minAmt uint64) error { + if minAmt > q.AssetMaxAmt { + return fmt.Errorf("asset min amount (%d) exceeds "+ + "max amount (%d)", minAmt, q.AssetMaxAmt) + } + return nil + }) + if err != nil { + return err + } + + // Ensure rate limit is positive when set. + err = fn.MapOptionZ( + q.AssetRateLimit, + func(limit rfqmath.BigIntFixedPoint) error { + zero := rfqmath.NewBigIntFromUint64(0) + if limit.Coefficient.Equals(zero) { + return fmt.Errorf("asset rate limit must " + + "be positive") + } + return nil + }, + ) + if err != nil { + return err + } + // Ensure that the suggested asset rate has not expired. err = fn.MapOptionZ(q.AssetRateHint, func(rate AssetRate) error { if rate.Expiry.Before(time.Now()) { @@ -261,10 +323,22 @@ func (q *BuyRequest) String() string { }, ) + minAmtStr := fn.MapOptionZ(q.AssetMinAmt, func(v uint64) string { + return fmt.Sprintf(", min_asset_amount=%d", v) + }) + + rateLimitStr := fn.MapOptionZ( + q.AssetRateLimit, + func(v rfqmath.BigIntFixedPoint) string { + return fmt.Sprintf(", asset_rate_limit=%s", + v.String()) + }, + ) + return fmt.Sprintf("BuyRequest(peer=%x, id=%x, asset=%s, "+ - "max_asset_amount=%d, asset_rate_hint=%s)", + "max_asset_amount=%d%s%s, asset_rate_hint=%s)", q.Peer[:], q.ID[:], q.AssetSpecifier.String(), q.AssetMaxAmt, - assetRateHintStr) + minAmtStr, rateLimitStr, assetRateHintStr) } // Ensure that the message type implements the OutgoingMsg interface. diff --git a/rfqmsg/request.go b/rfqmsg/request.go index 32c396a27..37bf692ea 100644 --- a/rfqmsg/request.go +++ b/rfqmsg/request.go @@ -8,7 +8,9 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/rfqmath" lfn "github.com/lightningnetwork/lnd/fn/v2" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/tlv" ) @@ -213,6 +215,23 @@ func newRequestWireMsgDataFromBuy(q BuyRequest) (requestWireMsgData, error) { ) } + // Set optional min asset amount. + var minInAsset tlv.OptionalRecordT[tlv.TlvType23, uint64] + q.AssetMinAmt.WhenSome(func(minAmt uint64) { + minInAsset = tlv.SomeRecordT[tlv.TlvType23]( + tlv.NewPrimitiveRecord[tlv.TlvType23](minAmt), + ) + }) + + // Set optional rate limit. + var assetRateLimit requestAssetRateLimit + q.AssetRateLimit.WhenSome(func(limit rfqmath.BigIntFixedPoint) { + wireRate := NewTlvFixedPointFromBigInt(limit) + assetRateLimit = tlv.SomeRecordT[tlv.TlvType29]( + tlv.NewRecordT[tlv.TlvType29](wireRate), + ) + }) + // Encode message data component as TLV bytes. return requestWireMsgData{ Version: version, @@ -225,6 +244,8 @@ func newRequestWireMsgDataFromBuy(q BuyRequest) (requestWireMsgData, error) { OutAssetGroupKey: outAssetGroupKey, MaxInAsset: maxInAsset, InAssetRateHint: inAssetRateHint, + MinInAsset: minInAsset, + AssetRateLimit: assetRateLimit, PriceOracleMetadata: oracleMetadata, }, nil } @@ -293,6 +314,25 @@ func newRequestWireMsgDataFromSell(q SellRequest) (requestWireMsgData, error) { ) } + // Set optional min payment amount. + var minOutAsset tlv.OptionalRecordT[tlv.TlvType25, uint64] + q.PaymentMinAmt.WhenSome(func(minAmt lnwire.MilliSatoshi) { + minOutAsset = tlv.SomeRecordT[tlv.TlvType25]( + tlv.NewPrimitiveRecord[tlv.TlvType25]( + uint64(minAmt), + ), + ) + }) + + // Set optional rate limit. + var assetRateLimit requestAssetRateLimit + q.AssetRateLimit.WhenSome(func(limit rfqmath.BigIntFixedPoint) { + wireRate := NewTlvFixedPointFromBigInt(limit) + assetRateLimit = tlv.SomeRecordT[tlv.TlvType29]( + tlv.NewRecordT[tlv.TlvType29](wireRate), + ) + }) + // Encode message data component as TLV bytes. return requestWireMsgData{ Version: version, @@ -304,6 +344,8 @@ func newRequestWireMsgDataFromSell(q SellRequest) (requestWireMsgData, error) { OutAssetGroupKey: outAssetGroupKey, MaxInAsset: maxInAsset, OutAssetRateHint: outAssetRateHint, + MinOutAsset: minOutAsset, + AssetRateLimit: assetRateLimit, PriceOracleMetadata: oracleMetadata, }, nil } diff --git a/rfqmsg/sell_request.go b/rfqmsg/sell_request.go index 4a581e71c..0771d4a8a 100644 --- a/rfqmsg/sell_request.go +++ b/rfqmsg/sell_request.go @@ -7,6 +7,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/tlv" @@ -52,6 +53,16 @@ type SellRequest struct { // must agree to pay. PaymentMaxAmt lnwire.MilliSatoshi + // PaymentMinAmt is an optional minimum msat amount for the quote. + // When set, the responding peer must agree to pay at least this + // much. + PaymentMinAmt fn.Option[lnwire.MilliSatoshi] + + // AssetRateLimit is an optional maximum acceptable rate (asset + // units per BTC). The seller sets a ceiling: "I won't give more + // than X units per BTC." + AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // AssetRateHint represents a proposed conversion rate between the // subject asset and BTC. This rate is an initial suggestion intended to // initiate the RFQ negotiation process and may differ from the final @@ -69,7 +80,10 @@ type SellRequest struct { // NewSellRequest creates a new asset sell quote request. func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, - paymentMaxAmt lnwire.MilliSatoshi, assetRateHint fn.Option[AssetRate], + paymentMaxAmt lnwire.MilliSatoshi, + paymentMinAmt fn.Option[lnwire.MilliSatoshi], + assetRateLimit fn.Option[rfqmath.BigIntFixedPoint], + assetRateHint fn.Option[AssetRate], oracleMetadata string) (*SellRequest, error) { id, err := NewID() @@ -89,6 +103,8 @@ func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, ID: id, AssetSpecifier: assetSpecifier, PaymentMaxAmt: paymentMaxAmt, + PaymentMinAmt: paymentMinAmt, + AssetRateLimit: assetRateLimit, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, }, nil @@ -147,6 +163,25 @@ func NewSellRequestFromWire(wireMsg WireMessage, }, ) + // Extract optional min payment amount. + var paymentMinAmt fn.Option[lnwire.MilliSatoshi] + msgData.MinOutAsset.WhenSome( + func(r tlv.RecordT[tlv.TlvType25, uint64]) { + paymentMinAmt = fn.Some( + lnwire.MilliSatoshi(r.Val), + ) + }, + ) + + // Extract optional rate limit. + var assetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + msgData.AssetRateLimit.WhenSome( + func(r tlv.RecordT[tlv.TlvType29, TlvFixedPoint]) { + fp := r.Val.IntoBigIntFixedPoint() + assetRateLimit = fn.Some(fp) + }, + ) + req := SellRequest{ Peer: wireMsg.Peer, Version: msgData.Version.Val, @@ -155,7 +190,9 @@ func NewSellRequestFromWire(wireMsg WireMessage, PaymentMaxAmt: lnwire.MilliSatoshi( msgData.MaxInAsset.Val, ), - AssetRateHint: assetRateHint, + PaymentMinAmt: paymentMinAmt, + AssetRateLimit: assetRateLimit, + AssetRateHint: assetRateHint, } msgData.PriceOracleMetadata.ValOpt().WhenSome(func(metaBytes []byte) { @@ -191,6 +228,38 @@ func (q *SellRequest) Validate() error { "length of %d bytes", MaxOracleMetadataLength) } + // Ensure min <= max when min is set. + err = fn.MapOptionZ( + q.PaymentMinAmt, + func(minAmt lnwire.MilliSatoshi) error { + if minAmt > q.PaymentMaxAmt { + return fmt.Errorf("payment min amount "+ + "(%d) exceeds max amount (%d)", + minAmt, q.PaymentMaxAmt) + } + return nil + }, + ) + if err != nil { + return err + } + + // Ensure rate limit is positive when set. + err = fn.MapOptionZ( + q.AssetRateLimit, + func(limit rfqmath.BigIntFixedPoint) error { + zero := rfqmath.NewBigIntFromUint64(0) + if limit.Coefficient.Equals(zero) { + return fmt.Errorf("asset rate limit must " + + "be positive") + } + return nil + }, + ) + if err != nil { + return err + } + return nil } @@ -246,10 +315,25 @@ func (q *SellRequest) String() string { }, ) + minAmtStr := fn.MapOptionZ( + q.PaymentMinAmt, + func(v lnwire.MilliSatoshi) string { + return fmt.Sprintf(", payment_min_amt=%d", v) + }, + ) + + rateLimitStr := fn.MapOptionZ( + q.AssetRateLimit, + func(v rfqmath.BigIntFixedPoint) string { + return fmt.Sprintf(", asset_rate_limit=%s", + v.String()) + }, + ) + return fmt.Sprintf("SellRequest(peer=%x, id=%x, asset=%s, "+ - "payment_max_amt=%d, asset_rate_hint=%s)", + "payment_max_amt=%d%s%s, asset_rate_hint=%s)", q.Peer[:], q.ID[:], q.AssetSpecifier.String(), q.PaymentMaxAmt, - assetRateHintStr) + minAmtStr, rateLimitStr, assetRateHintStr) } // Ensure that the message type implements the OutgoingMsg interface. From de0f36ccd1452bd11e529983b1ac0d62aa10f9f2 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 16:57:35 +0800 Subject: [PATCH 03/21] rfq+rpcserver: add proto fields, RPC marshalling, and reject codes Add min fill and rate limit fields to RFQ and portfolio pilot proto definitions. Update rpcserver unmarshalling, portfolio pilot RPC marshal/unmarshal functions, and add new reject codes and quote response status values. --- rfq/manager.go | 8 + rfq/portfolio_pilot_rpc.go | 43 +- rfqmsg/reject.go | 22 + rpcserver/rpcserver.go | 66 ++ .../portfoliopilotrpc/portfolio_pilot.pb.go | 460 ++++++----- .../portfoliopilotrpc/portfolio_pilot.proto | 32 + .../portfolio_pilot.swagger.json | 30 +- taprpc/rfqrpc/rfq.pb.go | 740 ++++++++++-------- taprpc/rfqrpc/rfq.proto | 20 + taprpc/rfqrpc/rfq.swagger.json | 18 + 10 files changed, 904 insertions(+), 535 deletions(-) diff --git a/rfq/manager.go b/rfq/manager.go index b424bddf6..5c6c1b3e1 100644 --- a/rfq/manager.go +++ b/rfq/manager.go @@ -1440,6 +1440,14 @@ const ( // ValidAcceptQuoteRespStatus indicates that the accepted quote passed // all validation checks successfully. ValidAcceptQuoteRespStatus QuoteRespStatus = 4 + + // MinFillNotMetQuoteRespStatus indicates that the minimum fill + // constraint was not satisfiable at the accepted rate. + MinFillNotMetQuoteRespStatus QuoteRespStatus = 5 + + // RateBoundMissQuoteRespStatus indicates that the accepted rate + // violated the requester's rate limit constraint. + RateBoundMissQuoteRespStatus QuoteRespStatus = 6 ) // InvalidQuoteRespEvent is an event that is broadcast when the RFQ manager diff --git a/rfq/portfolio_pilot_rpc.go b/rfq/portfolio_pilot_rpc.go index e0bdc965e..f82b9a4ef 100644 --- a/rfq/portfolio_pilot_rpc.go +++ b/rfq/portfolio_pilot_rpc.go @@ -8,8 +8,10 @@ import ( "time" "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" "github.com/lightninglabs/taproot-assets/rpcutils" + "github.com/lightningnetwork/lnd/lnwire" pilotrpc "github.com/lightninglabs/taproot-assets/taprpc/portfoliopilotrpc" "github.com/lightningnetwork/lnd/routing/route" "google.golang.org/grpc" @@ -441,14 +443,25 @@ func rpcMarshalBuyRequest( return nil, fmt.Errorf("marshal rate hint: %w", err) } + var rpcRateLimit *pilotrpc.FixedPoint + req.AssetRateLimit.WhenSome( + func(fp rfqmath.BigIntFixedPoint) { + rpcRateLimit = rpcutils.MarshalPortfolioFixedPoint(fp) + }, + ) + peer := req.MsgPeer() - rpcSpecifier := rpcMarshalPortfolioAssetSpecifier(req.AssetSpecifier) + rpcSpecifier := rpcMarshalPortfolioAssetSpecifier( + req.AssetSpecifier, + ) return &pilotrpc.BuyRequest{ AssetSpecifier: rpcSpecifier, AssetMaxAmount: req.AssetMaxAmt, AssetRateHint: rpcRateHint, PriceOracleMetadata: req.PriceOracleMetadata, PeerId: peer[:], + AssetMinAmount: req.AssetMinAmt.UnwrapOr(0), + AssetRateLimit: rpcRateLimit, }, nil } @@ -475,14 +488,32 @@ func rpcMarshalSellRequest( return nil, fmt.Errorf("marshal rate hint: %w", err) } + var rpcRateLimit *pilotrpc.FixedPoint + req.AssetRateLimit.WhenSome( + func(fp rfqmath.BigIntFixedPoint) { + rpcRateLimit = rpcutils.MarshalPortfolioFixedPoint(fp) + }, + ) + + var paymentMinAmt uint64 + req.PaymentMinAmt.WhenSome( + func(v lnwire.MilliSatoshi) { + paymentMinAmt = uint64(v) + }, + ) + peer := req.MsgPeer() - rpcSpecifier := rpcMarshalPortfolioAssetSpecifier(req.AssetSpecifier) + rpcSpecifier := rpcMarshalPortfolioAssetSpecifier( + req.AssetSpecifier, + ) return &pilotrpc.SellRequest{ AssetSpecifier: rpcSpecifier, PaymentMaxAmount: uint64(req.PaymentMaxAmt), AssetRateHint: rpcRateHint, PriceOracleMetadata: req.PriceOracleMetadata, PeerId: peer[:], + PaymentMinAmount: paymentMinAmt, + AssetRateLimit: rpcRateLimit, }, nil } @@ -573,6 +604,10 @@ func rpcUnmarshalQuoteRespStatus( return PortfolioPilotErrQuoteRespStatus, nil case pilotrpc.QuoteRespStatus_VALID_ACCEPT_QUOTE: return ValidAcceptQuoteRespStatus, nil + case pilotrpc.QuoteRespStatus_MIN_FILL_NOT_MET: + return MinFillNotMetQuoteRespStatus, nil + case pilotrpc.QuoteRespStatus_RATE_BOUND_MISS: + return RateBoundMissQuoteRespStatus, nil default: return 0, fmt.Errorf("unknown quote response status: %v", status) @@ -588,6 +623,10 @@ func rpcUnmarshalRejectCode( return rfqmsg.PriceOracleUnspecifiedRejectCode case pilotrpc.RejectCode_REJECT_CODE_PRICE_ORACLE_UNAVAILABLE: return rfqmsg.PriceOracleUnavailableRejectCode + case pilotrpc.RejectCode_REJECT_CODE_MIN_FILL_NOT_MET: + return rfqmsg.MinFillNotMetRejectCode + case pilotrpc.RejectCode_REJECT_CODE_PRICE_BOUND_MISS: + return rfqmsg.PriceBoundMissRejectCode default: return rfqmsg.PriceOracleUnspecifiedRejectCode } diff --git a/rfqmsg/reject.go b/rfqmsg/reject.go index 2988d6f01..ea675a686 100644 --- a/rfqmsg/reject.go +++ b/rfqmsg/reject.go @@ -89,6 +89,14 @@ const ( // PriceOracleUnavailableRejectCode indicates that a request-for-quote // was rejected as a price oracle was unavailable. PriceOracleUnavailableRejectCode RejectCode = 1 + + // MinFillNotMetRejectCode indicates that the minimum fill + // constraint was not satisfiable at the accepted rate. + MinFillNotMetRejectCode RejectCode = 2 + + // PriceBoundMissRejectCode indicates that the accepted rate + // violated the requester's rate limit constraint. + PriceBoundMissRejectCode RejectCode = 3 ) var ( @@ -105,6 +113,20 @@ var ( Code: PriceOracleUnavailableRejectCode, Msg: "price oracle unavailable", } + + // ErrMinFillNotMet is the error for when the minimum fill + // constraint cannot be met at the accepted rate. + ErrMinFillNotMet = RejectErr{ + Code: MinFillNotMetRejectCode, + Msg: "minimum fill not met", + } + + // ErrPriceBoundMiss is the error for when the accepted rate + // violates the requester's rate limit constraint. + ErrPriceBoundMiss = RejectErr{ + Code: PriceBoundMissRejectCode, + Msg: "rate limit constraint violated", + } ) // NewRejectErr produces the "unknown" error code, but pairs it with a diff --git a/rpcserver/rpcserver.go b/rpcserver/rpcserver.go index 77e688a04..481fce5da 100644 --- a/rpcserver/rpcserver.go +++ b/rpcserver/rpcserver.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "math" + "math/big" "net/http" "net/url" "sort" @@ -8325,6 +8326,35 @@ func parseAssetSpecifier(reqAssetID []byte, reqAssetIDStr string, } // unmarshalAssetBuyOrder unmarshals an asset buy order from the RPC form. +// unmarshalOptionalFixedPoint converts an optional RPC FixedPoint into an +// fn.Option[rfqmath.BigIntFixedPoint]. A nil input returns fn.None. +func unmarshalOptionalFixedPoint( + fp *rfqrpc.FixedPoint) (fn.Option[rfqmath.BigIntFixedPoint], error) { + + if fp == nil { + return fn.None[rfqmath.BigIntFixedPoint](), nil + } + + if fp.Scale > 255 { + return fn.None[rfqmath.BigIntFixedPoint](), + fmt.Errorf("scale value overflow: %v", fp.Scale) + } + + cBigInt := new(big.Int) + if _, ok := cBigInt.SetString(fp.Coefficient, 10); !ok { + return fn.None[rfqmath.BigIntFixedPoint](), + fmt.Errorf("invalid coefficient: %s", + fp.Coefficient) + } + + result := rfqmath.BigIntFixedPoint{ + Coefficient: rfqmath.NewBigInt(cBigInt), + Scale: uint8(fp.Scale), + } + + return fn.Some(result), nil +} + func unmarshalAssetBuyOrder( req *rfqrpc.AddAssetBuyOrderRequest) (*rfq.BuyOrder, error) { @@ -8370,9 +8400,26 @@ func unmarshalAssetBuyOrder( len(req.PriceOracleMetadata)) } + // Unmarshal optional min amount. + var assetMinAmt fn.Option[uint64] + if req.AssetMinAmt > 0 { + assetMinAmt = fn.Some(req.AssetMinAmt) + } + + // Unmarshal optional rate limit. + assetRateLimit, err := unmarshalOptionalFixedPoint( + req.AssetRateLimit, + ) + if err != nil { + return nil, fmt.Errorf("error unmarshalling asset rate "+ + "limit: %w", err) + } + return &rfq.BuyOrder{ AssetSpecifier: assetSpecifier, AssetMaxAmt: req.AssetMaxAmt, + AssetMinAmt: assetMinAmt, + AssetRateLimit: assetRateLimit, Expiry: expiry, Peer: fn.MaybeSome(peer), PriceOracleMetadata: req.PriceOracleMetadata, @@ -8586,9 +8633,28 @@ func unmarshalAssetSellOrder( len(req.PriceOracleMetadata)) } + // Unmarshal optional min payment amount. + var paymentMinAmt fn.Option[lnwire.MilliSatoshi] + if req.PaymentMinAmt > 0 { + paymentMinAmt = fn.Some( + lnwire.MilliSatoshi(req.PaymentMinAmt), + ) + } + + // Unmarshal optional rate limit. + assetRateLimit, err := unmarshalOptionalFixedPoint( + req.AssetRateLimit, + ) + if err != nil { + return nil, fmt.Errorf("error unmarshalling asset rate "+ + "limit: %w", err) + } + return &rfq.SellOrder{ AssetSpecifier: assetSpecifier, PaymentMaxAmt: lnwire.MilliSatoshi(req.PaymentMaxAmt), + PaymentMinAmt: paymentMinAmt, + AssetRateLimit: assetRateLimit, Expiry: expiry, Peer: peer, PriceOracleMetadata: req.PriceOracleMetadata, diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go b/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go index fabfd1afd..7d12ffa36 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go @@ -187,6 +187,12 @@ const ( // REJECT_CODE_PRICE_ORACLE_UNAVAILABLE indicates that pricing could not be // provided due to an unavailable oracle. RejectCode_REJECT_CODE_PRICE_ORACLE_UNAVAILABLE RejectCode = 1 + // REJECT_CODE_MIN_FILL_NOT_MET indicates that the minimum fill + // constraint was not satisfiable at the accepted rate. + RejectCode_REJECT_CODE_MIN_FILL_NOT_MET RejectCode = 2 + // REJECT_CODE_PRICE_BOUND_MISS indicates that the accepted rate + // violated the requester's rate limit constraint. + RejectCode_REJECT_CODE_PRICE_BOUND_MISS RejectCode = 3 ) // Enum value maps for RejectCode. @@ -194,10 +200,14 @@ var ( RejectCode_name = map[int32]string{ 0: "REJECT_CODE_UNSPECIFIED", 1: "REJECT_CODE_PRICE_ORACLE_UNAVAILABLE", + 2: "REJECT_CODE_MIN_FILL_NOT_MET", + 3: "REJECT_CODE_PRICE_BOUND_MISS", } RejectCode_value = map[string]int32{ "REJECT_CODE_UNSPECIFIED": 0, "REJECT_CODE_PRICE_ORACLE_UNAVAILABLE": 1, + "REJECT_CODE_MIN_FILL_NOT_MET": 2, + "REJECT_CODE_PRICE_BOUND_MISS": 3, } ) @@ -247,6 +257,12 @@ const ( // VALID_ACCEPT_QUOTE indicates that the accepted quote passed all // validation checks successfully. QuoteRespStatus_VALID_ACCEPT_QUOTE QuoteRespStatus = 4 + // MIN_FILL_NOT_MET indicates that the minimum fill constraint was + // not satisfiable at the accepted rate. + QuoteRespStatus_MIN_FILL_NOT_MET QuoteRespStatus = 5 + // RATE_BOUND_MISS indicates that the accepted rate violated the + // requester's rate limit constraint. + QuoteRespStatus_RATE_BOUND_MISS QuoteRespStatus = 6 ) // Enum value maps for QuoteRespStatus. @@ -257,6 +273,8 @@ var ( 2: "PRICE_ORACLE_QUERY_ERR", 3: "PORTFOLIO_PILOT_ERR", 4: "VALID_ACCEPT_QUOTE", + 5: "MIN_FILL_NOT_MET", + 6: "RATE_BOUND_MISS", } QuoteRespStatus_value = map[string]int32{ "INVALID_ASSET_RATES": 0, @@ -264,6 +282,8 @@ var ( "PRICE_ORACLE_QUERY_ERR": 2, "PORTFOLIO_PILOT_ERR": 3, "VALID_ACCEPT_QUOTE": 4, + "MIN_FILL_NOT_MET": 5, + "RATE_BOUND_MISS": 6, } ) @@ -626,6 +646,12 @@ type BuyRequest struct { PriceOracleMetadata string `protobuf:"bytes,4,opt,name=price_oracle_metadata,json=priceOracleMetadata,proto3" json:"price_oracle_metadata,omitempty"` // peer_id is the 33-byte public key of the counterparty peer. PeerId []byte `protobuf:"bytes,5,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` + // asset_min_amount is an optional minimum asset amount. A value of 0 + // means unset. + AssetMinAmount uint64 `protobuf:"varint,6,opt,name=asset_min_amount,json=assetMinAmount,proto3" json:"asset_min_amount,omitempty"` + // asset_rate_limit is an optional minimum acceptable rate (asset units + // per BTC). + AssetRateLimit *FixedPoint `protobuf:"bytes,7,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` } func (x *BuyRequest) Reset() { @@ -695,6 +721,20 @@ func (x *BuyRequest) GetPeerId() []byte { return nil } +func (x *BuyRequest) GetAssetMinAmount() uint64 { + if x != nil { + return x.AssetMinAmount + } + return 0 +} + +func (x *BuyRequest) GetAssetRateLimit() *FixedPoint { + if x != nil { + return x.AssetRateLimit + } + return nil +} + // SellRequest represents a request to sell the subject asset. type SellRequest struct { state protoimpl.MessageState @@ -712,6 +752,12 @@ type SellRequest struct { PriceOracleMetadata string `protobuf:"bytes,4,opt,name=price_oracle_metadata,json=priceOracleMetadata,proto3" json:"price_oracle_metadata,omitempty"` // peer_id is the 33-byte public key of the counterparty peer. PeerId []byte `protobuf:"bytes,5,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` + // payment_min_amount is an optional minimum msat amount. A value of 0 + // means unset. + PaymentMinAmount uint64 `protobuf:"varint,6,opt,name=payment_min_amount,json=paymentMinAmount,proto3" json:"payment_min_amount,omitempty"` + // asset_rate_limit is an optional maximum acceptable rate (asset units + // per BTC). + AssetRateLimit *FixedPoint `protobuf:"bytes,7,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` } func (x *SellRequest) Reset() { @@ -781,6 +827,20 @@ func (x *SellRequest) GetPeerId() []byte { return nil } +func (x *SellRequest) GetPaymentMinAmount() uint64 { + if x != nil { + return x.PaymentMinAmount + } + return 0 +} + +func (x *SellRequest) GetAssetRateLimit() *FixedPoint { + if x != nil { + return x.AssetRateLimit + } + return nil +} + // ResolveRequestRequest specifies a quote request to resolve. type ResolveRequestRequest struct { state protoimpl.MessageState @@ -1355,7 +1415,7 @@ var file_portfoliopilotrpc_portfolio_pilot_proto_rawDesc = []byte{ 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x95, 0x02, 0x0a, 0x0a, 0x42, 0x75, 0x79, 0x52, 0x65, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x88, 0x03, 0x0a, 0x0a, 0x42, 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, @@ -1372,170 +1432,192 @@ var file_portfoliopilotrpc_portfolio_pilot_proto_rawDesc = []byte{ 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0x9a, - 0x02, 0x0a, 0x0b, 0x53, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, - 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x61, - 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, - 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, - 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, - 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x15, - 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x40, 0x0a, 0x0b, 0x62, 0x75, 0x79, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, - 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x75, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x65, 0x6c, 0x6c, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x53, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, - 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x09, 0x0a, 0x07, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x92, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x36, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, - 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, - 0x48, 0x00, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x12, 0x36, 0x0a, 0x06, 0x72, 0x65, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x65, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xfd, 0x01, 0x0a, - 0x0d, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x17, - 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x61, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x52, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x62, 0x75, - 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, - 0x52, 0x0a, 0x62, 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, - 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, - 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x54, 0x0a, 0x18, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, + 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, + 0x69, 0x6e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, + 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x22, 0x91, 0x03, 0x0a, 0x0b, 0x53, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4a, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x72, + 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x2c, 0x0a, + 0x12, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x61, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, + 0x74, 0x65, 0x52, 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x48, 0x69, 0x6e, + 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2c, + 0x0a, 0x12, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x10, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xa9, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x40, 0x0a, 0x0b, 0x62, 0x75, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x75, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x92, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x06, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x06, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x12, 0x36, 0x0a, 0x06, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x45, + 0x72, 0x72, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x08, 0x0a, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xfd, 0x01, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x41, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x52, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x62, 0x75, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x75, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x75, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, + 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, + 0x73, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x54, 0x0a, 0x18, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, + 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, + 0x75, 0x6f, 0x74, 0x65, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x22, 0x57, 0x0a, 0x19, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x22, 0x57, 0x0a, 0x19, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x22, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xe8, 0x03, 0x0a, 0x16, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, - 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, - 0x65, 0x72, 0x12, 0x47, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x06, 0x69, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x6f, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xe8, 0x03, 0x0a, 0x16, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x4a, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x09, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x06, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x52, 0x06, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, + 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x41, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, + 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x44, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, - 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, - 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x56, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x61, 0x74, 0x65, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x2a, 0x87, - 0x01, 0x0a, 0x16, 0x41, 0x73, 0x73, 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, - 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x53, 0x53, - 0x45, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, - 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x52, 0x41, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x52, 0x61, 0x74, 0x65, 0x48, 0x69, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, + 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, + 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, + 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, + 0x65, 0x65, 0x72, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x56, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x52, 0x09, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x2a, 0x87, 0x01, 0x0a, 0x16, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x42, 0x55, 0x59, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x54, - 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x53, 0x45, 0x4c, 0x4c, 0x10, 0x02, 0x2a, 0xcd, 0x01, 0x0a, 0x06, 0x49, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x49, - 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, - 0x45, 0x5f, 0x48, 0x49, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x54, 0x45, - 0x4e, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x02, - 0x12, 0x1e, 0x0a, 0x1a, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x5f, 0x49, - 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x46, 0x59, 0x10, 0x03, - 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, - 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x48, 0x49, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x17, - 0x0a, 0x13, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x50, 0x41, - 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x49, 0x4e, 0x54, 0x45, 0x4e, - 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x51, - 0x55, 0x41, 0x4c, 0x49, 0x46, 0x59, 0x10, 0x06, 0x2a, 0x53, 0x0a, 0x0a, 0x52, 0x65, 0x6a, 0x65, - 0x63, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, - 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, - 0x44, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, - 0x55, 0x4e, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x2a, 0x8b, 0x01, - 0x0a, 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, - 0x45, 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, - 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, - 0x0a, 0x16, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, - 0x55, 0x45, 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, - 0x52, 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, - 0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, - 0x45, 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x32, 0xd1, 0x02, 0x0a, 0x0e, - 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x65, - 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x72, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, + 0x1c, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x5f, + 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x59, 0x10, 0x01, 0x12, + 0x21, 0x0a, 0x1d, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, + 0x52, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x4c, 0x4c, + 0x10, 0x02, 0x2a, 0xcd, 0x01, 0x0a, 0x06, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, + 0x12, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, + 0x50, 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x48, 0x49, 0x4e, 0x54, + 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x41, 0x59, + 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x49, 0x4e, + 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x41, 0x59, 0x5f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, + 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x46, 0x59, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, + 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, + 0x54, 0x5f, 0x48, 0x49, 0x4e, 0x54, 0x10, 0x04, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x54, 0x45, + 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x10, + 0x05, 0x12, 0x1f, 0x0a, 0x1b, 0x49, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x43, 0x56, + 0x5f, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x51, 0x55, 0x41, 0x4c, 0x49, 0x46, 0x59, + 0x10, 0x06, 0x2a, 0x97, 0x01, 0x0a, 0x0a, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6f, 0x64, + 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, + 0x0a, 0x24, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, + 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x55, 0x4e, 0x41, 0x56, 0x41, + 0x49, 0x4c, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x4a, 0x45, + 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x49, 0x4e, 0x5f, 0x46, 0x49, 0x4c, 0x4c, + 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, + 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, + 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x10, 0x03, 0x2a, 0xb6, 0x01, 0x0a, + 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, + 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, + 0x16, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, 0x55, + 0x45, 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, + 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, + 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, + 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x49, + 0x4e, 0x5f, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x10, 0x05, + 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, + 0x49, 0x53, 0x53, 0x10, 0x06, 0x32, 0xd1, 0x02, 0x0a, 0x0e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, + 0x6c, 0x69, 0x6f, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, - 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x0f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, - 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, - 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, - 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, - 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, - 0x63, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, - 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x6e, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, + 0x75, 0x6f, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, + 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, + 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x68, 0x0a, 0x0f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, + 0x65, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, + 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, + 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x6f, 0x72, 0x74, + 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1576,33 +1658,35 @@ var file_portfoliopilotrpc_portfolio_pilot_proto_depIdxs = []int32{ 6, // 1: portfoliopilotrpc.AssetRate.rate:type_name -> portfoliopilotrpc.FixedPoint 5, // 2: portfoliopilotrpc.BuyRequest.asset_specifier:type_name -> portfoliopilotrpc.AssetSpecifier 7, // 3: portfoliopilotrpc.BuyRequest.asset_rate_hint:type_name -> portfoliopilotrpc.AssetRate - 5, // 4: portfoliopilotrpc.SellRequest.asset_specifier:type_name -> portfoliopilotrpc.AssetSpecifier - 7, // 5: portfoliopilotrpc.SellRequest.asset_rate_hint:type_name -> portfoliopilotrpc.AssetRate - 8, // 6: portfoliopilotrpc.ResolveRequestRequest.buy_request:type_name -> portfoliopilotrpc.BuyRequest - 9, // 7: portfoliopilotrpc.ResolveRequestRequest.sell_request:type_name -> portfoliopilotrpc.SellRequest - 7, // 8: portfoliopilotrpc.ResolveRequestResponse.accept:type_name -> portfoliopilotrpc.AssetRate - 4, // 9: portfoliopilotrpc.ResolveRequestResponse.reject:type_name -> portfoliopilotrpc.RejectErr - 7, // 10: portfoliopilotrpc.AcceptedQuote.accepted_rate:type_name -> portfoliopilotrpc.AssetRate - 8, // 11: portfoliopilotrpc.AcceptedQuote.buy_request:type_name -> portfoliopilotrpc.BuyRequest - 9, // 12: portfoliopilotrpc.AcceptedQuote.sell_request:type_name -> portfoliopilotrpc.SellRequest - 12, // 13: portfoliopilotrpc.VerifyAcceptQuoteRequest.accept:type_name -> portfoliopilotrpc.AcceptedQuote - 3, // 14: portfoliopilotrpc.VerifyAcceptQuoteResponse.status:type_name -> portfoliopilotrpc.QuoteRespStatus - 5, // 15: portfoliopilotrpc.QueryAssetRatesRequest.asset_specifier:type_name -> portfoliopilotrpc.AssetSpecifier - 0, // 16: portfoliopilotrpc.QueryAssetRatesRequest.direction:type_name -> portfoliopilotrpc.AssetTransferDirection - 1, // 17: portfoliopilotrpc.QueryAssetRatesRequest.intent:type_name -> portfoliopilotrpc.Intent - 7, // 18: portfoliopilotrpc.QueryAssetRatesRequest.asset_rate_hint:type_name -> portfoliopilotrpc.AssetRate - 7, // 19: portfoliopilotrpc.QueryAssetRatesResponse.asset_rate:type_name -> portfoliopilotrpc.AssetRate - 10, // 20: portfoliopilotrpc.PortfolioPilot.ResolveRequest:input_type -> portfoliopilotrpc.ResolveRequestRequest - 13, // 21: portfoliopilotrpc.PortfolioPilot.VerifyAcceptQuote:input_type -> portfoliopilotrpc.VerifyAcceptQuoteRequest - 15, // 22: portfoliopilotrpc.PortfolioPilot.QueryAssetRates:input_type -> portfoliopilotrpc.QueryAssetRatesRequest - 11, // 23: portfoliopilotrpc.PortfolioPilot.ResolveRequest:output_type -> portfoliopilotrpc.ResolveRequestResponse - 14, // 24: portfoliopilotrpc.PortfolioPilot.VerifyAcceptQuote:output_type -> portfoliopilotrpc.VerifyAcceptQuoteResponse - 16, // 25: portfoliopilotrpc.PortfolioPilot.QueryAssetRates:output_type -> portfoliopilotrpc.QueryAssetRatesResponse - 23, // [23:26] is the sub-list for method output_type - 20, // [20:23] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 6, // 4: portfoliopilotrpc.BuyRequest.asset_rate_limit:type_name -> portfoliopilotrpc.FixedPoint + 5, // 5: portfoliopilotrpc.SellRequest.asset_specifier:type_name -> portfoliopilotrpc.AssetSpecifier + 7, // 6: portfoliopilotrpc.SellRequest.asset_rate_hint:type_name -> portfoliopilotrpc.AssetRate + 6, // 7: portfoliopilotrpc.SellRequest.asset_rate_limit:type_name -> portfoliopilotrpc.FixedPoint + 8, // 8: portfoliopilotrpc.ResolveRequestRequest.buy_request:type_name -> portfoliopilotrpc.BuyRequest + 9, // 9: portfoliopilotrpc.ResolveRequestRequest.sell_request:type_name -> portfoliopilotrpc.SellRequest + 7, // 10: portfoliopilotrpc.ResolveRequestResponse.accept:type_name -> portfoliopilotrpc.AssetRate + 4, // 11: portfoliopilotrpc.ResolveRequestResponse.reject:type_name -> portfoliopilotrpc.RejectErr + 7, // 12: portfoliopilotrpc.AcceptedQuote.accepted_rate:type_name -> portfoliopilotrpc.AssetRate + 8, // 13: portfoliopilotrpc.AcceptedQuote.buy_request:type_name -> portfoliopilotrpc.BuyRequest + 9, // 14: portfoliopilotrpc.AcceptedQuote.sell_request:type_name -> portfoliopilotrpc.SellRequest + 12, // 15: portfoliopilotrpc.VerifyAcceptQuoteRequest.accept:type_name -> portfoliopilotrpc.AcceptedQuote + 3, // 16: portfoliopilotrpc.VerifyAcceptQuoteResponse.status:type_name -> portfoliopilotrpc.QuoteRespStatus + 5, // 17: portfoliopilotrpc.QueryAssetRatesRequest.asset_specifier:type_name -> portfoliopilotrpc.AssetSpecifier + 0, // 18: portfoliopilotrpc.QueryAssetRatesRequest.direction:type_name -> portfoliopilotrpc.AssetTransferDirection + 1, // 19: portfoliopilotrpc.QueryAssetRatesRequest.intent:type_name -> portfoliopilotrpc.Intent + 7, // 20: portfoliopilotrpc.QueryAssetRatesRequest.asset_rate_hint:type_name -> portfoliopilotrpc.AssetRate + 7, // 21: portfoliopilotrpc.QueryAssetRatesResponse.asset_rate:type_name -> portfoliopilotrpc.AssetRate + 10, // 22: portfoliopilotrpc.PortfolioPilot.ResolveRequest:input_type -> portfoliopilotrpc.ResolveRequestRequest + 13, // 23: portfoliopilotrpc.PortfolioPilot.VerifyAcceptQuote:input_type -> portfoliopilotrpc.VerifyAcceptQuoteRequest + 15, // 24: portfoliopilotrpc.PortfolioPilot.QueryAssetRates:input_type -> portfoliopilotrpc.QueryAssetRatesRequest + 11, // 25: portfoliopilotrpc.PortfolioPilot.ResolveRequest:output_type -> portfoliopilotrpc.ResolveRequestResponse + 14, // 26: portfoliopilotrpc.PortfolioPilot.VerifyAcceptQuote:output_type -> portfoliopilotrpc.VerifyAcceptQuoteResponse + 16, // 27: portfoliopilotrpc.PortfolioPilot.QueryAssetRates:output_type -> portfoliopilotrpc.QueryAssetRatesResponse + 25, // [25:28] is the sub-list for method output_type + 22, // [22:25] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_portfoliopilotrpc_portfolio_pilot_proto_init() } diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.proto b/taprpc/portfoliopilotrpc/portfolio_pilot.proto index f2184580b..c875aa17b 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.proto +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.proto @@ -107,6 +107,14 @@ enum RejectCode { // REJECT_CODE_PRICE_ORACLE_UNAVAILABLE indicates that pricing could not be // provided due to an unavailable oracle. REJECT_CODE_PRICE_ORACLE_UNAVAILABLE = 1; + + // REJECT_CODE_MIN_FILL_NOT_MET indicates that the minimum fill + // constraint was not satisfiable at the accepted rate. + REJECT_CODE_MIN_FILL_NOT_MET = 2; + + // REJECT_CODE_PRICE_BOUND_MISS indicates that the accepted rate + // violated the requester's rate limit constraint. + REJECT_CODE_PRICE_BOUND_MISS = 3; } // QuoteRespStatus is an enum that represents the status of a quote response. @@ -130,6 +138,14 @@ enum QuoteRespStatus { // VALID_ACCEPT_QUOTE indicates that the accepted quote passed all // validation checks successfully. VALID_ACCEPT_QUOTE = 4; + + // MIN_FILL_NOT_MET indicates that the minimum fill constraint was + // not satisfiable at the accepted rate. + MIN_FILL_NOT_MET = 5; + + // RATE_BOUND_MISS indicates that the accepted rate violated the + // requester's rate limit constraint. + RATE_BOUND_MISS = 6; } // RejectErr captures a rejection reason for a quote request. @@ -220,6 +236,14 @@ message BuyRequest { // peer_id is the 33-byte public key of the counterparty peer. bytes peer_id = 5; + + // asset_min_amount is an optional minimum asset amount. A value of 0 + // means unset. + uint64 asset_min_amount = 6; + + // asset_rate_limit is an optional minimum acceptable rate (asset units + // per BTC). + FixedPoint asset_rate_limit = 7; } // SellRequest represents a request to sell the subject asset. @@ -239,6 +263,14 @@ message SellRequest { // peer_id is the 33-byte public key of the counterparty peer. bytes peer_id = 5; + + // payment_min_amount is an optional minimum msat amount. A value of 0 + // means unset. + uint64 payment_min_amount = 6; + + // asset_rate_limit is an optional maximum acceptable rate (asset units + // per BTC). + FixedPoint asset_rate_limit = 7; } // ResolveRequestRequest specifies a quote request to resolve. diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json b/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json index 443f9ef2e..99c6e8f96 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json @@ -215,6 +215,15 @@ "type": "string", "format": "byte", "description": "peer_id is the 33-byte public key of the counterparty peer." + }, + "asset_min_amount": { + "type": "string", + "format": "uint64", + "description": "asset_min_amount is an optional minimum asset amount. A value of 0\nmeans unset." + }, + "asset_rate_limit": { + "$ref": "#/definitions/portfoliopilotrpcFixedPoint", + "description": "asset_rate_limit is an optional minimum acceptable rate (asset units\nper BTC)." } }, "description": "BuyRequest represents a request to buy the subject asset." @@ -311,19 +320,23 @@ "INVALID_EXPIRY", "PRICE_ORACLE_QUERY_ERR", "PORTFOLIO_PILOT_ERR", - "VALID_ACCEPT_QUOTE" + "VALID_ACCEPT_QUOTE", + "MIN_FILL_NOT_MET", + "RATE_BOUND_MISS" ], "default": "INVALID_ASSET_RATES", - "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully." + "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully.\n - MIN_FILL_NOT_MET: MIN_FILL_NOT_MET indicates that the minimum fill constraint was\nnot satisfiable at the accepted rate.\n - RATE_BOUND_MISS: RATE_BOUND_MISS indicates that the accepted rate violated the\nrequester's rate limit constraint." }, "portfoliopilotrpcRejectCode": { "type": "string", "enum": [ "REJECT_CODE_UNSPECIFIED", - "REJECT_CODE_PRICE_ORACLE_UNAVAILABLE" + "REJECT_CODE_PRICE_ORACLE_UNAVAILABLE", + "REJECT_CODE_MIN_FILL_NOT_MET", + "REJECT_CODE_PRICE_BOUND_MISS" ], "default": "REJECT_CODE_UNSPECIFIED", - "description": "RejectCode represents the possible error codes that can be returned in a\nResolveRequestResponse reject result.\n\n - REJECT_CODE_UNSPECIFIED: REJECT_CODE_UNSPECIFIED indicates an unspecified error.\n - REJECT_CODE_PRICE_ORACLE_UNAVAILABLE: REJECT_CODE_PRICE_ORACLE_UNAVAILABLE indicates that pricing could not be\nprovided due to an unavailable oracle." + "description": "RejectCode represents the possible error codes that can be returned in a\nResolveRequestResponse reject result.\n\n - REJECT_CODE_UNSPECIFIED: REJECT_CODE_UNSPECIFIED indicates an unspecified error.\n - REJECT_CODE_PRICE_ORACLE_UNAVAILABLE: REJECT_CODE_PRICE_ORACLE_UNAVAILABLE indicates that pricing could not be\nprovided due to an unavailable oracle.\n - REJECT_CODE_MIN_FILL_NOT_MET: REJECT_CODE_MIN_FILL_NOT_MET indicates that the minimum fill\nconstraint was not satisfiable at the accepted rate.\n - REJECT_CODE_PRICE_BOUND_MISS: REJECT_CODE_PRICE_BOUND_MISS indicates that the accepted rate\nviolated the requester's rate limit constraint." }, "portfoliopilotrpcRejectErr": { "type": "object", @@ -391,6 +404,15 @@ "type": "string", "format": "byte", "description": "peer_id is the 33-byte public key of the counterparty peer." + }, + "payment_min_amount": { + "type": "string", + "format": "uint64", + "description": "payment_min_amount is an optional minimum msat amount. A value of 0\nmeans unset." + }, + "asset_rate_limit": { + "$ref": "#/definitions/portfoliopilotrpcFixedPoint", + "description": "asset_rate_limit is an optional maximum acceptable rate (asset units\nper BTC)." } }, "description": "SellRequest represents a request to sell the subject asset." diff --git a/taprpc/rfqrpc/rfq.pb.go b/taprpc/rfqrpc/rfq.pb.go index f6590578e..5790532cc 100644 --- a/taprpc/rfqrpc/rfq.pb.go +++ b/taprpc/rfqrpc/rfq.pb.go @@ -362,6 +362,14 @@ type AddAssetBuyOrderRequest struct { // This field is optional and can be left empty if no metadata is available. // The maximum length of this field is 32'768 bytes. PriceOracleMetadata string `protobuf:"bytes,7,opt,name=price_oracle_metadata,json=priceOracleMetadata,proto3" json:"price_oracle_metadata,omitempty"` + // The optional minimum amount of the asset that the provider must be + // willing to offer. If set, must be less than or equal to asset_max_amt. + // A value of 0 means unset. + AssetMinAmt uint64 `protobuf:"varint,8,opt,name=asset_min_amt,json=assetMinAmt,proto3" json:"asset_min_amt,omitempty"` + // An optional rate limit constraint expressed as a fixed-point number. + // For buy orders this is the minimum acceptable rate (asset units per + // BTC). If unset, no rate floor is enforced. + AssetRateLimit *FixedPoint `protobuf:"bytes,9,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` } func (x *AddAssetBuyOrderRequest) Reset() { @@ -445,6 +453,20 @@ func (x *AddAssetBuyOrderRequest) GetPriceOracleMetadata() string { return "" } +func (x *AddAssetBuyOrderRequest) GetAssetMinAmt() uint64 { + if x != nil { + return x.AssetMinAmt + } + return 0 +} + +func (x *AddAssetBuyOrderRequest) GetAssetRateLimit() *FixedPoint { + if x != nil { + return x.AssetRateLimit + } + return nil +} + type AddAssetBuyOrderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -577,6 +599,14 @@ type AddAssetSellOrderRequest struct { // This field is optional and can be left empty if no metadata is available. // The maximum length of this field is 32'768 bytes. PriceOracleMetadata string `protobuf:"bytes,7,opt,name=price_oracle_metadata,json=priceOracleMetadata,proto3" json:"price_oracle_metadata,omitempty"` + // The optional minimum msat amount that the responding peer must agree + // to pay. If set, must be less than or equal to payment_max_amt. + // A value of 0 means unset (units: millisats). + PaymentMinAmt uint64 `protobuf:"varint,8,opt,name=payment_min_amt,json=paymentMinAmt,proto3" json:"payment_min_amt,omitempty"` + // An optional rate limit constraint expressed as a fixed-point number. + // For sell orders this is the maximum acceptable rate (asset units per + // BTC). If unset, no rate ceiling is enforced. + AssetRateLimit *FixedPoint `protobuf:"bytes,9,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` } func (x *AddAssetSellOrderRequest) Reset() { @@ -660,6 +690,20 @@ func (x *AddAssetSellOrderRequest) GetPriceOracleMetadata() string { return "" } +func (x *AddAssetSellOrderRequest) GetPaymentMinAmt() uint64 { + if x != nil { + return x.PaymentMinAmt + } + return 0 +} + +func (x *AddAssetSellOrderRequest) GetAssetRateLimit() *FixedPoint { + if x != nil { + return x.AssetRateLimit + } + return nil +} + type AddAssetSellOrderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2174,7 +2218,7 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x65, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x22, - 0xce, 0x02, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, + 0xb0, 0x03, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, @@ -2195,306 +2239,318 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x22, 0xfa, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, - 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, - 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, - 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x72, 0x65, 0x6a, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xd3, 0x02, - 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, + 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x69, + 0x6e, 0x41, 0x6d, 0x74, 0x12, 0x3c, 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, + 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x22, 0xfa, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, + 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x45, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x72, + 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0xb9, 0x03, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x26, 0x0a, + 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, + 0x61, 0x78, 0x41, 0x6d, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, + 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x6b, 0x69, 0x70, + 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x6b, 0x69, 0x70, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x41, 0x6d, 0x74, 0x12, 0x3c, 0x0a, + 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xfc, 0x01, 0x0a, 0x19, + 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, + 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, + 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x71, 0x75, 0x6f, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, + 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x42, 0x0a, + 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x18, 0x41, 0x64, + 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, + 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, + 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x77, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, + 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, + 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x64, + 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x0a, 0x1e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, + 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3f, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, + 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xe8, 0x02, 0x0a, 0x14, 0x50, 0x65, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x61, 0x73, 0x6b, 0x5f, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, + 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x52, 0x0c, 0x61, 0x73, 0x6b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x32, + 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x22, 0xe0, 0x02, 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, + 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x62, 0x69, 0x64, + 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x62, 0x69, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, + 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x6d, 0x69, 0x6e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x73, 0x61, + 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, + 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, 0x6b, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x7f, 0x0a, 0x15, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, + 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x9e, 0x01, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, + 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x62, 0x75, 0x79, + 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x09, 0x62, 0x75, 0x79, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, + 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, + 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x6c, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x19, 0x50, 0x65, 0x65, 0x72, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x12, 0x53, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x1a, 0x50, 0x65, 0x65, + 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x56, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, + 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x43, 0x0a, + 0x0f, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, + 0x69, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x08, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x5a, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x18, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, + 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x61, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, + 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0xe7, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x70, - 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x78, - 0x41, 0x6d, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0c, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, - 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, - 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x22, 0xfc, 0x01, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, - 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, - 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, - 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, - 0x0a, 0x0e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x78, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, - 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, - 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, - 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, - 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1b, 0x0a, 0x19, + 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x71, 0x0a, 0x19, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x52, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdd, 0x03, 0x0a, + 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, + 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, + 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x1e, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, + 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, + 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x1b, + 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x09, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, + 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, 0x6e, + 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, + 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x4f, + 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x66, + 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, + 0x65, 0x72, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, + 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x2a, 0x8b, 0x01, 0x0a, + 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, + 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, + 0x16, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, 0x55, + 0x45, 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, + 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, + 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, + 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, + 0x71, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, + 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, + 0x41, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, + 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, + 0x45, 0x10, 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, + 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, + 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x17, 0x41, 0x64, 0x64, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, - 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, - 0x74, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, - 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, - 0x0a, 0x1e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, + 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, + 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, + 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, + 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, + 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x3f, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, - 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x22, 0xe8, 0x02, 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, - 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, - 0x61, 0x73, 0x6b, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, - 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x61, 0x73, 0x6b, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x36, - 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x15, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, - 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, - 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, 0xe0, 0x02, 0x0a, - 0x15, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, - 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x21, - 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, - 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x62, - 0x69, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, - 0x69, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, - 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, - 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, - 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, - 0x6b, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x7f, 0x0a, 0x15, - 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x9e, 0x01, - 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x52, 0x09, 0x62, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x3e, - 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, - 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, - 0x74, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x1f, - 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x8e, 0x01, 0x0a, 0x19, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, - 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x53, 0x0a, 0x17, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, - 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, - 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, - 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x22, 0x92, 0x01, 0x0a, 0x1a, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x56, 0x0a, - 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, - 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x15, - 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, - 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x08, 0x52, - 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x5a, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, - 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, - 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, - 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x70, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, - 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x15, 0x70, 0x65, 0x65, - 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, - 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x74, 0x6c, - 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x42, 0x07, - 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xe7, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, 0x6e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x65, - 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x22, 0x71, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, - 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, - 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdd, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, - 0x1c, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x1e, 0x0a, - 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x65, - 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, - 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x0b, - 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, - 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x36, - 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x04, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, - 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, - 0x72, 0x61, 0x74, 0x65, 0x2a, 0x8b, 0x01, 0x0a, 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, - 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, - 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, - 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, - 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, - 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, - 0x10, 0x04, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, 0x71, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, - 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x41, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, - 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, 0x45, 0x10, 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, - 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, - 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, - 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, - 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, - 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, - 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, - 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, - 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, + 0x1a, 0x27, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, - 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, - 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, - 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, - 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, - 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, - 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, + 0x66, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, + 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, + 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, + 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, + 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2542,51 +2598,53 @@ var file_rfqrpc_rfq_proto_goTypes = []any{ } var file_rfqrpc_rfq_proto_depIdxs = []int32{ 2, // 0: rfqrpc.AddAssetBuyOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 14, // 1: rfqrpc.AddAssetBuyOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote - 16, // 2: rfqrpc.AddAssetBuyOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse - 17, // 3: rfqrpc.AddAssetBuyOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse - 2, // 4: rfqrpc.AddAssetSellOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 15, // 5: rfqrpc.AddAssetSellOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedSellQuote - 16, // 6: rfqrpc.AddAssetSellOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse - 17, // 7: rfqrpc.AddAssetSellOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse - 2, // 8: rfqrpc.AddAssetSellOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 2, // 9: rfqrpc.AddAssetBuyOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 3, // 10: rfqrpc.PeerAcceptedBuyQuote.ask_asset_rate:type_name -> rfqrpc.FixedPoint - 13, // 11: rfqrpc.PeerAcceptedBuyQuote.asset_spec:type_name -> rfqrpc.AssetSpec - 3, // 12: rfqrpc.PeerAcceptedSellQuote.bid_asset_rate:type_name -> rfqrpc.FixedPoint - 13, // 13: rfqrpc.PeerAcceptedSellQuote.asset_spec:type_name -> rfqrpc.AssetSpec - 0, // 14: rfqrpc.InvalidQuoteResponse.status:type_name -> rfqrpc.QuoteRespStatus - 14, // 15: rfqrpc.QueryPeerAcceptedQuotesResponse.buy_quotes:type_name -> rfqrpc.PeerAcceptedBuyQuote - 15, // 16: rfqrpc.QueryPeerAcceptedQuotesResponse.sell_quotes:type_name -> rfqrpc.PeerAcceptedSellQuote - 14, // 17: rfqrpc.PeerAcceptedBuyQuoteEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote - 15, // 18: rfqrpc.PeerAcceptedSellQuoteEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuote - 20, // 19: rfqrpc.RfqEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuoteEvent - 21, // 20: rfqrpc.RfqEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuoteEvent - 22, // 21: rfqrpc.RfqEvent.accept_htlc:type_name -> rfqrpc.AcceptHtlcEvent - 2, // 22: rfqrpc.ForwardingHistoryRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 26, // 23: rfqrpc.ForwardingHistoryResponse.forwards:type_name -> rfqrpc.ForwardingEvent - 1, // 24: rfqrpc.ForwardingEvent.policy_type:type_name -> rfqrpc.RfqPolicyType - 13, // 25: rfqrpc.ForwardingEvent.asset_spec:type_name -> rfqrpc.AssetSpec - 3, // 26: rfqrpc.ForwardingEvent.rate:type_name -> rfqrpc.FixedPoint - 4, // 27: rfqrpc.Rfq.AddAssetBuyOrder:input_type -> rfqrpc.AddAssetBuyOrderRequest - 6, // 28: rfqrpc.Rfq.AddAssetSellOrder:input_type -> rfqrpc.AddAssetSellOrderRequest - 8, // 29: rfqrpc.Rfq.AddAssetSellOffer:input_type -> rfqrpc.AddAssetSellOfferRequest - 10, // 30: rfqrpc.Rfq.AddAssetBuyOffer:input_type -> rfqrpc.AddAssetBuyOfferRequest - 12, // 31: rfqrpc.Rfq.QueryPeerAcceptedQuotes:input_type -> rfqrpc.QueryPeerAcceptedQuotesRequest - 19, // 32: rfqrpc.Rfq.SubscribeRfqEventNtfns:input_type -> rfqrpc.SubscribeRfqEventNtfnsRequest - 24, // 33: rfqrpc.Rfq.ForwardingHistory:input_type -> rfqrpc.ForwardingHistoryRequest - 5, // 34: rfqrpc.Rfq.AddAssetBuyOrder:output_type -> rfqrpc.AddAssetBuyOrderResponse - 7, // 35: rfqrpc.Rfq.AddAssetSellOrder:output_type -> rfqrpc.AddAssetSellOrderResponse - 9, // 36: rfqrpc.Rfq.AddAssetSellOffer:output_type -> rfqrpc.AddAssetSellOfferResponse - 11, // 37: rfqrpc.Rfq.AddAssetBuyOffer:output_type -> rfqrpc.AddAssetBuyOfferResponse - 18, // 38: rfqrpc.Rfq.QueryPeerAcceptedQuotes:output_type -> rfqrpc.QueryPeerAcceptedQuotesResponse - 23, // 39: rfqrpc.Rfq.SubscribeRfqEventNtfns:output_type -> rfqrpc.RfqEvent - 25, // 40: rfqrpc.Rfq.ForwardingHistory:output_type -> rfqrpc.ForwardingHistoryResponse - 34, // [34:41] is the sub-list for method output_type - 27, // [27:34] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 3, // 1: rfqrpc.AddAssetBuyOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint + 14, // 2: rfqrpc.AddAssetBuyOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote + 16, // 3: rfqrpc.AddAssetBuyOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse + 17, // 4: rfqrpc.AddAssetBuyOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse + 2, // 5: rfqrpc.AddAssetSellOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 3, // 6: rfqrpc.AddAssetSellOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint + 15, // 7: rfqrpc.AddAssetSellOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedSellQuote + 16, // 8: rfqrpc.AddAssetSellOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse + 17, // 9: rfqrpc.AddAssetSellOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse + 2, // 10: rfqrpc.AddAssetSellOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 2, // 11: rfqrpc.AddAssetBuyOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 3, // 12: rfqrpc.PeerAcceptedBuyQuote.ask_asset_rate:type_name -> rfqrpc.FixedPoint + 13, // 13: rfqrpc.PeerAcceptedBuyQuote.asset_spec:type_name -> rfqrpc.AssetSpec + 3, // 14: rfqrpc.PeerAcceptedSellQuote.bid_asset_rate:type_name -> rfqrpc.FixedPoint + 13, // 15: rfqrpc.PeerAcceptedSellQuote.asset_spec:type_name -> rfqrpc.AssetSpec + 0, // 16: rfqrpc.InvalidQuoteResponse.status:type_name -> rfqrpc.QuoteRespStatus + 14, // 17: rfqrpc.QueryPeerAcceptedQuotesResponse.buy_quotes:type_name -> rfqrpc.PeerAcceptedBuyQuote + 15, // 18: rfqrpc.QueryPeerAcceptedQuotesResponse.sell_quotes:type_name -> rfqrpc.PeerAcceptedSellQuote + 14, // 19: rfqrpc.PeerAcceptedBuyQuoteEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote + 15, // 20: rfqrpc.PeerAcceptedSellQuoteEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuote + 20, // 21: rfqrpc.RfqEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuoteEvent + 21, // 22: rfqrpc.RfqEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuoteEvent + 22, // 23: rfqrpc.RfqEvent.accept_htlc:type_name -> rfqrpc.AcceptHtlcEvent + 2, // 24: rfqrpc.ForwardingHistoryRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 26, // 25: rfqrpc.ForwardingHistoryResponse.forwards:type_name -> rfqrpc.ForwardingEvent + 1, // 26: rfqrpc.ForwardingEvent.policy_type:type_name -> rfqrpc.RfqPolicyType + 13, // 27: rfqrpc.ForwardingEvent.asset_spec:type_name -> rfqrpc.AssetSpec + 3, // 28: rfqrpc.ForwardingEvent.rate:type_name -> rfqrpc.FixedPoint + 4, // 29: rfqrpc.Rfq.AddAssetBuyOrder:input_type -> rfqrpc.AddAssetBuyOrderRequest + 6, // 30: rfqrpc.Rfq.AddAssetSellOrder:input_type -> rfqrpc.AddAssetSellOrderRequest + 8, // 31: rfqrpc.Rfq.AddAssetSellOffer:input_type -> rfqrpc.AddAssetSellOfferRequest + 10, // 32: rfqrpc.Rfq.AddAssetBuyOffer:input_type -> rfqrpc.AddAssetBuyOfferRequest + 12, // 33: rfqrpc.Rfq.QueryPeerAcceptedQuotes:input_type -> rfqrpc.QueryPeerAcceptedQuotesRequest + 19, // 34: rfqrpc.Rfq.SubscribeRfqEventNtfns:input_type -> rfqrpc.SubscribeRfqEventNtfnsRequest + 24, // 35: rfqrpc.Rfq.ForwardingHistory:input_type -> rfqrpc.ForwardingHistoryRequest + 5, // 36: rfqrpc.Rfq.AddAssetBuyOrder:output_type -> rfqrpc.AddAssetBuyOrderResponse + 7, // 37: rfqrpc.Rfq.AddAssetSellOrder:output_type -> rfqrpc.AddAssetSellOrderResponse + 9, // 38: rfqrpc.Rfq.AddAssetSellOffer:output_type -> rfqrpc.AddAssetSellOfferResponse + 11, // 39: rfqrpc.Rfq.AddAssetBuyOffer:output_type -> rfqrpc.AddAssetBuyOfferResponse + 18, // 40: rfqrpc.Rfq.QueryPeerAcceptedQuotes:output_type -> rfqrpc.QueryPeerAcceptedQuotesResponse + 23, // 41: rfqrpc.Rfq.SubscribeRfqEventNtfns:output_type -> rfqrpc.RfqEvent + 25, // 42: rfqrpc.Rfq.ForwardingHistory:output_type -> rfqrpc.ForwardingHistoryResponse + 36, // [36:43] is the sub-list for method output_type + 29, // [29:36] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_rfqrpc_rfq_proto_init() } diff --git a/taprpc/rfqrpc/rfq.proto b/taprpc/rfqrpc/rfq.proto index 4d612dd10..f64ec2278 100644 --- a/taprpc/rfqrpc/rfq.proto +++ b/taprpc/rfqrpc/rfq.proto @@ -160,6 +160,16 @@ message AddAssetBuyOrderRequest { // This field is optional and can be left empty if no metadata is available. // The maximum length of this field is 32'768 bytes. string price_oracle_metadata = 7; + + // The optional minimum amount of the asset that the provider must be + // willing to offer. If set, must be less than or equal to asset_max_amt. + // A value of 0 means unset. + uint64 asset_min_amt = 8; + + // An optional rate limit constraint expressed as a fixed-point number. + // For buy orders this is the minimum acceptable rate (asset units per + // BTC). If unset, no rate floor is enforced. + FixedPoint asset_rate_limit = 9; } message AddAssetBuyOrderResponse { @@ -211,6 +221,16 @@ message AddAssetSellOrderRequest { // This field is optional and can be left empty if no metadata is available. // The maximum length of this field is 32'768 bytes. string price_oracle_metadata = 7; + + // The optional minimum msat amount that the responding peer must agree + // to pay. If set, must be less than or equal to payment_max_amt. + // A value of 0 means unset (units: millisats). + uint64 payment_min_amt = 8; + + // An optional rate limit constraint expressed as a fixed-point number. + // For sell orders this is the maximum acceptable rate (asset units per + // BTC). If unset, no rate ceiling is enforced. + FixedPoint asset_rate_limit = 9; } message AddAssetSellOrderResponse { diff --git a/taprpc/rfqrpc/rfq.swagger.json b/taprpc/rfqrpc/rfq.swagger.json index c830910d0..9d79afb39 100644 --- a/taprpc/rfqrpc/rfq.swagger.json +++ b/taprpc/rfqrpc/rfq.swagger.json @@ -584,6 +584,15 @@ "price_oracle_metadata": { "type": "string", "description": "An optional text field that can be used to provide additional metadata\nabout the buy order to the price oracle. This can include information\nabout the wallet end user that initiated the transaction, or any\nauthentication information that the price oracle can use to give out a\nmore accurate (or discount) asset rate. Though not verified or enforced\nby tapd, the suggested format for this field is a JSON string.\nThis field is optional and can be left empty if no metadata is available.\nThe maximum length of this field is 32'768 bytes." + }, + "asset_min_amt": { + "type": "string", + "format": "uint64", + "description": "The optional minimum amount of the asset that the provider must be\nwilling to offer. If set, must be less than or equal to asset_max_amt.\nA value of 0 means unset." + }, + "asset_rate_limit": { + "$ref": "#/definitions/rfqrpcFixedPoint", + "description": "An optional rate limit constraint expressed as a fixed-point number.\nFor buy orders this is the minimum acceptable rate (asset units per\nBTC). If unset, no rate floor is enforced." } } }, @@ -669,6 +678,15 @@ "price_oracle_metadata": { "type": "string", "description": "An optional text field that can be used to provide additional metadata\nabout the sell order to the price oracle. This can include information\nabout the wallet end user that initiated the transaction, or any\nauthentication information that the price oracle can use to give out a\nmore accurate (or discount) asset rate. Though not verified or enforced\nby tapd, the suggested format for this field is a JSON string.\nThis field is optional and can be left empty if no metadata is available.\nThe maximum length of this field is 32'768 bytes." + }, + "payment_min_amt": { + "type": "string", + "format": "uint64", + "description": "The optional minimum msat amount that the responding peer must agree\nto pay. If set, must be less than or equal to payment_max_amt.\nA value of 0 means unset (units: millisats)." + }, + "asset_rate_limit": { + "$ref": "#/definitions/rfqrpcFixedPoint", + "description": "An optional rate limit constraint expressed as a fixed-point number.\nFor sell orders this is the maximum acceptable rate (asset units per\nBTC). If unset, no rate ceiling is enforced." } } }, From ef5578bf9697a66085ed29c10bbc0410df7b3b56 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 16:59:34 +0800 Subject: [PATCH 04/21] rfq: enforce rate bound and min fill in VerifyAcceptQuote Add checkRateBound and checkMinFill enforcement to the internal portfolio pilot's VerifyAcceptQuote. Rate bound checks that the accepted rate satisfies the requester's limit (floor for buy, ceiling for sell). Min fill checks that the minimum amount is transportable at the accepted rate. Also adds MinFillNotMetRejectCode, PriceBoundMissRejectCode, and corresponding QuoteRespStatus values. --- rfq/portfolio_pilot.go | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/rfq/portfolio_pilot.go b/rfq/portfolio_pilot.go index 8a690593a..5ed6a5391 100644 --- a/rfq/portfolio_pilot.go +++ b/rfq/portfolio_pilot.go @@ -363,6 +363,18 @@ func (p *InternalPortfolioPilot) VerifyAcceptQuote(ctx context.Context, return InvalidAssetRatesQuoteRespStatus, nil } + // Enforce the requester's rate bound constraint if set. + status := checkRateBound(req, counterRate.Rate) + if status != ValidAcceptQuoteRespStatus { + return status, nil + } + + // Enforce the requester's min fill constraint if set. + status = checkMinFill(req, counterRate.Rate) + if status != ValidAcceptQuoteRespStatus { + return status, nil + } + return ValidAcceptQuoteRespStatus, nil } @@ -443,3 +455,84 @@ func (p *InternalPortfolioPilot) QueryAssetRates(ctx context.Context, func (p *InternalPortfolioPilot) Close() error { return nil } + +// checkRateBound verifies that the accepted rate satisfies the +// requester's rate limit constraint. For a buy request, the accepted +// rate must be >= the limit (buyer's floor). For a sell request, the +// accepted rate must be <= the limit (seller's ceiling). +func checkRateBound(req rfqmsg.Request, + acceptedRate rfqmath.BigIntFixedPoint) QuoteRespStatus { + + switch r := req.(type) { + case *rfqmsg.BuyRequest: + miss := fn.MapOptionZ( + r.AssetRateLimit, + func(limit rfqmath.BigIntFixedPoint) bool { + return acceptedRate.Cmp(limit) < 0 + }, + ) + if miss { + return RateBoundMissQuoteRespStatus + } + + case *rfqmsg.SellRequest: + miss := fn.MapOptionZ( + r.AssetRateLimit, + func(limit rfqmath.BigIntFixedPoint) bool { + return acceptedRate.Cmp(limit) > 0 + }, + ) + if miss { + return RateBoundMissQuoteRespStatus + } + } + + return ValidAcceptQuoteRespStatus +} + +// checkMinFill verifies that the requester's minimum fill amount is +// transportable at the accepted rate. For a buy request, the min asset +// amount must convert to a non-zero msat value. For a sell request, +// the min payment amount must convert to non-zero asset units. +func checkMinFill(req rfqmsg.Request, + acceptedRate rfqmath.BigIntFixedPoint) QuoteRespStatus { + + switch r := req.(type) { + case *rfqmsg.BuyRequest: + notMet := fn.MapOptionZ( + r.AssetMinAmt, + func(minAmt uint64) bool { + units := rfqmath.FixedPoint[rfqmath.BigInt]{ + Coefficient: rfqmath.NewBigIntFromUint64( + minAmt, + ), + Scale: 0, + } + msat := rfqmath.UnitsToMilliSatoshi( + units, acceptedRate, + ) + return msat == 0 + }, + ) + if notMet { + return MinFillNotMetQuoteRespStatus + } + + case *rfqmsg.SellRequest: + notMet := fn.MapOptionZ( + r.PaymentMinAmt, + func(minAmt lnwire.MilliSatoshi) bool { + units := rfqmath.MilliSatoshiToUnits( + minAmt, acceptedRate, + ) + zero := rfqmath.NewBigIntFromUint64(0) + return units.Coefficient.Equals(zero) + }, + ) + if notMet { + return MinFillNotMetQuoteRespStatus + } + } + + return ValidAcceptQuoteRespStatus +} From 46c63d28effe57d5f32c2ecdd60251993a7e3970 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 17:16:11 +0800 Subject: [PATCH 05/21] itest: log min fill and rate limit in portfolio pilot --- docs/examples/basic-portfolio-pilot/main.go | 14 ++++++++++++-- itest/portfolio_pilot_harness.go | 14 ++++++++++++-- rfq/portfolio_pilot_rpc.go | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/examples/basic-portfolio-pilot/main.go b/docs/examples/basic-portfolio-pilot/main.go index a35de9f5a..898f1f715 100644 --- a/docs/examples/basic-portfolio-pilot/main.go +++ b/docs/examples/basic-portfolio-pilot/main.go @@ -104,9 +104,19 @@ func (p *RpcPortfolioPilotServer) ResolveRequest(_ context.Context, var hint *portfoliopilotrpc.AssetRate switch r := req.GetRequest().(type) { case *portfoliopilotrpc.ResolveRequestRequest_BuyRequest: - hint = r.BuyRequest.GetAssetRateHint() + br := r.BuyRequest + hint = br.GetAssetRateHint() + log.Printf("ResolveRequest buy: max=%d min=%d "+ + "rate_limit=%v", br.GetAssetMaxAmount(), + br.GetAssetMinAmount(), + br.GetAssetRateLimit()) case *portfoliopilotrpc.ResolveRequestRequest_SellRequest: - hint = r.SellRequest.GetAssetRateHint() + sr := r.SellRequest + hint = sr.GetAssetRateHint() + log.Printf("ResolveRequest sell: max=%d min=%d "+ + "rate_limit=%v", sr.GetPaymentMaxAmount(), + sr.GetPaymentMinAmount(), + sr.GetAssetRateLimit()) default: return nil, fmt.Errorf("unknown request type: %T", r) } diff --git a/itest/portfolio_pilot_harness.go b/itest/portfolio_pilot_harness.go index ec23785c9..2f28d9d7e 100644 --- a/itest/portfolio_pilot_harness.go +++ b/itest/portfolio_pilot_harness.go @@ -149,9 +149,19 @@ func (p *portfolioPilotHarness) ResolveRequest(_ context.Context, var hint *pilotrpc.AssetRate switch r := req.GetRequest().(type) { case *pilotrpc.ResolveRequestRequest_BuyRequest: - hint = r.BuyRequest.GetAssetRateHint() + br := r.BuyRequest + hint = br.GetAssetRateHint() + log.Infof("ResolveRequest buy: max=%d min=%d "+ + "rate_limit=%v", br.GetAssetMaxAmount(), + br.GetAssetMinAmount(), + br.GetAssetRateLimit()) case *pilotrpc.ResolveRequestRequest_SellRequest: - hint = r.SellRequest.GetAssetRateHint() + sr := r.SellRequest + hint = sr.GetAssetRateHint() + log.Infof("ResolveRequest sell: max=%d min=%d "+ + "rate_limit=%v", sr.GetPaymentMaxAmount(), + sr.GetPaymentMinAmount(), + sr.GetAssetRateLimit()) default: return nil, fmt.Errorf("unknown request type: %T", r) } diff --git a/rfq/portfolio_pilot_rpc.go b/rfq/portfolio_pilot_rpc.go index f82b9a4ef..476bd3373 100644 --- a/rfq/portfolio_pilot_rpc.go +++ b/rfq/portfolio_pilot_rpc.go @@ -11,8 +11,8 @@ import ( "github.com/lightninglabs/taproot-assets/rfqmath" "github.com/lightninglabs/taproot-assets/rfqmsg" "github.com/lightninglabs/taproot-assets/rpcutils" - "github.com/lightningnetwork/lnd/lnwire" pilotrpc "github.com/lightninglabs/taproot-assets/taprpc/portfoliopilotrpc" + "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/route" "google.golang.org/grpc" "google.golang.org/grpc/credentials" From 26df44bc1275c4905547f1fa91973aa1ea75d488 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 17:15:46 +0800 Subject: [PATCH 06/21] rfqmsg+rfq: add tests for limit-order constraints Add typed roundtrip tests for BuyRequest and SellRequest covering min fill, rate limit, and backward-compatible no-optional-field cases. Add validation tests for min > max and zero rate limit. Add rapid-based property tests covering wire roundtrip, min/max constraint validation, and rate bound enforcement semantics for both buy and sell requests. Extend TestVerifyAcceptQuote with rate bound enforcement cases (buy below/at/above limit, sell above/at limit) and min fill cases (zero msat, transportable). Add dedicated unit tests for checkRateBound and checkMinFill helpers. Update wire-level roundtrip test to cover AssetRateLimit TLV 29. --- rfq/portfolio_pilot_test.go | 445 +++++++++++++++++++++++++++++ rfqmsg/buy_request_test.go | 196 +++++++++++++ rfqmsg/request_property_test.go | 491 ++++++++++++++++++++++++++++++++ rfqmsg/request_test.go | 15 + rfqmsg/sell_request_test.go | 207 ++++++++++++++ 5 files changed, 1354 insertions(+) create mode 100644 rfqmsg/buy_request_test.go create mode 100644 rfqmsg/request_property_test.go create mode 100644 rfqmsg/sell_request_test.go diff --git a/rfq/portfolio_pilot_test.go b/rfq/portfolio_pilot_test.go index f1a11d5b1..b1ae8d449 100644 --- a/rfq/portfolio_pilot_test.go +++ b/rfq/portfolio_pilot_test.go @@ -836,6 +836,249 @@ func TestVerifyAcceptQuote(t *testing.T) { expectStatus: InvalidAssetRatesQuoteRespStatus, expectErr: false, }, + + // --- Rate bound enforcement cases --- + + { + name: "buy accept: rate below limit", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Buyer sets floor at 150 units/BTC, + // but peer offers only 100. + limit := rfqmath.NewBigIntFixedPoint( + 150, 0, + ) + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.None[uint64](), + fn.Some(limit), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRateMatch, + }, nil, + ) + }, + expectStatus: RateBoundMissQuoteRespStatus, + expectErr: false, + }, + { + name: "buy accept: rate at limit", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Buyer floor exactly equals accepted + // rate. + limit := rfqmath.NewBigIntFixedPoint( + 100, 0, + ) + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.None[uint64](), + fn.Some(limit), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRateMatch, + }, nil, + ) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, + { + name: "buy accept: rate above limit", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Buyer floor is 50, accepted rate is + // 100 — should pass. + limit := rfqmath.NewBigIntFixedPoint( + 50, 0, + ) + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.None[uint64](), + fn.Some(limit), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRateMatch, + }, nil, + ) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, + { + name: "sell accept: rate above limit", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Seller ceiling is 50, but accepted + // rate is 100 — should fail. + limit := rfqmath.NewBigIntFixedPoint( + 50, 0, + ) + sellReq, err := rfqmsg.NewSellRequest( + peerID, assetSpec, + lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.Some(limit), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.SellAccept{ + Peer: peerID, + Request: *sellReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + resp := OracleResponse{ + AssetRate: oracleRateMatch, + } + expectQuerySellPrice(p, &resp, nil) + }, + expectStatus: RateBoundMissQuoteRespStatus, + expectErr: false, + }, + { + name: "sell accept: rate at limit", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Seller ceiling equals accepted rate. + limit := rfqmath.NewBigIntFixedPoint( + 100, 0, + ) + sellReq, err := rfqmsg.NewSellRequest( + peerID, assetSpec, + lnwire.MilliSatoshi(1000), + fn.None[lnwire.MilliSatoshi](), + fn.Some(limit), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.SellAccept{ + Peer: peerID, + Request: *sellReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + resp := OracleResponse{ + AssetRate: oracleRateMatch, + } + expectQuerySellPrice(p, &resp, nil) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, + + // --- Min fill enforcement cases --- + + { + name: "buy accept: min fill zero msat", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Rate is very high (1e12 units/BTC), + // so 1 unit converts to ~0 msat. + hugeRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ), + validExpiryFuture, + ) + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.Some[uint64](1), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: hugeRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + oracleRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ), + validExpiryFuture, + ) + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRate, + }, nil, + ) + }, + expectStatus: MinFillNotMetQuoteRespStatus, + expectErr: false, + }, + { + name: "buy accept: min fill transportable", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Rate of 100 units/BTC, min of 50 + // units = 0.5 BTC = 500M msat. Easily + // transportable. + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.Some[uint64](50), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRateMatch, + }, nil, + ) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, } for _, tc := range tests { @@ -961,3 +1204,205 @@ func TestVerifyAcceptQuoteWithoutPriceOracle(t *testing.T) { require.NoError(t, err) require.Equal(t, PriceOracleQueryErrQuoteRespStatus, status) } + +// TestCheckRateBound exercises the checkRateBound helper directly. +func TestCheckRateBound(t *testing.T) { + t.Parallel() + + spec := asset.NewSpecifierFromId(asset.ID{0x01}) + rate100 := rfqmath.NewBigIntFixedPoint(100, 0) + rate50 := rfqmath.NewBigIntFixedPoint(50, 0) + rate150 := rfqmath.NewBigIntFixedPoint(150, 0) + + tests := []struct { + name string + req rfqmsg.Request + rate rfqmath.BigIntFixedPoint + expect QuoteRespStatus + }{ + { + name: "buy: no limit set", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: rate above limit", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.Some(rate50), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: rate at limit", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.Some(rate100), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: rate below limit", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.Some(rate150), + }, + rate: rate100, + expect: RateBoundMissQuoteRespStatus, + }, + { + name: "sell: no limit set", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: rate below limit", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.Some(rate150), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: rate at limit", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.Some(rate100), + }, + rate: rate100, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: rate above limit", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.Some(rate50), + }, + rate: rate100, + expect: RateBoundMissQuoteRespStatus, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + status := checkRateBound(tc.req, tc.rate) + require.Equal(t, tc.expect, status) + }) + } +} + +// TestCheckMinFill exercises the checkMinFill helper directly. +func TestCheckMinFill(t *testing.T) { + t.Parallel() + + spec := asset.NewSpecifierFromId(asset.ID{0x01}) + + // A rate of 100 units/BTC. 50 units = 0.5 BTC = + // 50_000_000_000 msat (easily non-zero). + normalRate := rfqmath.NewBigIntFixedPoint(100, 0) + + // A huge rate: 1e12 units/BTC. 1 unit = 1e-12 BTC = + // 0.1 msat, rounds to 0. + hugeRate := rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ) + + tests := []struct { + name string + req rfqmsg.Request + rate rfqmath.BigIntFixedPoint + expect QuoteRespStatus + }{ + { + name: "buy: no min set", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetMinAmt: fn.None[uint64](), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: min fill transportable", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetMinAmt: fn.Some[uint64](50), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: min fill rounds to zero", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetMinAmt: fn.Some[uint64](1), + }, + rate: hugeRate, + expect: MinFillNotMetQuoteRespStatus, + }, + { + name: "sell: no min set", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + PaymentMinAmt: fn.None[lnwire.MilliSatoshi](), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: min fill transportable", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: lnwire.MilliSatoshi( + 50_000_000_000, + ), + PaymentMinAmt: fn.Some( + lnwire.MilliSatoshi( + 10_000_000_000, + ), + ), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + // NOTE: A sell min fill that rounds to zero units is + // very hard to trigger with the default arithmetic + // scale (11), since MilliSatoshiToUnits preserves + // enough precision for any non-zero msat. The + // check acts as a safety net for degenerate inputs. + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + status := checkMinFill(tc.req, tc.rate) + require.Equal(t, tc.expect, status) + }) + } +} diff --git a/rfqmsg/buy_request_test.go b/rfqmsg/buy_request_test.go new file mode 100644 index 000000000..1ee7b2f57 --- /dev/null +++ b/rfqmsg/buy_request_test.go @@ -0,0 +1,196 @@ +package rfqmsg + +import ( + "bytes" + "testing" + "time" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/rfqmath" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/stretchr/testify/require" +) + +// buyRequestRoundtrip encodes a BuyRequest to wire and decodes it +// back, returning the decoded request. +func buyRequestRoundtrip(t *testing.T, + req *BuyRequest) *BuyRequest { + + t.Helper() + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode(bytes.NewReader(wireMsg.Data)) + require.NoError(t, err) + + decoded, err := NewBuyRequestFromWire(wireMsg, msgData) + require.NoError(t, err) + + return decoded +} + +// TestBuyRequestMinAmtRoundtrip verifies that AssetMinAmt survives +// a wire roundtrip. +func TestBuyRequestMinAmtRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x01} + spec := asset.NewSpecifierFromId(asset.ID{0xAA}) + + req, err := NewBuyRequest( + peer, spec, 100, fn.Some[uint64](50), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.AssetMinAmt.IsSome()) + decoded.AssetMinAmt.WhenSome(func(v uint64) { + require.Equal(t, uint64(50), v) + }) + require.Equal(t, uint64(100), decoded.AssetMaxAmt) +} + +// TestBuyRequestRateLimitRoundtrip verifies that AssetRateLimit +// survives a wire roundtrip. +func TestBuyRequestRateLimitRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x02} + spec := asset.NewSpecifierFromId(asset.ID{0xBB}) + limit := rfqmath.NewBigIntFixedPoint(42000, 2) + + req, err := NewBuyRequest( + peer, spec, 200, fn.None[uint64](), + fn.Some(limit), fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.AssetRateLimit.IsSome()) + decoded.AssetRateLimit.WhenSome( + func(v rfqmath.BigIntFixedPoint) { + require.Equal(t, 0, v.Cmp(limit)) + }, + ) +} + +// TestBuyRequestNoOptionalFieldsRoundtrip verifies backward +// compatibility when no optional fields are set. +func TestBuyRequestNoOptionalFieldsRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x03} + spec := asset.NewSpecifierFromId(asset.ID{0xCC}) + + req, err := NewBuyRequest( + peer, spec, 300, fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.AssetMinAmt.IsNone()) + require.True(t, decoded.AssetRateLimit.IsNone()) + require.Equal(t, uint64(300), decoded.AssetMaxAmt) +} + +// TestBuyRequestAllFieldsRoundtrip verifies that all optional fields +// survive a roundtrip together. +func TestBuyRequestAllFieldsRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x04} + spec := asset.NewSpecifierFromId(asset.ID{0xDD}) + limit := rfqmath.NewBigIntFixedPoint(99000, 3) + expiry := time.Now().Add(5 * time.Minute).UTC() + rateHint := NewAssetRate( + rfqmath.NewBigIntFixedPoint(100000, 0), expiry, + ) + + req, err := NewBuyRequest( + peer, spec, 500, fn.Some[uint64](10), + fn.Some(limit), fn.Some(rateHint), "metadata", + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.AssetMinAmt.IsSome()) + decoded.AssetMinAmt.WhenSome(func(v uint64) { + require.Equal(t, uint64(10), v) + }) + + require.True(t, decoded.AssetRateLimit.IsSome()) + decoded.AssetRateLimit.WhenSome( + func(v rfqmath.BigIntFixedPoint) { + require.Equal(t, 0, v.Cmp(limit)) + }, + ) + + require.True(t, decoded.AssetRateHint.IsSome()) + require.Equal(t, "metadata", decoded.PriceOracleMetadata) +} + +// TestBuyRequestValidateMinGtMax ensures Validate rejects min > max. +func TestBuyRequestValidateMinGtMax(t *testing.T) { + t.Parallel() + + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + AssetMaxAmt: 100, + AssetMinAmt: fn.Some[uint64](200), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "exceeds max amount") +} + +// TestBuyRequestValidateMinEqMax ensures min == max is valid. +func TestBuyRequestValidateMinEqMax(t *testing.T) { + t.Parallel() + + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + AssetMaxAmt: 100, + AssetMinAmt: fn.Some[uint64](100), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.NoError(t, err) +} + +// TestBuyRequestValidateZeroRateLimit ensures a zero rate limit +// coefficient is rejected. +func TestBuyRequestValidateZeroRateLimit(t *testing.T) { + t.Parallel() + + zeroLimit := rfqmath.NewBigIntFixedPoint(0, 0) + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + AssetMaxAmt: 100, + AssetMinAmt: fn.None[uint64](), + AssetRateLimit: fn.Some(zeroLimit), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "must be positive") +} diff --git a/rfqmsg/request_property_test.go b/rfqmsg/request_property_test.go new file mode 100644 index 000000000..492e5b8c6 --- /dev/null +++ b/rfqmsg/request_property_test.go @@ -0,0 +1,491 @@ +package rfqmsg + +import ( + "bytes" + "testing" + "time" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/rfqmath" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/stretchr/testify/require" + "pgregory.net/rapid" +) + +// optionalUint64Gen draws an fn.Option[uint64] that is None half the +// time and Some(v) otherwise, where v is drawn from [0, bound]. +func optionalUint64Gen(bound uint64) *rapid.Generator[fn.Option[uint64]] { + return rapid.Custom(func(t *rapid.T) fn.Option[uint64] { + if rapid.Bool().Draw(t, "present") { + v := rapid.Uint64Range(0, bound).Draw( + t, "value", + ) + return fn.Some(v) + } + return fn.None[uint64]() + }) +} + +// optionalMsatGen draws an fn.Option[lnwire.MilliSatoshi] that is +// None half the time and Some(v) otherwise, where v <= bound. +func optionalMsatGen( + bound uint64) *rapid.Generator[fn.Option[lnwire.MilliSatoshi]] { + + return rapid.Custom( + func(t *rapid.T) fn.Option[lnwire.MilliSatoshi] { + if rapid.Bool().Draw(t, "present") { + v := rapid.Uint64Range(0, bound).Draw( + t, "value", + ) + return fn.Some( + lnwire.MilliSatoshi(v), + ) + } + return fn.None[lnwire.MilliSatoshi]() + }, + ) +} + +// fixedPointGen draws a BigIntFixedPoint with coefficient in [1,1e12] +// and scale in [0,11]. +func fixedPointGen() *rapid.Generator[rfqmath.BigIntFixedPoint] { + return rapid.Custom( + func(t *rapid.T) rfqmath.BigIntFixedPoint { + coeff := rapid.Uint64Range(1, 1_000_000_000_000). + Draw(t, "coeff") + scale := rapid.Uint8Range(0, 11).Draw(t, "scale") + return rfqmath.NewBigIntFixedPoint( + coeff, scale, + ) + }, + ) +} + +// optionalFixedPointGen draws an optional BigIntFixedPoint, None half +// the time. +func optionalFixedPointGen() *rapid.Generator[fn.Option[rfqmath.BigIntFixedPoint]] { + return rapid.Custom( + func(t *rapid.T) fn.Option[rfqmath.BigIntFixedPoint] { + if rapid.Bool().Draw(t, "present") { + return fn.Some( + fixedPointGen().Draw(t, "fp"), + ) + } + return fn.None[rfqmath.BigIntFixedPoint]() + }, + ) +} + +// assetIDGen draws a random 32-byte asset.ID. +func assetIDGen() *rapid.Generator[asset.ID] { + return rapid.Custom(func(t *rapid.T) asset.ID { + var id asset.ID + for i := range id { + id[i] = rapid.Byte().Draw(t, "byte") + } + return id + }) +} + +// peerGen draws a random 33-byte route.Vertex. +func peerGen() *rapid.Generator[route.Vertex] { + return rapid.Custom(func(t *rapid.T) route.Vertex { + var v route.Vertex + for i := range v { + v[i] = rapid.Byte().Draw(t, "byte") + } + return v + }) +} + +// TestBuyRequestWireRoundtripProperty checks that any valid +// BuyRequest survives a wire encode/decode roundtrip. +func TestBuyRequestWireRoundtripProperty(t *testing.T) { + t.Parallel() + + rapid.Check(t, func(t *rapid.T) { + peer := peerGen().Draw(t, "peer") + id := assetIDGen().Draw(t, "id") + spec := asset.NewSpecifierFromId(id) + + maxAmt := rapid.Uint64Range(1, 1_000_000).Draw( + t, "maxAmt", + ) + minAmt := optionalUint64Gen(maxAmt).Draw( + t, "minAmt", + ) + rateLimit := optionalFixedPointGen().Draw( + t, "rateLimit", + ) + + req, err := NewBuyRequest( + peer, spec, maxAmt, minAmt, + rateLimit, fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode( + bytes.NewReader(wireMsg.Data), + ) + require.NoError(t, err) + + decoded, err := NewBuyRequestFromWire( + wireMsg, msgData, + ) + require.NoError(t, err) + + // Max amount must be preserved. + require.Equal(t, maxAmt, decoded.AssetMaxAmt) + + // Min amount must match. + requireOptEq(t, minAmt, decoded.AssetMinAmt) + + // Rate limit must match via Cmp. + requireOptFpEq( + t, rateLimit, decoded.AssetRateLimit, + ) + }) +} + +// TestSellRequestWireRoundtripProperty checks that any valid +// SellRequest survives a wire encode/decode roundtrip. +func TestSellRequestWireRoundtripProperty(t *testing.T) { + t.Parallel() + + rapid.Check(t, func(t *rapid.T) { + peer := peerGen().Draw(t, "peer") + id := assetIDGen().Draw(t, "id") + spec := asset.NewSpecifierFromId(id) + + maxAmt := rapid.Uint64Range(1, 1_000_000).Draw( + t, "maxAmt", + ) + minAmt := optionalMsatGen(maxAmt).Draw( + t, "minAmt", + ) + rateLimit := optionalFixedPointGen().Draw( + t, "rateLimit", + ) + + req, err := NewSellRequest( + peer, spec, + lnwire.MilliSatoshi(maxAmt), minAmt, + rateLimit, fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode( + bytes.NewReader(wireMsg.Data), + ) + require.NoError(t, err) + + decoded, err := NewSellRequestFromWire( + wireMsg, msgData, + ) + require.NoError(t, err) + + require.Equal( + t, lnwire.MilliSatoshi(maxAmt), + decoded.PaymentMaxAmt, + ) + + requireOptMsatEq( + t, minAmt, decoded.PaymentMinAmt, + ) + + requireOptFpEq( + t, rateLimit, decoded.AssetRateLimit, + ) + }) +} + +// TestMinMaxConstraintProperty verifies that Validate accepts +// min <= max and rejects min > max for both buy and sell requests. +func TestMinMaxConstraintProperty(t *testing.T) { + t.Parallel() + + t.Run("buy_valid", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + maxAmt := rapid.Uint64Range(1, 1_000_000). + Draw(t, "max") + minAmt := rapid.Uint64Range(0, maxAmt). + Draw(t, "min") + + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId( + asset.ID{1}, + ), + AssetMaxAmt: maxAmt, + AssetMinAmt: fn.Some(minAmt), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + require.NoError(t, req.Validate()) + }) + }) + + t.Run("buy_invalid", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + maxAmt := rapid.Uint64Range( + 0, 1_000_000-1, + ).Draw(t, "max") + minAmt := rapid.Uint64Range( + maxAmt+1, 1_000_000, + ).Draw(t, "min") + + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId( + asset.ID{1}, + ), + AssetMaxAmt: maxAmt, + AssetMinAmt: fn.Some(minAmt), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + require.Error(t, req.Validate()) + }) + }) + + t.Run("sell_valid", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + maxAmt := rapid.Uint64Range(1, 1_000_000). + Draw(t, "max") + minAmt := rapid.Uint64Range(0, maxAmt). + Draw(t, "min") + + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId( + asset.ID{1}, + ), + PaymentMaxAmt: lnwire.MilliSatoshi( + maxAmt, + ), + PaymentMinAmt: fn.Some( + lnwire.MilliSatoshi(minAmt), + ), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + require.NoError(t, req.Validate()) + }) + }) + + t.Run("sell_invalid", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + maxAmt := rapid.Uint64Range( + 0, 1_000_000-1, + ).Draw(t, "max") + minAmt := rapid.Uint64Range( + maxAmt+1, 1_000_000, + ).Draw(t, "min") + + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId( + asset.ID{1}, + ), + PaymentMaxAmt: lnwire.MilliSatoshi( + maxAmt, + ), + PaymentMinAmt: fn.Some( + lnwire.MilliSatoshi(minAmt), + ), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + require.Error(t, req.Validate()) + }) + }) +} + +// TestRateBoundEnforcementProperty verifies that rate bound +// comparison follows the correct direction semantics: +// - Buy: accepted >= limit passes, accepted < limit fails. +// - Sell: accepted <= limit passes, accepted > limit fails. +func TestRateBoundEnforcementProperty(t *testing.T) { + t.Parallel() + + t.Run("buy", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + accepted := fixedPointGen().Draw( + t, "accepted", + ) + limit := fixedPointGen().Draw(t, "limit") + cmp := accepted.Cmp(limit) + + // Buy: accepted must be >= limit. + if cmp >= 0 { + require.False( + t, accepted.Cmp(limit) < 0, + "buy pass: accepted=%v limit=%v", + accepted, limit, + ) + } else { + require.True( + t, accepted.Cmp(limit) < 0, + "buy fail: accepted=%v limit=%v", + accepted, limit, + ) + } + }) + }) + + t.Run("sell", func(t *testing.T) { + t.Parallel() + rapid.Check(t, func(t *rapid.T) { + accepted := fixedPointGen().Draw( + t, "accepted", + ) + limit := fixedPointGen().Draw(t, "limit") + cmp := accepted.Cmp(limit) + + // Sell: accepted must be <= limit. + if cmp <= 0 { + require.False( + t, accepted.Cmp(limit) > 0, + "sell pass: accepted=%v limit=%v", + accepted, limit, + ) + } else { + require.True( + t, accepted.Cmp(limit) > 0, + "sell fail: accepted=%v limit=%v", + accepted, limit, + ) + } + }) + }) +} + +// TestBuyRequestRoundtripWithHintProperty verifies that a BuyRequest +// with all fields (including AssetRateHint) survives a roundtrip. +func TestBuyRequestRoundtripWithHintProperty(t *testing.T) { + t.Parallel() + + rapid.Check(t, func(t *rapid.T) { + peer := peerGen().Draw(t, "peer") + id := assetIDGen().Draw(t, "id") + spec := asset.NewSpecifierFromId(id) + + maxAmt := rapid.Uint64Range(1, 1_000_000).Draw( + t, "maxAmt", + ) + minAmt := optionalUint64Gen(maxAmt).Draw( + t, "minAmt", + ) + rateLimit := optionalFixedPointGen().Draw( + t, "rateLimit", + ) + + // Always include a rate hint so we test the full + // field set. + expiry := time.Now().Add(5 * time.Minute).UTC() + fp := fixedPointGen().Draw(t, "hintRate") + hint := fn.Some(NewAssetRate(fp, expiry)) + + req, err := NewBuyRequest( + peer, spec, maxAmt, minAmt, + rateLimit, hint, "", + ) + require.NoError(t, err) + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode( + bytes.NewReader(wireMsg.Data), + ) + require.NoError(t, err) + + decoded, err := NewBuyRequestFromWire( + wireMsg, msgData, + ) + require.NoError(t, err) + + require.Equal(t, maxAmt, decoded.AssetMaxAmt) + requireOptEq(t, minAmt, decoded.AssetMinAmt) + requireOptFpEq( + t, rateLimit, decoded.AssetRateLimit, + ) + require.True(t, decoded.AssetRateHint.IsSome()) + }) +} + +// --- helpers --- + +// requireOptEq asserts two fn.Option[uint64] values are equal. +func requireOptEq(t require.TestingT, + want, got fn.Option[uint64]) { + + t.(*rapid.T).Helper() + + if want.IsNone() { + require.True(t, got.IsNone()) + return + } + + require.True(t, got.IsSome()) + + wantVal := want.UnwrapOr(0) + gotVal := got.UnwrapOr(0) + require.Equal(t, wantVal, gotVal) +} + +// requireOptMsatEq asserts two fn.Option[lnwire.MilliSatoshi] +// values are equal. +func requireOptMsatEq(t require.TestingT, + want, got fn.Option[lnwire.MilliSatoshi]) { + + t.(*rapid.T).Helper() + + if want.IsNone() { + require.True(t, got.IsNone()) + return + } + + require.True(t, got.IsSome()) + + wantVal := want.UnwrapOr(0) + gotVal := got.UnwrapOr(0) + require.Equal(t, wantVal, gotVal) +} + +// requireOptFpEq asserts two optional BigIntFixedPoint values are +// equal via Cmp. +func requireOptFpEq(t require.TestingT, + want, got fn.Option[rfqmath.BigIntFixedPoint]) { + + if want.IsNone() { + require.True(t, got.IsNone()) + return + } + + require.True(t, got.IsSome()) + + wantVal := want.UnwrapOr( + rfqmath.NewBigIntFixedPoint(0, 0), + ) + gotVal := got.UnwrapOr( + rfqmath.NewBigIntFixedPoint(0, 0), + ) + require.Equal(t, 0, gotVal.Cmp(wantVal)) +} diff --git a/rfqmsg/request_test.go b/rfqmsg/request_test.go index 017e3e583..39c98fa32 100644 --- a/rfqmsg/request_test.go +++ b/rfqmsg/request_test.go @@ -34,6 +34,8 @@ type testCaseEncodeDecode struct { minInAsset *uint64 minOutAsset *uint64 + assetRateLimit *uint64 + oracleMetadata string } @@ -109,6 +111,16 @@ func (tc testCaseEncodeDecode) Request() requestWireMsgData { ) } + var assetRateLimit requestAssetRateLimit + if tc.assetRateLimit != nil { + rate := NewTlvFixedPointFromUint64( + *tc.assetRateLimit, 3, + ) + assetRateLimit = tlv.SomeRecordT[tlv.TlvType29]( + tlv.NewRecordT[tlv.TlvType29](rate), + ) + } + var oracleMetadata requestOracleMetadata if tc.oracleMetadata != "" { oracleMetadata = tlv.SomeRecordT[tlv.TlvType27]( @@ -131,6 +143,7 @@ func (tc testCaseEncodeDecode) Request() requestWireMsgData { OutAssetRateHint: outAssetRateHint, MinInAsset: minInAsset, MinOutAsset: minOutAsset, + AssetRateLimit: assetRateLimit, PriceOracleMetadata: oracleMetadata, } } @@ -162,6 +175,7 @@ func TestRequestMsgDataEncodeDecode(t *testing.T) { minInAsset := uint64(1) minOutAsset := uint64(10) + assetRateLimit := uint64(50000) testCases := []testCaseEncodeDecode{ { @@ -180,6 +194,7 @@ func TestRequestMsgDataEncodeDecode(t *testing.T) { outAssetRateHint: &outAssetRateHint, minInAsset: &minInAsset, minOutAsset: &minOutAsset, + assetRateLimit: &assetRateLimit, oracleMetadata: "this could be a JSON string", }, { diff --git a/rfqmsg/sell_request_test.go b/rfqmsg/sell_request_test.go new file mode 100644 index 000000000..099170d62 --- /dev/null +++ b/rfqmsg/sell_request_test.go @@ -0,0 +1,207 @@ +package rfqmsg + +import ( + "bytes" + "testing" + "time" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/fn" + "github.com/lightninglabs/taproot-assets/rfqmath" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing/route" + "github.com/stretchr/testify/require" +) + +// sellRequestRoundtrip encodes a SellRequest to wire and decodes it +// back, returning the decoded request. +func sellRequestRoundtrip(t *testing.T, + req *SellRequest) *SellRequest { + + t.Helper() + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode(bytes.NewReader(wireMsg.Data)) + require.NoError(t, err) + + decoded, err := NewSellRequestFromWire(wireMsg, msgData) + require.NoError(t, err) + + return decoded +} + +// TestSellRequestMinAmtRoundtrip verifies that PaymentMinAmt survives +// a wire roundtrip. +func TestSellRequestMinAmtRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x01} + spec := asset.NewSpecifierFromId(asset.ID{0xAA}) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(5000), + fn.Some(lnwire.MilliSatoshi(1000)), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.PaymentMinAmt.IsSome()) + decoded.PaymentMinAmt.WhenSome(func(v lnwire.MilliSatoshi) { + require.Equal(t, lnwire.MilliSatoshi(1000), v) + }) + require.Equal(t, lnwire.MilliSatoshi(5000), decoded.PaymentMaxAmt) +} + +// TestSellRequestRateLimitRoundtrip verifies that AssetRateLimit +// survives a wire roundtrip. +func TestSellRequestRateLimitRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x02} + spec := asset.NewSpecifierFromId(asset.ID{0xBB}) + limit := rfqmath.NewBigIntFixedPoint(75000, 2) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(10000), + fn.None[lnwire.MilliSatoshi](), + fn.Some(limit), fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.AssetRateLimit.IsSome()) + decoded.AssetRateLimit.WhenSome( + func(v rfqmath.BigIntFixedPoint) { + require.Equal(t, 0, v.Cmp(limit)) + }, + ) +} + +// TestSellRequestNoOptionalFieldsRoundtrip verifies backward +// compatibility when no optional fields are set. +func TestSellRequestNoOptionalFieldsRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x03} + spec := asset.NewSpecifierFromId(asset.ID{0xCC}) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(3000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.PaymentMinAmt.IsNone()) + require.True(t, decoded.AssetRateLimit.IsNone()) + require.Equal( + t, lnwire.MilliSatoshi(3000), decoded.PaymentMaxAmt, + ) +} + +// TestSellRequestAllFieldsRoundtrip verifies that all optional fields +// survive a roundtrip together. +func TestSellRequestAllFieldsRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x04} + spec := asset.NewSpecifierFromId(asset.ID{0xDD}) + limit := rfqmath.NewBigIntFixedPoint(88000, 3) + expiry := time.Now().Add(5 * time.Minute).UTC() + rateHint := NewAssetRate( + rfqmath.NewBigIntFixedPoint(100000, 0), expiry, + ) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(9000), + fn.Some(lnwire.MilliSatoshi(2000)), + fn.Some(limit), fn.Some(rateHint), "sell-meta", + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.PaymentMinAmt.IsSome()) + decoded.PaymentMinAmt.WhenSome(func(v lnwire.MilliSatoshi) { + require.Equal(t, lnwire.MilliSatoshi(2000), v) + }) + + require.True(t, decoded.AssetRateLimit.IsSome()) + decoded.AssetRateLimit.WhenSome( + func(v rfqmath.BigIntFixedPoint) { + require.Equal(t, 0, v.Cmp(limit)) + }, + ) + + require.True(t, decoded.AssetRateHint.IsSome()) + require.Equal(t, "sell-meta", decoded.PriceOracleMetadata) +} + +// TestSellRequestValidateMinGtMax ensures Validate rejects min > max. +func TestSellRequestValidateMinGtMax(t *testing.T) { + t.Parallel() + + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + PaymentMaxAmt: lnwire.MilliSatoshi(100), + PaymentMinAmt: fn.Some( + lnwire.MilliSatoshi(200), + ), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "exceeds max amount") +} + +// TestSellRequestValidateMinEqMax ensures min == max is valid. +func TestSellRequestValidateMinEqMax(t *testing.T) { + t.Parallel() + + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + PaymentMaxAmt: lnwire.MilliSatoshi(500), + PaymentMinAmt: fn.Some( + lnwire.MilliSatoshi(500), + ), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.NoError(t, err) +} + +// TestSellRequestValidateZeroRateLimit ensures a zero rate limit +// coefficient is rejected. +func TestSellRequestValidateZeroRateLimit(t *testing.T) { + t.Parallel() + + zeroLimit := rfqmath.NewBigIntFixedPoint(0, 0) + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + PaymentMaxAmt: lnwire.MilliSatoshi(1000), + PaymentMinAmt: fn.None[lnwire.MilliSatoshi](), + AssetRateLimit: fn.Some(zeroLimit), + AssetRateHint: fn.None[AssetRate](), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "must be positive") +} From 8df1ad39b2c89675cdddd8846fa495e75cdbdca5 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 20:47:40 +0800 Subject: [PATCH 07/21] itest+rfqmsg: add integration tests for limit-order constraints Add Validate() to NewBuyRequest and NewSellRequest constructors so that invalid requests (e.g. min > max) are caught locally before being sent over the wire. Add testRfqLimitConstraints: a pure RFQ integration test with five sub-tests exercising buy/sell orders with satisfied and violated rate limits, plus client-side min > max rejection. Add testCustomChannelsLimitConstraints: a custom channels smoke test that negotiates a sell quote with asset_rate_limit and payment_min_amt constraints over a real asset channel, then pays an invoice using the pre-negotiated quote. --- itest/custom_channels/custom_channels_test.go | 4 + .../custom_channels/limit_constraints_test.go | 253 ++++++++++++++++++ itest/rfq_test.go | 241 +++++++++++++++++ itest/test_list_on_test.go | 4 + rfqmsg/buy_request.go | 11 +- rfqmsg/sell_request.go | 11 +- 6 files changed, 520 insertions(+), 4 deletions(-) create mode 100644 itest/custom_channels/limit_constraints_test.go diff --git a/itest/custom_channels/custom_channels_test.go b/itest/custom_channels/custom_channels_test.go index 00614d383..292ab4c78 100644 --- a/itest/custom_channels/custom_channels_test.go +++ b/itest/custom_channels/custom_channels_test.go @@ -99,6 +99,10 @@ var testCases = []*ccTestCase{ name: "invoice quote expiry mismatch", test: testCustomChannelsInvoiceQuoteExpiryMismatch, }, + { + name: "limit constraints", + test: testCustomChannelsLimitConstraints, + }, { name: "fee", test: testCustomChannelsFee, diff --git a/itest/custom_channels/limit_constraints_test.go b/itest/custom_channels/limit_constraints_test.go new file mode 100644 index 000000000..832b1fc39 --- /dev/null +++ b/itest/custom_channels/limit_constraints_test.go @@ -0,0 +1,253 @@ +//go:build itest + +package custom_channels + +import ( + "context" + "fmt" + "math" + "math/big" + "slices" + "time" + + "github.com/lightninglabs/taproot-assets/asset" + "github.com/lightninglabs/taproot-assets/itest" + "github.com/lightninglabs/taproot-assets/proof" + "github.com/lightninglabs/taproot-assets/rfqmath" + "github.com/lightninglabs/taproot-assets/rfqmsg" + "github.com/lightninglabs/taproot-assets/taprpc" + "github.com/lightninglabs/taproot-assets/taprpc/mintrpc" + "github.com/lightninglabs/taproot-assets/taprpc/rfqrpc" + tchrpc "github.com/lightninglabs/taproot-assets/taprpc/tapchannelrpc" + "github.com/lightningnetwork/lnd/lnrpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/port" + "github.com/stretchr/testify/require" +) + +// testCustomChannelsLimitConstraints verifies that RFQ limit-order +// constraints (asset_rate_limit, payment_min_amt) work correctly in +// the context of real asset channels. It negotiates a sell quote with +// satisfied constraints, then sends a payment using that quote. +// +//nolint:lll +func testCustomChannelsLimitConstraints(_ context.Context, + net *itest.IntegratedNetworkHarness, t *ccHarnessTest) { + + usdMetaData := &taprpc.AssetMeta{ + Data: []byte(`{ +"description":"USD stablecoin for limit constraint test" +}`), + Type: taprpc.AssetMetaType_META_TYPE_JSON, + } + + const decimalDisplay = 6 + tcAsset := &mintrpc.MintAsset{ + AssetType: taprpc.AssetType_NORMAL, + Name: "USD-limits", + AssetMeta: usdMetaData, + Amount: 1_000_000_000_000, + DecimalDisplay: decimalDisplay, + } + + oracleAddr := fmt.Sprintf( + "localhost:%d", port.NextAvailablePort(), + ) + oracle := itest.NewOracleHarness(oracleAddr) + oracle.Start(t.t) + t.t.Cleanup(oracle.Stop) + + lndArgs := slices.Clone(lndArgsTemplate) + tapdArgs := slices.Clone(tapdArgsTemplateNoOracle) + tapdArgs = append(tapdArgs, fmt.Sprintf( + "--experimental.rfq.priceoracleaddress=rfqrpc://%s", + oracleAddr, + )) + tapdArgs = append( + tapdArgs, + "--experimental.rfq.priceoracletlsinsecure", + ) + + charliePort := port.NextAvailablePort() + tapdArgs = append(tapdArgs, fmt.Sprintf( + "--proofcourieraddr=%s://%s", + proof.UniverseRpcCourierType, + fmt.Sprintf(node.ListenerFormat, charliePort), + )) + + // Topology: Charlie --[assets]--> Dave --[sats]--> Erin + charlieLndArgs := slices.Clone(lndArgs) + charlieLndArgs = append(charlieLndArgs, fmt.Sprintf( + "--rpclisten=127.0.0.1:%d", charliePort, + )) + charlie := net.NewNode("Charlie", charlieLndArgs, tapdArgs) + dave := net.NewNode("Dave", lndArgs, tapdArgs) + erin := net.NewNode("Erin", lndArgs, tapdArgs) + + nodes := []*itest.IntegratedNode{charlie, dave, erin} + connectAllNodes(t.t, net, nodes) + fundAllNodes(t.t, net, nodes) + + // Open a normal BTC channel between Dave and Erin. + const btcChannelFundingAmount = 10_000_000 + chanPointDE := openChannelAndAssert( + t, net, dave, erin, lntest.OpenChannelParams{ + Amt: btcChannelFundingAmount, + SatPerVByte: 5, + }, + ) + defer closeChannelAndAssert(t, net, dave, chanPointDE, false) + + assertChannelKnown(t.t, charlie, chanPointDE) + + // Mint on Charlie. + mintedAssets := itest.MintAssetsConfirmBatch( + t.t, net.Miner.Client, asTapd(charlie), + []*mintrpc.MintAssetRequest{ + {Asset: tcAsset}, + }, + ) + usdAsset := mintedAssets[0] + assetID := usdAsset.AssetGenesis.AssetId + + var id asset.ID + copy(id[:], assetID) + + // Oracle price: ~66,548.40 USD/BTC with decimal display 6. + salePrice := rfqmath.NewBigIntFixedPoint(65_217_43, 2) + purchasePrice := rfqmath.NewBigIntFixedPoint(67_879_37, 2) + factor := rfqmath.NewBigInt( + big.NewInt(int64(math.Pow10(decimalDisplay))), + ) + salePrice.Coefficient = salePrice.Coefficient.Mul(factor) + purchasePrice.Coefficient = purchasePrice.Coefficient.Mul( + factor, + ) + oracle.SetPrice( + asset.NewSpecifierFromId(id), purchasePrice, salePrice, + ) + + t.Logf("Syncing universes...") + syncUniverses(t.t, charlie, dave, erin) + + // Send assets to Dave so he has a balance. + const sendAmount = uint64(400_000_000) + charlieFundingAmount := usdAsset.Amount - sendAmount + + ctxb := context.Background() + daveAddr, err := dave.NewAddr(ctxb, &taprpc.NewAddrRequest{ + Amt: sendAmount, + AssetId: assetID, + ProofCourierAddr: fmt.Sprintf( + "%s://%s", proof.UniverseRpcCourierType, + charlie.RPCAddr(), + ), + }) + require.NoError(t.t, err) + + sendResp, err := charlie.SendAsset( + ctxb, &taprpc.SendAssetRequest{ + TapAddrs: []string{daveAddr.Encoded}, + }, + ) + require.NoError(t.t, err) + itest.ConfirmAndAssertOutboundTransfer( + t.t, net.Miner.Client, asTapd(charlie), sendResp, + assetID, + []uint64{usdAsset.Amount - sendAmount, sendAmount}, + 0, 1, + ) + itest.AssertNonInteractiveRecvComplete(t.t, asTapd(dave), 1) + + // Open asset channel Charlie → Dave. + t.Logf("Opening asset channel Charlie → Dave...") + net.EnsureConnected(t.t, charlie, dave) + fundResp, err := charlie.FundChannel( + ctxb, &tchrpc.FundChannelRequest{ + AssetAmount: charlieFundingAmount, + AssetId: assetID, + PeerPubkey: dave.PubKey[:], + FeeRateSatPerVbyte: 5, + PushSat: DefaultPushSat, + }, + ) + require.NoError(t.t, err) + t.Logf("Funded channel: %v", fundResp) + + mineBlocks(t, net, 6, 1) + + chanPointCD := &lnrpc.ChannelPoint{ + OutputIndex: uint32(fundResp.OutputIndex), + FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ + FundingTxidStr: fundResp.Txid, + }, + } + + // Make sure Erin knows about the Charlie → Dave channel. + assertChannelKnown(t.t, erin, chanPointCD) + + logBalance(t.t, nodes, assetID, "after channel open") + + // ----------------------------------------------------------------- + // Negotiate a sell order from Charlie with constraints. + // Rate limit is set well above the oracle rate (ceiling for + // sell), so the constraint is satisfied. + // ----------------------------------------------------------------- + t.Logf("Negotiating sell order with constraints...") + + inOneHour := time.Now().Add(time.Hour) + sellResp, err := asTapd(charlie).RfqClient.AddAssetSellOrder( + ctxb, &rfqrpc.AddAssetSellOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: assetID, + }, + }, + PaymentMaxAmt: 180_000_000, + PaymentMinAmt: 1000, + AssetRateLimit: &rfqrpc.FixedPoint{ + // Ceiling well above oracle rate. + Coefficient: "100000000000000", + Scale: 2, + }, + Expiry: uint64(inOneHour.Unix()), + PeerPubKey: dave.PubKey[:], + TimeoutSeconds: 10, + }, + ) + require.NoError(t.t, err, "sell order with constraints") + + accepted := sellResp.GetAcceptedQuote() + require.NotNil(t.t, accepted, "expected accepted sell quote") + t.Logf("Sell quote accepted: scid=%d", accepted.Scid) + + // ----------------------------------------------------------------- + // Pay an invoice using the pre-negotiated quote. + // ----------------------------------------------------------------- + t.Logf("Paying invoice with constrained quote...") + + const invoiceAssetAmount = 10_000_000 + invoiceResp := createAssetInvoice( + t.t, dave, erin, invoiceAssetAmount, assetID, + ) + + var quoteID rfqmsg.ID + copy(quoteID[:], accepted.Id) + + numUnits, _ := payInvoiceWithAssets( + t.t, charlie, dave, invoiceResp.PaymentRequest, + assetID, withRFQ(quoteID), + ) + require.Greater(t.t, numUnits, uint64(0)) + + logBalance(t.t, nodes, assetID, "after payment") + t.Logf("Payment completed: %d asset units sent", numUnits) + + // Close channels. + closeAssetChannelAndAssert( + t, net, charlie, dave, chanPointCD, + [][]byte{assetID}, nil, charlie, + noOpCoOpCloseBalanceCheck, + ) +} diff --git a/itest/rfq_test.go b/itest/rfq_test.go index e38f6bc45..5bcad5989 100644 --- a/itest/rfq_test.go +++ b/itest/rfq_test.go @@ -844,6 +844,247 @@ func testRfqPortfolioPilotRpc(t *harnessTest) { }, rfqTimeout, 50*time.Millisecond) } +// testRfqLimitConstraints tests that RFQ negotiation correctly enforces +// limit-order constraints (asset_rate_limit, asset_min_amt, +// payment_min_amt) at the RPC layer. It uses the oracle harness with +// SetPrice for deterministic rates and the standard 3-node topology. +func testRfqLimitConstraints(t *harnessTest) { + // Start a mock price oracle RPC server. + oracleAddr := fmt.Sprintf("127.0.0.1:%d", port.NextAvailablePort()) + oracle := NewOracleHarness(oracleAddr) + oracle.Start(t.t) + t.t.Cleanup(oracle.Stop) + + oracleURL := fmt.Sprintf("rfqrpc://%s", oracleAddr) + + // Initialize the standard 3-node test scenario. + ts := newRfqTestScenario(t, WithRfqOracleServer(oracleURL)) + + // Mint an asset with Bob's tapd node. + rpcAssets := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner().Client, ts.BobTapd, + []*mintrpc.MintAssetRequest{issuableAssets[0]}, + ) + mintedAssetId := rpcAssets[0].AssetGenesis.AssetId + + var assetID asset.ID + copy(assetID[:], mintedAssetId) + specifier := asset.NewSpecifierFromId(assetID) + + // Set oracle rate to 1000 asset units per BTC (coeff=1000000, + // scale=3). Use same price for both bid and ask (no spread). + oracleRate := rfqmath.NewBigIntFixedPoint(1000_000, 3) + oracle.SetPrice(specifier, oracleRate, oracleRate) + + ctx := context.Background() + + // Bob registers a sell offer so Carol can buy. + _, err := ts.BobTapd.AddAssetSellOffer( + ctx, &rfqrpc.AddAssetSellOfferRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + MaxUnits: 1000, + }, + ) + require.NoError(t.t, err) + + // Bob also registers a buy offer so Alice can sell. + _, err = ts.BobTapd.AddAssetBuyOffer( + ctx, &rfqrpc.AddAssetBuyOfferRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + MaxUnits: 1000, + }, + ) + require.NoError(t.t, err) + + expiry := uint64(time.Now().Add(24 * time.Hour).Unix()) + + // ----------------------------------------------------------------- + // Sub-test 1: Buy with satisfied rate limit. + // + // Oracle rate = 1000. Carol sets rate limit = 500 (floor for + // buy). Since 1000 >= 500, the constraint passes. + // ----------------------------------------------------------------- + t.Log("Sub-test 1: buy with satisfied rate limit") + + carolEvents, err := ts.CarolTapd.SubscribeRfqEventNtfns( + ctx, &rfqrpc.SubscribeRfqEventNtfnsRequest{}, + ) + require.NoError(t.t, err) + + buyReq := &rfqrpc.AddAssetBuyOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + AssetMaxAmt: 6, + AssetMinAmt: 1, + AssetRateLimit: &rfqrpc.FixedPoint{ + Coefficient: "500000", + Scale: 3, + }, + Expiry: expiry, + PeerPubKey: ts.BobLnd.PubKey[:], + TimeoutSeconds: uint32(rfqTimeout.Seconds()), + SkipAssetChannelCheck: true, + } + _, err = ts.CarolTapd.AddAssetBuyOrder(ctx, buyReq) + require.NoError(t.t, err, "buy with satisfied rate limit") + + BeforeTimeout(t.t, func() { + event, err := carolEvents.Recv() + require.NoError(t.t, err) + + _, ok := event.Event.(*rfqrpc.RfqEvent_PeerAcceptedBuyQuote) + require.True(t.t, ok, "expected PeerAcceptedBuyQuote, "+ + "got: %v", event) + }, rfqTimeout) + + err = carolEvents.CloseSend() + require.NoError(t.t, err) + + // ----------------------------------------------------------------- + // Sub-test 2: Buy with violated rate limit. + // + // Oracle rate = 1000. Carol sets rate limit = 2000 (floor for + // buy). Since 1000 < 2000, the rate bound check fails. + // ----------------------------------------------------------------- + t.Log("Sub-test 2: buy with violated rate limit") + + buyReq2 := &rfqrpc.AddAssetBuyOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + AssetMaxAmt: 6, + AssetRateLimit: &rfqrpc.FixedPoint{ + Coefficient: "2000000", + Scale: 3, + }, + Expiry: expiry, + PeerPubKey: ts.BobLnd.PubKey[:], + TimeoutSeconds: uint32(rfqTimeout.Seconds()), + SkipAssetChannelCheck: true, + } + _, err = ts.CarolTapd.AddAssetBuyOrder(ctx, buyReq2) + require.ErrorContains( + t.t, err, "rejected quote", + "expected rate bound rejection for buy order", + ) + + // ----------------------------------------------------------------- + // Sub-test 3: Sell with satisfied constraints. + // + // Oracle rate = 1000. Alice sets rate limit = 2000 (ceiling + // for sell). Since 1000 <= 2000, the constraint passes. + // ----------------------------------------------------------------- + t.Log("Sub-test 3: sell with satisfied constraints") + + aliceEvents, err := ts.AliceTapd.SubscribeRfqEventNtfns( + ctx, &rfqrpc.SubscribeRfqEventNtfnsRequest{}, + ) + require.NoError(t.t, err) + + sellReq := &rfqrpc.AddAssetSellOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + PaymentMaxAmt: 42000, + PaymentMinAmt: 1000, + AssetRateLimit: &rfqrpc.FixedPoint{ + Coefficient: "2000000", + Scale: 3, + }, + Expiry: expiry, + PeerPubKey: ts.BobLnd.PubKey[:], + TimeoutSeconds: uint32(rfqTimeout.Seconds()), + SkipAssetChannelCheck: true, + } + _, err = ts.AliceTapd.AddAssetSellOrder(ctx, sellReq) + require.NoError(t.t, err, "sell with satisfied constraints") + + BeforeTimeout(t.t, func() { + event, err := aliceEvents.Recv() + require.NoError(t.t, err) + + _, ok := event.Event.(*rfqrpc.RfqEvent_PeerAcceptedSellQuote) + require.True(t.t, ok, "expected PeerAcceptedSellQuote, "+ + "got: %v", event) + }, rfqTimeout) + + err = aliceEvents.CloseSend() + require.NoError(t.t, err) + + // ----------------------------------------------------------------- + // Sub-test 4: Sell with violated rate limit. + // + // Oracle rate = 1000. Alice sets rate limit = 500 (ceiling for + // sell). Since 1000 > 500, the rate bound check fails. + // ----------------------------------------------------------------- + t.Log("Sub-test 4: sell with violated rate limit") + + sellReq2 := &rfqrpc.AddAssetSellOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + PaymentMaxAmt: 42000, + AssetRateLimit: &rfqrpc.FixedPoint{ + Coefficient: "500000", + Scale: 3, + }, + Expiry: expiry, + PeerPubKey: ts.BobLnd.PubKey[:], + TimeoutSeconds: uint32(rfqTimeout.Seconds()), + SkipAssetChannelCheck: true, + } + _, err = ts.AliceTapd.AddAssetSellOrder(ctx, sellReq2) + require.ErrorContains( + t.t, err, "rejected quote", + "expected rate bound rejection for sell order", + ) + + // ----------------------------------------------------------------- + // Sub-test 5: Client validation — min > max rejected locally. + // + // Carol sends a buy order with asset_min_amt = 10 and + // asset_max_amt = 5. The Validate() check in NewBuyRequest + // catches this before any wire negotiation. + // ----------------------------------------------------------------- + t.Log("Sub-test 5: client validation min > max") + + buyReq3 := &rfqrpc.AddAssetBuyOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: mintedAssetId, + }, + }, + AssetMaxAmt: 5, + AssetMinAmt: 10, + Expiry: expiry, + PeerPubKey: ts.BobLnd.PubKey[:], + TimeoutSeconds: uint32(rfqTimeout.Seconds()), + SkipAssetChannelCheck: true, + } + _, err = ts.CarolTapd.AddAssetBuyOrder(ctx, buyReq3) + require.ErrorContains( + t.t, err, "exceeds max amount", + "expected immediate min > max rejection", + ) +} + // rfqTestScenario is a struct which holds test scenario helper infra. type rfqTestScenario struct { testHarness *harnessTest diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index 177701e28..30156adb3 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -383,6 +383,10 @@ var allTestCases = []*testCase{ name: "rfq portfolio pilot rpc", test: testRfqPortfolioPilotRpc, }, + { + name: "rfq limit constraints", + test: testRfqLimitConstraints, + }, { name: "multi signature on all levels", test: testMultiSignature, diff --git a/rfqmsg/buy_request.go b/rfqmsg/buy_request.go index a1aa50304..165bd5fe9 100644 --- a/rfqmsg/buy_request.go +++ b/rfqmsg/buy_request.go @@ -99,7 +99,7 @@ func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, "length of %d bytes", MaxOracleMetadataLength) } - return &BuyRequest{ + req := &BuyRequest{ Peer: peer, Version: latestBuyRequestVersion, ID: id, @@ -109,7 +109,14 @@ func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, AssetRateLimit: assetRateLimit, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, - }, nil + } + + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("unable to validate buy "+ + "request: %w", err) + } + + return req, nil } // NewBuyRequestFromWire instantiates a new instance from a wire message. diff --git a/rfqmsg/sell_request.go b/rfqmsg/sell_request.go index 0771d4a8a..dcee8ff27 100644 --- a/rfqmsg/sell_request.go +++ b/rfqmsg/sell_request.go @@ -97,7 +97,7 @@ func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, "length of %d bytes", MaxOracleMetadataLength) } - return &SellRequest{ + req := &SellRequest{ Peer: peer, Version: latestSellRequestVersion, ID: id, @@ -107,7 +107,14 @@ func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, AssetRateLimit: assetRateLimit, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, - }, nil + } + + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("unable to validate sell "+ + "request: %w", err) + } + + return req, nil } // NewSellRequestFromWire instantiates a new instance from a wire message. From dd6e20a87738453978eb30a8731da4a3863fbd38 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Mon, 23 Mar 2026 21:10:57 +0800 Subject: [PATCH 08/21] rfqmsg+rfq: address review findings for limit-order constraints Harden rate limit validation to reject negative coefficients (not just zero), add default cases to checkRateBound/checkMinFill type switches, fix stale godoc on unmarshalOptionalFixedPoint, path- anchor the gitignore entry, rewrite TestRateBoundEnforcementProperty to exercise wire roundtrip code, and add TestNegativeRateLimitRejected. --- .gitignore | 2 + rfq/portfolio_pilot.go | 8 ++ rfqmsg/buy_request.go | 8 +- rfqmsg/request_property_test.go | 215 ++++++++++++++++++++++++++------ rfqmsg/sell_request.go | 8 +- rpcserver/rpcserver.go | 5 +- 6 files changed, 197 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index e6cc0d1b4..9ed1ba78b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ cmd/tapd/tapd /itest/custom_channels/*.log.* /docs/examples/basic-price-oracle/basic-price-oracle +/docs/examples/basic-portfolio-pilot/basic-portfolio-pilot +basic-portfolio-pilot # Test binary, built with `go test -c` *.test diff --git a/rfq/portfolio_pilot.go b/rfq/portfolio_pilot.go index 5ed6a5391..2385f8148 100644 --- a/rfq/portfolio_pilot.go +++ b/rfq/portfolio_pilot.go @@ -485,6 +485,10 @@ func checkRateBound(req rfqmsg.Request, if miss { return RateBoundMissQuoteRespStatus } + + default: + log.Warnf("checkRateBound: unhandled request type %T", + req) } return ValidAcceptQuoteRespStatus @@ -532,6 +536,10 @@ func checkMinFill(req rfqmsg.Request, if notMet { return MinFillNotMetQuoteRespStatus } + + default: + log.Warnf("checkMinFill: unhandled request type %T", + req) } return ValidAcceptQuoteRespStatus diff --git a/rfqmsg/buy_request.go b/rfqmsg/buy_request.go index 165bd5fe9..9fed655a1 100644 --- a/rfqmsg/buy_request.go +++ b/rfqmsg/buy_request.go @@ -248,14 +248,14 @@ func (q *BuyRequest) Validate() error { return err } - // Ensure rate limit is positive when set. + // Ensure rate limit is strictly positive when set. err = fn.MapOptionZ( q.AssetRateLimit, func(limit rfqmath.BigIntFixedPoint) error { zero := rfqmath.NewBigIntFromUint64(0) - if limit.Coefficient.Equals(zero) { - return fmt.Errorf("asset rate limit must " + - "be positive") + if !limit.Coefficient.Gt(zero) { + return fmt.Errorf("asset rate limit " + + "coefficient must be positive") } return nil }, diff --git a/rfqmsg/request_property_test.go b/rfqmsg/request_property_test.go index 492e5b8c6..29b7c145a 100644 --- a/rfqmsg/request_property_test.go +++ b/rfqmsg/request_property_test.go @@ -2,6 +2,8 @@ package rfqmsg import ( "bytes" + "fmt" + "math/big" "testing" "time" @@ -315,66 +317,203 @@ func TestMinMaxConstraintProperty(t *testing.T) { }) } -// TestRateBoundEnforcementProperty verifies that rate bound -// comparison follows the correct direction semantics: -// - Buy: accepted >= limit passes, accepted < limit fails. -// - Sell: accepted <= limit passes, accepted > limit fails. +// TestNegativeRateLimitRejected verifies that Validate rejects a +// rate limit with a non-positive coefficient for both buy and sell +// requests. +func TestNegativeRateLimitRejected(t *testing.T) { + t.Parallel() + + spec := asset.NewSpecifierFromId(asset.ID{1}) + negLimit := rfqmath.FixedPoint[rfqmath.BigInt]{ + Coefficient: rfqmath.NewBigInt( + big.NewInt(-5), + ), + Scale: 0, + } + zeroLimit := rfqmath.FixedPoint[rfqmath.BigInt]{ + Coefficient: rfqmath.NewBigIntFromUint64(0), + Scale: 0, + } + + t.Run("buy_negative", func(t *testing.T) { + t.Parallel() + req := &BuyRequest{ + Version: V1, + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.Some(negLimit), + AssetRateHint: fn.None[AssetRate](), + } + err := req.Validate() + require.ErrorContains( + t, err, "coefficient must be positive", + ) + }) + + t.Run("buy_zero", func(t *testing.T) { + t.Parallel() + req := &BuyRequest{ + Version: V1, + AssetSpecifier: spec, + AssetMaxAmt: 100, + AssetRateLimit: fn.Some(zeroLimit), + AssetRateHint: fn.None[AssetRate](), + } + err := req.Validate() + require.ErrorContains( + t, err, "coefficient must be positive", + ) + }) + + t.Run("sell_negative", func(t *testing.T) { + t.Parallel() + req := &SellRequest{ + Version: V1, + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.Some(negLimit), + AssetRateHint: fn.None[AssetRate](), + } + err := req.Validate() + require.ErrorContains( + t, err, "coefficient must be positive", + ) + }) + + t.Run("sell_zero", func(t *testing.T) { + t.Parallel() + req := &SellRequest{ + Version: V1, + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + AssetRateLimit: fn.Some(zeroLimit), + AssetRateHint: fn.None[AssetRate](), + } + err := req.Validate() + require.ErrorContains( + t, err, "coefficient must be positive", + ) + }) +} + +// TestRateBoundEnforcementProperty verifies that rate limit fields +// survive a wire roundtrip and preserve the ordering relationship +// that checkRateBound relies on. For each request type we draw a +// random rate limit, encode/decode the request, then confirm that +// Cmp between an independently drawn accepted rate and the decoded +// limit yields the same result as comparing against the original. func TestRateBoundEnforcementProperty(t *testing.T) { t.Parallel() t.Run("buy", func(t *testing.T) { t.Parallel() rapid.Check(t, func(t *rapid.T) { + peer := peerGen().Draw(t, "peer") + id := assetIDGen().Draw(t, "id") + spec := asset.NewSpecifierFromId(id) + + maxAmt := rapid.Uint64Range(1, 1_000_000). + Draw(t, "maxAmt") + limit := fixedPointGen().Draw(t, "limit") + + req, err := NewBuyRequest( + peer, spec, maxAmt, + fn.None[uint64](), + fn.Some(limit), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode( + bytes.NewReader(wireMsg.Data), + ) + require.NoError(t, err) + + decoded, err := NewBuyRequestFromWire( + wireMsg, msgData, + ) + require.NoError(t, err) + + decodedLimit, err := decoded.AssetRateLimit. + UnwrapOrErr( + errMissingRateLimit, + ) + require.NoError(t, err) + + // Ordering vs an independent rate must + // be identical before and after roundtrip. accepted := fixedPointGen().Draw( t, "accepted", ) - limit := fixedPointGen().Draw(t, "limit") - cmp := accepted.Cmp(limit) - - // Buy: accepted must be >= limit. - if cmp >= 0 { - require.False( - t, accepted.Cmp(limit) < 0, - "buy pass: accepted=%v limit=%v", - accepted, limit, - ) - } else { - require.True( - t, accepted.Cmp(limit) < 0, - "buy fail: accepted=%v limit=%v", - accepted, limit, - ) - } + require.Equal( + t, accepted.Cmp(limit), + accepted.Cmp(decodedLimit), + "buy Cmp mismatch after roundtrip", + ) }) }) t.Run("sell", func(t *testing.T) { t.Parallel() rapid.Check(t, func(t *rapid.T) { + peer := peerGen().Draw(t, "peer") + id := assetIDGen().Draw(t, "id") + spec := asset.NewSpecifierFromId(id) + + maxAmt := rapid.Uint64Range( + 1, 1_000_000, + ).Draw(t, "maxAmt") + limit := fixedPointGen().Draw(t, "limit") + + req, err := NewSellRequest( + peer, spec, + lnwire.MilliSatoshi(maxAmt), + fn.None[lnwire.MilliSatoshi](), + fn.Some(limit), + fn.None[AssetRate](), "", + ) + require.NoError(t, err) + + wireMsg, err := req.ToWire() + require.NoError(t, err) + + var msgData requestWireMsgData + err = msgData.Decode( + bytes.NewReader(wireMsg.Data), + ) + require.NoError(t, err) + + decoded, err := NewSellRequestFromWire( + wireMsg, msgData, + ) + require.NoError(t, err) + + decodedLimit, err := decoded.AssetRateLimit. + UnwrapOrErr( + errMissingRateLimit, + ) + require.NoError(t, err) + accepted := fixedPointGen().Draw( t, "accepted", ) - limit := fixedPointGen().Draw(t, "limit") - cmp := accepted.Cmp(limit) - - // Sell: accepted must be <= limit. - if cmp <= 0 { - require.False( - t, accepted.Cmp(limit) > 0, - "sell pass: accepted=%v limit=%v", - accepted, limit, - ) - } else { - require.True( - t, accepted.Cmp(limit) > 0, - "sell fail: accepted=%v limit=%v", - accepted, limit, - ) - } + require.Equal( + t, accepted.Cmp(limit), + accepted.Cmp(decodedLimit), + "sell Cmp mismatch after roundtrip", + ) }) }) } +// errMissingRateLimit is returned when a rate limit is expected +// but not present after wire roundtrip. +var errMissingRateLimit = fmt.Errorf("rate limit missing") + // TestBuyRequestRoundtripWithHintProperty verifies that a BuyRequest // with all fields (including AssetRateHint) survives a roundtrip. func TestBuyRequestRoundtripWithHintProperty(t *testing.T) { diff --git a/rfqmsg/sell_request.go b/rfqmsg/sell_request.go index dcee8ff27..3a255be2d 100644 --- a/rfqmsg/sell_request.go +++ b/rfqmsg/sell_request.go @@ -251,14 +251,14 @@ func (q *SellRequest) Validate() error { return err } - // Ensure rate limit is positive when set. + // Ensure rate limit is strictly positive when set. err = fn.MapOptionZ( q.AssetRateLimit, func(limit rfqmath.BigIntFixedPoint) error { zero := rfqmath.NewBigIntFromUint64(0) - if limit.Coefficient.Equals(zero) { - return fmt.Errorf("asset rate limit must " + - "be positive") + if !limit.Coefficient.Gt(zero) { + return fmt.Errorf("asset rate limit " + + "coefficient must be positive") } return nil }, diff --git a/rpcserver/rpcserver.go b/rpcserver/rpcserver.go index 481fce5da..2607f4ee8 100644 --- a/rpcserver/rpcserver.go +++ b/rpcserver/rpcserver.go @@ -8325,9 +8325,8 @@ func parseAssetSpecifier(reqAssetID []byte, reqAssetIDStr string, return assetID, groupKey, nil } -// unmarshalAssetBuyOrder unmarshals an asset buy order from the RPC form. -// unmarshalOptionalFixedPoint converts an optional RPC FixedPoint into an -// fn.Option[rfqmath.BigIntFixedPoint]. A nil input returns fn.None. +// unmarshalOptionalFixedPoint converts an optional RPC FixedPoint into +// an fn.Option[rfqmath.BigIntFixedPoint]. A nil input returns fn.None. func unmarshalOptionalFixedPoint( fp *rfqrpc.FixedPoint) (fn.Option[rfqmath.BigIntFixedPoint], error) { From 693ab07ad11e5a4be442707c833f77ad91a84106 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 3 Apr 2026 20:18:31 +0800 Subject: [PATCH 09/21] multi: fix lint and itest failures Fix gofmt alignment in rfq_test.go and request_property_test.go. Fix line-length violations in portfolio_pilot.go, portfolio_pilot_test.go, and request_property_test.go. Remove PaymentMinAmt from rate-limit-focused sell sub-tests: at the test oracle rate (1000 units/BTC), 1000 msat converts to zero asset units, tripping the checkMinFill guard. --- .../custom_channels/limit_constraints_test.go | 18 +++++----- itest/rfq_test.go | 3 +- rfq/portfolio_pilot.go | 9 ++--- rfq/portfolio_pilot_test.go | 5 +-- rfqmsg/request_property_test.go | 34 +++++++++++-------- 5 files changed, 38 insertions(+), 31 deletions(-) diff --git a/itest/custom_channels/limit_constraints_test.go b/itest/custom_channels/limit_constraints_test.go index 832b1fc39..6d7539ff6 100644 --- a/itest/custom_channels/limit_constraints_test.go +++ b/itest/custom_channels/limit_constraints_test.go @@ -184,9 +184,6 @@ func testCustomChannelsLimitConstraints(_ context.Context, }, } - // Make sure Erin knows about the Charlie → Dave channel. - assertChannelKnown(t.t, erin, chanPointCD) - logBalance(t.t, nodes, assetID, "after channel open") // ----------------------------------------------------------------- @@ -205,7 +202,6 @@ func testCustomChannelsLimitConstraints(_ context.Context, }, }, PaymentMaxAmt: 180_000_000, - PaymentMinAmt: 1000, AssetRateLimit: &rfqrpc.FixedPoint{ // Ceiling well above oracle rate. Coefficient: "100000000000000", @@ -227,16 +223,22 @@ func testCustomChannelsLimitConstraints(_ context.Context, // ----------------------------------------------------------------- t.Logf("Paying invoice with constrained quote...") - const invoiceAssetAmount = 10_000_000 - invoiceResp := createAssetInvoice( - t.t, dave, erin, invoiceAssetAmount, assetID, + // Erin has no asset channel, so we create a regular BTC + // invoice. Charlie pays it with assets via the sell quote. + const invoiceMsat = 100_000_000 // 100K sats + invoiceResp, err := erin.LightningClient.AddInvoice( + ctxb, &lnrpc.Invoice{ + ValueMsat: invoiceMsat, + }, ) + require.NoError(t.t, err) var quoteID rfqmsg.ID copy(quoteID[:], accepted.Id) numUnits, _ := payInvoiceWithAssets( - t.t, charlie, dave, invoiceResp.PaymentRequest, + t.t, charlie, dave, + invoiceResp.PaymentRequest, assetID, withRFQ(quoteID), ) require.Greater(t.t, numUnits, uint64(0)) diff --git a/itest/rfq_test.go b/itest/rfq_test.go index 5bcad5989..78b651b3e 100644 --- a/itest/rfq_test.go +++ b/itest/rfq_test.go @@ -1000,8 +1000,7 @@ func testRfqLimitConstraints(t *harnessTest) { AssetId: mintedAssetId, }, }, - PaymentMaxAmt: 42000, - PaymentMinAmt: 1000, + PaymentMaxAmt: 42000, AssetRateLimit: &rfqrpc.FixedPoint{ Coefficient: "2000000", Scale: 3, diff --git a/rfq/portfolio_pilot.go b/rfq/portfolio_pilot.go index 2385f8148..881fcf626 100644 --- a/rfq/portfolio_pilot.go +++ b/rfq/portfolio_pilot.go @@ -506,11 +506,12 @@ func checkMinFill(req rfqmsg.Request, notMet := fn.MapOptionZ( r.AssetMinAmt, func(minAmt uint64) bool { + coeff := rfqmath.NewBigIntFromUint64( + minAmt, + ) units := rfqmath.FixedPoint[rfqmath.BigInt]{ - Coefficient: rfqmath.NewBigIntFromUint64( - minAmt, - ), - Scale: 0, + Coefficient: coeff, + Scale: 0, } msat := rfqmath.UnitsToMilliSatoshi( units, acceptedRate, diff --git a/rfq/portfolio_pilot_test.go b/rfq/portfolio_pilot_test.go index b1ae8d449..7956f1c3f 100644 --- a/rfq/portfolio_pilot_test.go +++ b/rfq/portfolio_pilot_test.go @@ -1213,6 +1213,7 @@ func TestCheckRateBound(t *testing.T) { rate100 := rfqmath.NewBigIntFixedPoint(100, 0) rate50 := rfqmath.NewBigIntFixedPoint(50, 0) rate150 := rfqmath.NewBigIntFixedPoint(150, 0) + noLimit := fn.None[rfqmath.BigIntFixedPoint]() tests := []struct { name string @@ -1225,7 +1226,7 @@ func TestCheckRateBound(t *testing.T) { req: &rfqmsg.BuyRequest{ AssetSpecifier: spec, AssetMaxAmt: 100, - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateLimit: noLimit, }, rate: rate100, expect: ValidAcceptQuoteRespStatus, @@ -1265,7 +1266,7 @@ func TestCheckRateBound(t *testing.T) { req: &rfqmsg.SellRequest{ AssetSpecifier: spec, PaymentMaxAmt: 1000, - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateLimit: noLimit, }, rate: rate100, expect: ValidAcceptQuoteRespStatus, diff --git a/rfqmsg/request_property_test.go b/rfqmsg/request_property_test.go index 29b7c145a..f8a53b6c3 100644 --- a/rfqmsg/request_property_test.go +++ b/rfqmsg/request_property_test.go @@ -65,9 +65,13 @@ func fixedPointGen() *rapid.Generator[rfqmath.BigIntFixedPoint] { ) } -// optionalFixedPointGen draws an optional BigIntFixedPoint, None half -// the time. -func optionalFixedPointGen() *rapid.Generator[fn.Option[rfqmath.BigIntFixedPoint]] { +// optFP is a short alias used in test generators to stay +// within the 80-character line limit. +type optFP = fn.Option[rfqmath.BigIntFixedPoint] + +// optionalFixedPointGen draws an optional BigIntFixedPoint, +// None half the time. +func optionalFixedPointGen() *rapid.Generator[optFP] { return rapid.Custom( func(t *rapid.T) fn.Option[rfqmath.BigIntFixedPoint] { if rapid.Bool().Draw(t, "present") { @@ -229,10 +233,10 @@ func TestMinMaxConstraintProperty(t *testing.T) { AssetSpecifier: asset.NewSpecifierFromId( asset.ID{1}, ), - AssetMaxAmt: maxAmt, - AssetMinAmt: fn.Some(minAmt), - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), - AssetRateHint: fn.None[AssetRate](), + AssetMaxAmt: maxAmt, + AssetMinAmt: fn.Some(minAmt), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), //nolint:lll + AssetRateHint: fn.None[AssetRate](), } require.NoError(t, req.Validate()) }) @@ -253,10 +257,10 @@ func TestMinMaxConstraintProperty(t *testing.T) { AssetSpecifier: asset.NewSpecifierFromId( asset.ID{1}, ), - AssetMaxAmt: maxAmt, - AssetMinAmt: fn.Some(minAmt), - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), - AssetRateHint: fn.None[AssetRate](), + AssetMaxAmt: maxAmt, + AssetMinAmt: fn.Some(minAmt), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), //nolint:lll + AssetRateHint: fn.None[AssetRate](), } require.Error(t, req.Validate()) }) @@ -281,8 +285,8 @@ func TestMinMaxConstraintProperty(t *testing.T) { PaymentMinAmt: fn.Some( lnwire.MilliSatoshi(minAmt), ), - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), - AssetRateHint: fn.None[AssetRate](), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), //nolint:lll + AssetRateHint: fn.None[AssetRate](), } require.NoError(t, req.Validate()) }) @@ -309,8 +313,8 @@ func TestMinMaxConstraintProperty(t *testing.T) { PaymentMinAmt: fn.Some( lnwire.MilliSatoshi(minAmt), ), - AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), - AssetRateHint: fn.None[AssetRate](), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), //nolint:lll + AssetRateHint: fn.None[AssetRate](), } require.Error(t, req.Validate()) }) From 194d0c1f0e3908340e51038f2e027ced5789fcf8 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 3 Apr 2026 19:28:12 +0800 Subject: [PATCH 10/21] docs: add release note --- docs/release-notes/release-notes-0.8.0.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/release-notes/release-notes-0.8.0.md b/docs/release-notes/release-notes-0.8.0.md index 2221af873..f03fc676d 100644 --- a/docs/release-notes/release-notes-0.8.0.md +++ b/docs/release-notes/release-notes-0.8.0.md @@ -103,6 +103,14 @@ **Note:** For full functionality, it is highly recommended to start LND with the `--store-final-htlc-resolutions` flag enabled, which is disabled by default. +- [Limit-Order Constraints](https://github.com/lightninglabs/taproot-assets/pull/2048): + RFQ buy and sell orders can now carry explicit limit-price bounds + (`asset_rate_limit`) and minimum fill sizes (`asset_min_amt` / + `payment_min_amt`). Quotes that violate these constraints are rejected + with machine-readable reasons (`RATE_BOUND_MISS`, `MIN_FILL_NOT_MET`). + New fields are optional and backward-compatible; constraint validation + only activates when they are present. + ## Functional Enhancements - [Wallet Backup/Restore](https://github.com/lightninglabs/taproot-assets/pull/1980): @@ -148,6 +156,13 @@ Add `RemoveMessage` RPC to the auth mailbox service. Receivers can authenticate with a Schnorr signature to delete their own messages by ID. +- [PR#2048](https://github.com/lightninglabs/taproot-assets/pull/2048): + Add `asset_rate_limit` to `AddAssetBuyOrder` and `AddAssetSellOrder` + requests. Add `asset_min_amt` to buy orders and `payment_min_amt` + to sell orders. Add `asset_rate_limit` and min fill fields to + `PortfolioPilot.ResolveRequest` for constraint forwarding. Add + `RATE_BOUND_MISS` and `MIN_FILL_NOT_MET` to `QuoteRespStatus`. + ## tapcli Additions - [Wallet Backup CLI](https://github.com/lightninglabs/taproot-assets/pull/1980): @@ -331,6 +346,10 @@ New integration test `testForwardingEventHistory` verifies that forwarding events are properly logged when routing asset payments. +- [PR#2048](https://github.com/lightninglabs/taproot-assets/pull/2048): + Add unit, property-based, and integration tests for limit-order + constraint fields. + ## Database - [forwards table](https://github.com/lightninglabs/taproot-assets/pull/1921): From 211a8d8599ecd8c5667e4407f79874a4b498df79 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:00 +0800 Subject: [PATCH 11/21] rfqmsg: add execution policy type and wire encoding Introduce ExecutionPolicy (uint8) with IOC (0) and FOK (1) constants. Add TLV type alias at TlvType31 and wire it into requestWireMsgData Encode/Decode. Add the field to BuyRequest and SellRequest with constructor parameters, Validate() checks, and wire extraction. --- rfqmsg/buy_request.go | 60 ++++++++++++++++++++++++++++++++++-------- rfqmsg/request.go | 56 +++++++++++++++++++++++++++++++++++++++ rfqmsg/sell_request.go | 50 ++++++++++++++++++++++++++++++----- 3 files changed, 149 insertions(+), 17 deletions(-) diff --git a/rfqmsg/buy_request.go b/rfqmsg/buy_request.go index 9fed655a1..313825df7 100644 --- a/rfqmsg/buy_request.go +++ b/rfqmsg/buy_request.go @@ -65,6 +65,11 @@ type BuyRequest struct { // than X units per BTC." AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // ExecutionPolicy is an optional execution policy for the + // quote request. IOC (default) accepts partial fills; FOK + // requires the full max amount to be viable. + ExecutionPolicy fn.Option[ExecutionPolicy] + // AssetRateHint represents a proposed conversion rate between the // subject asset and BTC. This rate is an initial suggestion intended to // initiate the RFQ negotiation process and may differ from the final @@ -85,7 +90,8 @@ func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, assetMaxAmt uint64, assetMinAmt fn.Option[uint64], assetRateLimit fn.Option[rfqmath.BigIntFixedPoint], assetRateHint fn.Option[AssetRate], - oracleMetadata string) (*BuyRequest, error) { + oracleMetadata string, + execPolicy fn.Option[ExecutionPolicy]) (*BuyRequest, error) { id, err := NewID() if err != nil { @@ -107,6 +113,7 @@ func NewBuyRequest(peer route.Vertex, assetSpecifier asset.Specifier, AssetMaxAmt: assetMaxAmt, AssetMinAmt: assetMinAmt, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, } @@ -192,15 +199,24 @@ func NewBuyRequestFromWire(wireMsg WireMessage, }, ) + // Extract optional execution policy. + var execPolicy fn.Option[ExecutionPolicy] + msgData.ExecutionPolicy.WhenSome( + func(r tlv.RecordT[tlv.TlvType31, uint8]) { + execPolicy = fn.Some(ExecutionPolicy(r.Val)) + }, + ) + req := BuyRequest{ - Peer: wireMsg.Peer, - Version: msgData.Version.Val, - ID: msgData.ID.Val, - AssetSpecifier: assetSpecifier, - AssetMaxAmt: msgData.MaxInAsset.Val, - AssetMinAmt: assetMinAmt, - AssetRateLimit: assetRateLimit, - AssetRateHint: assetRateHint, + Peer: wireMsg.Peer, + Version: msgData.Version.Val, + ID: msgData.ID.Val, + AssetSpecifier: assetSpecifier, + AssetMaxAmt: msgData.MaxInAsset.Val, + AssetMinAmt: assetMinAmt, + AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, + AssetRateHint: assetRateHint, } msgData.PriceOracleMetadata.ValOpt().WhenSome(func(metaBytes []byte) { @@ -264,6 +280,21 @@ func (q *BuyRequest) Validate() error { return err } + // Ensure execution policy is valid when set. + err = fn.MapOptionZ( + q.ExecutionPolicy, + func(p ExecutionPolicy) error { + if p > ExecutionPolicyFOK { + return fmt.Errorf("invalid execution "+ + "policy: %d", p) + } + return nil + }, + ) + if err != nil { + return err + } + // Ensure that the suggested asset rate has not expired. err = fn.MapOptionZ(q.AssetRateHint, func(rate AssetRate) error { if rate.Expiry.Before(time.Now()) { @@ -342,10 +373,17 @@ func (q *BuyRequest) String() string { }, ) + execPolicyStr := fn.MapOptionZ( + q.ExecutionPolicy, + func(p ExecutionPolicy) string { + return fmt.Sprintf(", exec_policy=%d", p) + }, + ) + return fmt.Sprintf("BuyRequest(peer=%x, id=%x, asset=%s, "+ - "max_asset_amount=%d%s%s, asset_rate_hint=%s)", + "max_asset_amount=%d%s%s%s, asset_rate_hint=%s)", q.Peer[:], q.ID[:], q.AssetSpecifier.String(), q.AssetMaxAmt, - minAmtStr, rateLimitStr, assetRateHintStr) + minAmtStr, rateLimitStr, execPolicyStr, assetRateHintStr) } // Ensure that the message type implements the OutgoingMsg interface. diff --git a/rfqmsg/request.go b/rfqmsg/request.go index 37bf692ea..42ffefe71 100644 --- a/rfqmsg/request.go +++ b/rfqmsg/request.go @@ -68,6 +68,26 @@ type ( requestAssetRateLimit = tlv.OptionalRecordT[ tlv.TlvType29, TlvFixedPoint, ] + + // requestExecutionPolicy is a type alias for a record that + // represents an optional execution policy on the quote + // request. + requestExecutionPolicy = tlv.OptionalRecordT[ + tlv.TlvType31, uint8, + ] +) + +// ExecutionPolicy specifies how a quote request should be filled. +type ExecutionPolicy uint8 + +const ( + // ExecutionPolicyIOC (Immediate-Or-Cancel) accepts any partial + // fill >= the min threshold. This is the default behaviour. + ExecutionPolicyIOC ExecutionPolicy = 0 + + // ExecutionPolicyFOK (Fill-Or-Kill) requires the accepted rate + // to support the full max amount or the quote is rejected. + ExecutionPolicyFOK ExecutionPolicy = 1 ) // requestWireMsgData is a struct that represents the message data field for @@ -152,6 +172,10 @@ type requestWireMsgData struct { // requests this is the minimum acceptable rate; for sell requests // this is the maximum acceptable rate. AssetRateLimit requestAssetRateLimit + + // ExecutionPolicy is an optional execution policy for the + // quote request (IOC or FOK). + ExecutionPolicy requestExecutionPolicy } // newRequestWireMsgDataFromBuy creates a new requestWireMsgData from a buy @@ -232,6 +256,16 @@ func newRequestWireMsgDataFromBuy(q BuyRequest) (requestWireMsgData, error) { ) }) + // Set optional execution policy. + var execPolicy requestExecutionPolicy + q.ExecutionPolicy.WhenSome(func(p ExecutionPolicy) { + execPolicy = tlv.SomeRecordT[tlv.TlvType31]( + tlv.NewPrimitiveRecord[tlv.TlvType31]( + uint8(p), + ), + ) + }) + // Encode message data component as TLV bytes. return requestWireMsgData{ Version: version, @@ -246,6 +280,7 @@ func newRequestWireMsgDataFromBuy(q BuyRequest) (requestWireMsgData, error) { InAssetRateHint: inAssetRateHint, MinInAsset: minInAsset, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, PriceOracleMetadata: oracleMetadata, }, nil } @@ -333,6 +368,16 @@ func newRequestWireMsgDataFromSell(q SellRequest) (requestWireMsgData, error) { ) }) + // Set optional execution policy. + var execPolicy requestExecutionPolicy + q.ExecutionPolicy.WhenSome(func(p ExecutionPolicy) { + execPolicy = tlv.SomeRecordT[tlv.TlvType31]( + tlv.NewPrimitiveRecord[tlv.TlvType31]( + uint8(p), + ), + ) + }) + // Encode message data component as TLV bytes. return requestWireMsgData{ Version: version, @@ -346,6 +391,7 @@ func newRequestWireMsgDataFromSell(q SellRequest) (requestWireMsgData, error) { OutAssetRateHint: outAssetRateHint, MinOutAsset: minOutAsset, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, PriceOracleMetadata: oracleMetadata, }, nil } @@ -490,6 +536,11 @@ func (m *requestWireMsgData) Encode(w io.Writer) error { records = append(records, r.Record()) }, ) + m.ExecutionPolicy.WhenSome( + func(r tlv.RecordT[tlv.TlvType31, uint8]) { + records = append(records, r.Record()) + }, + ) tlv.SortRecords(records) @@ -519,6 +570,7 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { oracleMetadata := m.PriceOracleMetadata.Zero() assetRateLimit := m.AssetRateLimit.Zero() + executionPolicy := m.ExecutionPolicy.Zero() // Create a tlv stream with all the fields. tlvStream, err := tlv.NewStream( @@ -543,6 +595,7 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { oracleMetadata.Record(), assetRateLimit.Record(), + executionPolicy.Record(), ) if err != nil { return err @@ -588,6 +641,9 @@ func (m *requestWireMsgData) Decode(r io.Reader) error { if _, ok := tlvMap[assetRateLimit.TlvType()]; ok { m.AssetRateLimit = tlv.SomeRecordT(assetRateLimit) } + if _, ok := tlvMap[executionPolicy.TlvType()]; ok { + m.ExecutionPolicy = tlv.SomeRecordT(executionPolicy) + } return nil } diff --git a/rfqmsg/sell_request.go b/rfqmsg/sell_request.go index 3a255be2d..af31a0875 100644 --- a/rfqmsg/sell_request.go +++ b/rfqmsg/sell_request.go @@ -63,6 +63,11 @@ type SellRequest struct { // than X units per BTC." AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // ExecutionPolicy is an optional execution policy for the + // quote request. IOC (default) accepts partial fills; FOK + // requires the full max amount to be viable. + ExecutionPolicy fn.Option[ExecutionPolicy] + // AssetRateHint represents a proposed conversion rate between the // subject asset and BTC. This rate is an initial suggestion intended to // initiate the RFQ negotiation process and may differ from the final @@ -84,7 +89,8 @@ func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, paymentMinAmt fn.Option[lnwire.MilliSatoshi], assetRateLimit fn.Option[rfqmath.BigIntFixedPoint], assetRateHint fn.Option[AssetRate], - oracleMetadata string) (*SellRequest, error) { + oracleMetadata string, + execPolicy fn.Option[ExecutionPolicy]) (*SellRequest, error) { id, err := NewID() if err != nil { @@ -105,6 +111,7 @@ func NewSellRequest(peer route.Vertex, assetSpecifier asset.Specifier, PaymentMaxAmt: paymentMaxAmt, PaymentMinAmt: paymentMinAmt, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, AssetRateHint: assetRateHint, PriceOracleMetadata: oracleMetadata, } @@ -189,6 +196,14 @@ func NewSellRequestFromWire(wireMsg WireMessage, }, ) + // Extract optional execution policy. + var execPolicy fn.Option[ExecutionPolicy] + msgData.ExecutionPolicy.WhenSome( + func(r tlv.RecordT[tlv.TlvType31, uint8]) { + execPolicy = fn.Some(ExecutionPolicy(r.Val)) + }, + ) + req := SellRequest{ Peer: wireMsg.Peer, Version: msgData.Version.Val, @@ -197,9 +212,10 @@ func NewSellRequestFromWire(wireMsg WireMessage, PaymentMaxAmt: lnwire.MilliSatoshi( msgData.MaxInAsset.Val, ), - PaymentMinAmt: paymentMinAmt, - AssetRateLimit: assetRateLimit, - AssetRateHint: assetRateHint, + PaymentMinAmt: paymentMinAmt, + AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, + AssetRateHint: assetRateHint, } msgData.PriceOracleMetadata.ValOpt().WhenSome(func(metaBytes []byte) { @@ -251,6 +267,21 @@ func (q *SellRequest) Validate() error { return err } + // Ensure execution policy is valid when set. + err = fn.MapOptionZ( + q.ExecutionPolicy, + func(p ExecutionPolicy) error { + if p > ExecutionPolicyFOK { + return fmt.Errorf("invalid execution "+ + "policy: %d", p) + } + return nil + }, + ) + if err != nil { + return err + } + // Ensure rate limit is strictly positive when set. err = fn.MapOptionZ( q.AssetRateLimit, @@ -337,10 +368,17 @@ func (q *SellRequest) String() string { }, ) + execPolicyStr := fn.MapOptionZ( + q.ExecutionPolicy, + func(p ExecutionPolicy) string { + return fmt.Sprintf(", exec_policy=%d", p) + }, + ) + return fmt.Sprintf("SellRequest(peer=%x, id=%x, asset=%s, "+ - "payment_max_amt=%d%s%s, asset_rate_hint=%s)", + "payment_max_amt=%d%s%s%s, asset_rate_hint=%s)", q.Peer[:], q.ID[:], q.AssetSpecifier.String(), q.PaymentMaxAmt, - minAmtStr, rateLimitStr, assetRateHintStr) + minAmtStr, rateLimitStr, execPolicyStr, assetRateHintStr) } // Ensure that the message type implements the OutgoingMsg interface. From eb8b36fda6335bd461c43efc402befff0aeea48a Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:06 +0800 Subject: [PATCH 12/21] rfqmsg+rfq: surface execution policy through orders and negotiator Add ExecutionPolicy field to BuyOrder, SellOrder, AssetSalePolicy, and AssetPurchasePolicy. Thread the field from orders through the negotiator to NewBuyRequest/NewSellRequest. HTLC policy annotation is for future workstream D; no HTLC-level enforcement. --- rfq/negotiator.go | 2 ++ rfq/order.go | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/rfq/negotiator.go b/rfq/negotiator.go index 97ff70815..8f7d90f7a 100644 --- a/rfq/negotiator.go +++ b/rfq/negotiator.go @@ -238,6 +238,7 @@ func (n *Negotiator) HandleOutgoingBuyOrder(ctx context.Context, peer, buyOrder.AssetSpecifier, buyOrder.AssetMaxAmt, buyOrder.AssetMinAmt, buyOrder.AssetRateLimit, assetRateHint, buyOrder.PriceOracleMetadata, + buyOrder.ExecutionPolicy, ) if err != nil { err := fmt.Errorf("unable to create buy request "+ @@ -395,6 +396,7 @@ func (n *Negotiator) HandleOutgoingSellOrder(ctx context.Context, peer, order.AssetSpecifier, order.PaymentMaxAmt, order.PaymentMinAmt, order.AssetRateLimit, assetRateHint, order.PriceOracleMetadata, + order.ExecutionPolicy, ) if err != nil { diff --git a/rfq/order.go b/rfq/order.go index 9a2c8c2a5..594523178 100644 --- a/rfq/order.go +++ b/rfq/order.go @@ -191,6 +191,10 @@ type AssetSalePolicy struct { // peer is the peer pub key of the peer we established this policy with. peer route.Vertex + // ExecutionPolicy is the execution policy from the original + // request (annotation only; not enforced at the HTLC level). + ExecutionPolicy fn.Option[rfqmsg.ExecutionPolicy] + // expiry is the policy's expiry unix timestamp after which the policy // is no longer valid. expiry uint64 @@ -212,6 +216,7 @@ func NewAssetSalePolicy(quote rfqmsg.BuyAccept, noop bool, NoOpHTLCs: noop, auxChanNegotiator: chanNegotiator, peer: quote.Peer, + ExecutionPolicy: quote.Request.ExecutionPolicy, } } @@ -426,6 +431,10 @@ type AssetPurchasePolicy struct { // that they carry. htlcToAmt map[models.CircuitKey]lnwire.MilliSatoshi + // ExecutionPolicy is the execution policy from the original + // request (annotation only; not enforced at the HTLC level). + ExecutionPolicy fn.Option[rfqmsg.ExecutionPolicy] + // expiry is the policy's expiry unix timestamp in seconds after which // the policy is no longer valid. expiry uint64 @@ -443,6 +452,7 @@ func NewAssetPurchasePolicy(quote rfqmsg.SellAccept) *AssetPurchasePolicy { PaymentMaxAmt: quote.Request.PaymentMaxAmt, expiry: uint64(quote.AssetRate.Expiry.Unix()), htlcToAmt: htlcToAmtMap, + ExecutionPolicy: quote.Request.ExecutionPolicy, } } @@ -1740,6 +1750,10 @@ type BuyOrder struct { // units per BTC) for the order. AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // ExecutionPolicy is an optional execution policy (IOC or + // FOK) for the order. + ExecutionPolicy fn.Option[rfqmsg.ExecutionPolicy] + // Expiry is the time at which the order expires. Expiry time.Time @@ -1814,6 +1828,10 @@ type SellOrder struct { // units per BTC) for the order. AssetRateLimit fn.Option[rfqmath.BigIntFixedPoint] + // ExecutionPolicy is an optional execution policy (IOC or + // FOK) for the order. + ExecutionPolicy fn.Option[rfqmsg.ExecutionPolicy] + // Expiry is the time at which the order expires. Expiry time.Time From 42ed92475699b504b27bcf964043cff47c494017 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:13 +0800 Subject: [PATCH 13/21] rfq: add FOK reject code and enforcement in VerifyAcceptQuote Add FOKNotViableRejectCode (4) and FOKNotViableQuoteRespStatus (7). Implement isFOK() and checkFOK() helpers that verify the full max amount is transportable at the accepted rate when FOK is set. Wire checkFOK into VerifyAcceptQuote after checkMinFill. --- rfq/manager.go | 4 +++ rfq/portfolio_pilot.go | 59 ++++++++++++++++++++++++++++++++++++++++++ rfqmsg/reject.go | 11 ++++++++ 3 files changed, 74 insertions(+) diff --git a/rfq/manager.go b/rfq/manager.go index 5c6c1b3e1..48070c86f 100644 --- a/rfq/manager.go +++ b/rfq/manager.go @@ -1448,6 +1448,10 @@ const ( // RateBoundMissQuoteRespStatus indicates that the accepted rate // violated the requester's rate limit constraint. RateBoundMissQuoteRespStatus QuoteRespStatus = 6 + + // FOKNotViableQuoteRespStatus indicates that the FOK execution + // policy could not be satisfied at the accepted rate. + FOKNotViableQuoteRespStatus QuoteRespStatus = 7 ) // InvalidQuoteRespEvent is an event that is broadcast when the RFQ manager diff --git a/rfq/portfolio_pilot.go b/rfq/portfolio_pilot.go index 881fcf626..ba1c02eb8 100644 --- a/rfq/portfolio_pilot.go +++ b/rfq/portfolio_pilot.go @@ -375,6 +375,12 @@ func (p *InternalPortfolioPilot) VerifyAcceptQuote(ctx context.Context, return status, nil } + // Enforce FOK execution policy if set. + status = checkFOK(req, counterRate.Rate) + if status != ValidAcceptQuoteRespStatus { + return status, nil + } + return ValidAcceptQuoteRespStatus, nil } @@ -494,6 +500,59 @@ func checkRateBound(req rfqmsg.Request, return ValidAcceptQuoteRespStatus } +// isFOK returns true if the execution policy is Fill-Or-Kill. +func isFOK(p fn.Option[rfqmsg.ExecutionPolicy]) bool { + return fn.MapOptionZ(p, func(ep rfqmsg.ExecutionPolicy) bool { + return ep == rfqmsg.ExecutionPolicyFOK + }) +} + +// checkFOK verifies that the full max amount is transportable at the +// accepted rate when the execution policy is FOK. For a buy request, +// the max asset amount must convert to non-zero msat. For a sell +// request, the max payment amount must convert to non-zero asset units. +func checkFOK(req rfqmsg.Request, + acceptedRate rfqmath.BigIntFixedPoint) QuoteRespStatus { + + switch r := req.(type) { + case *rfqmsg.BuyRequest: + if !isFOK(r.ExecutionPolicy) { + return ValidAcceptQuoteRespStatus + } + + units := rfqmath.FixedPoint[rfqmath.BigInt]{ + Coefficient: rfqmath.NewBigIntFromUint64( + r.AssetMaxAmt, + ), + Scale: 0, + } + msat := rfqmath.UnitsToMilliSatoshi( + units, acceptedRate, + ) + if msat == 0 { + return FOKNotViableQuoteRespStatus + } + + case *rfqmsg.SellRequest: + if !isFOK(r.ExecutionPolicy) { + return ValidAcceptQuoteRespStatus + } + + units := rfqmath.MilliSatoshiToUnits( + r.PaymentMaxAmt, acceptedRate, + ) + zero := rfqmath.NewBigIntFromUint64(0) + if units.Coefficient.Equals(zero) { + return FOKNotViableQuoteRespStatus + } + + default: + log.Warnf("checkFOK: unhandled request type %T", req) + } + + return ValidAcceptQuoteRespStatus +} + // checkMinFill verifies that the requester's minimum fill amount is // transportable at the accepted rate. For a buy request, the min asset // amount must convert to a non-zero msat value. For a sell request, diff --git a/rfqmsg/reject.go b/rfqmsg/reject.go index ea675a686..56c455213 100644 --- a/rfqmsg/reject.go +++ b/rfqmsg/reject.go @@ -97,6 +97,10 @@ const ( // PriceBoundMissRejectCode indicates that the accepted rate // violated the requester's rate limit constraint. PriceBoundMissRejectCode RejectCode = 3 + + // FOKNotViableRejectCode indicates that the FOK execution + // policy could not be satisfied at the accepted rate. + FOKNotViableRejectCode RejectCode = 4 ) var ( @@ -127,6 +131,13 @@ var ( Code: PriceBoundMissRejectCode, Msg: "rate limit constraint violated", } + + // ErrFOKNotViable is the error for when the FOK execution + // policy cannot be satisfied at the accepted rate. + ErrFOKNotViable = RejectErr{ + Code: FOKNotViableRejectCode, + Msg: "FOK not viable at accepted rate", + } ) // NewRejectErr produces the "unknown" error code, but pairs it with a From 78c650f505bec332291564c55fa9d0400e025ad3 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:19 +0800 Subject: [PATCH 14/21] rfq+rpcserver: add execution policy proto field and RPC unmarshal Add ExecutionPolicy enum (IOC=0, FOK=1) to rfq.proto with field 10 on AddAssetBuyOrderRequest and AddAssetSellOrderRequest. Regenerate proto Go code. Unmarshal FOK in unmarshalAssetBuyOrder/unmarshalAssetSellOrder; IOC/unset maps to fn.None (default behavior). --- rpcserver/rpcserver.go | 16 + taprpc/rfqrpc/rfq.pb.go | 861 ++++++++++++++++++--------------- taprpc/rfqrpc/rfq.proto | 21 + taprpc/rfqrpc/rfq.swagger.json | 17 + 4 files changed, 529 insertions(+), 386 deletions(-) diff --git a/rpcserver/rpcserver.go b/rpcserver/rpcserver.go index 2607f4ee8..2005159da 100644 --- a/rpcserver/rpcserver.go +++ b/rpcserver/rpcserver.go @@ -8414,11 +8414,19 @@ func unmarshalAssetBuyOrder( "limit: %w", err) } + // Unmarshal optional execution policy. Only FOK is + // explicitly set; IOC (0) is the default. + var execPolicy fn.Option[rfqmsg.ExecutionPolicy] + if req.ExecutionPolicy == rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK { + execPolicy = fn.Some(rfqmsg.ExecutionPolicyFOK) + } + return &rfq.BuyOrder{ AssetSpecifier: assetSpecifier, AssetMaxAmt: req.AssetMaxAmt, AssetMinAmt: assetMinAmt, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, Expiry: expiry, Peer: fn.MaybeSome(peer), PriceOracleMetadata: req.PriceOracleMetadata, @@ -8649,11 +8657,19 @@ func unmarshalAssetSellOrder( "limit: %w", err) } + // Unmarshal optional execution policy. Only FOK is + // explicitly set; IOC (0) is the default. + var execPolicy fn.Option[rfqmsg.ExecutionPolicy] + if req.ExecutionPolicy == rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK { + execPolicy = fn.Some(rfqmsg.ExecutionPolicyFOK) + } + return &rfq.SellOrder{ AssetSpecifier: assetSpecifier, PaymentMaxAmt: lnwire.MilliSatoshi(req.PaymentMaxAmt), PaymentMinAmt: paymentMinAmt, AssetRateLimit: assetRateLimit, + ExecutionPolicy: execPolicy, Expiry: expiry, Peer: peer, PriceOracleMetadata: req.PriceOracleMetadata, diff --git a/taprpc/rfqrpc/rfq.pb.go b/taprpc/rfqrpc/rfq.pb.go index 5790532cc..c49677437 100644 --- a/taprpc/rfqrpc/rfq.pb.go +++ b/taprpc/rfqrpc/rfq.pb.go @@ -20,6 +20,57 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// ExecutionPolicy specifies how a quote request should be filled. +type ExecutionPolicy int32 + +const ( + // EXECUTION_POLICY_IOC is Immediate-Or-Cancel: accept any partial + // fill at or above the minimum threshold. This is the default. + ExecutionPolicy_EXECUTION_POLICY_IOC ExecutionPolicy = 0 + // EXECUTION_POLICY_FOK is Fill-Or-Kill: the accepted rate must + // support the full maximum amount or the quote is rejected. + ExecutionPolicy_EXECUTION_POLICY_FOK ExecutionPolicy = 1 +) + +// Enum value maps for ExecutionPolicy. +var ( + ExecutionPolicy_name = map[int32]string{ + 0: "EXECUTION_POLICY_IOC", + 1: "EXECUTION_POLICY_FOK", + } + ExecutionPolicy_value = map[string]int32{ + "EXECUTION_POLICY_IOC": 0, + "EXECUTION_POLICY_FOK": 1, + } +) + +func (x ExecutionPolicy) Enum() *ExecutionPolicy { + p := new(ExecutionPolicy) + *p = x + return p +} + +func (x ExecutionPolicy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ExecutionPolicy) Descriptor() protoreflect.EnumDescriptor { + return file_rfqrpc_rfq_proto_enumTypes[0].Descriptor() +} + +func (ExecutionPolicy) Type() protoreflect.EnumType { + return &file_rfqrpc_rfq_proto_enumTypes[0] +} + +func (x ExecutionPolicy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ExecutionPolicy.Descriptor instead. +func (ExecutionPolicy) EnumDescriptor() ([]byte, []int) { + return file_rfqrpc_rfq_proto_rawDescGZIP(), []int{0} +} + // QuoteRespStatus is an enum that represents the status of a quote response. type QuoteRespStatus int32 @@ -70,11 +121,11 @@ func (x QuoteRespStatus) String() string { } func (QuoteRespStatus) Descriptor() protoreflect.EnumDescriptor { - return file_rfqrpc_rfq_proto_enumTypes[0].Descriptor() + return file_rfqrpc_rfq_proto_enumTypes[1].Descriptor() } func (QuoteRespStatus) Type() protoreflect.EnumType { - return &file_rfqrpc_rfq_proto_enumTypes[0] + return &file_rfqrpc_rfq_proto_enumTypes[1] } func (x QuoteRespStatus) Number() protoreflect.EnumNumber { @@ -83,7 +134,7 @@ func (x QuoteRespStatus) Number() protoreflect.EnumNumber { // Deprecated: Use QuoteRespStatus.Descriptor instead. func (QuoteRespStatus) EnumDescriptor() ([]byte, []int) { - return file_rfqrpc_rfq_proto_rawDescGZIP(), []int{0} + return file_rfqrpc_rfq_proto_rawDescGZIP(), []int{1} } // RfqPolicyType indicates the type of policy of an RFQ session. @@ -119,11 +170,11 @@ func (x RfqPolicyType) String() string { } func (RfqPolicyType) Descriptor() protoreflect.EnumDescriptor { - return file_rfqrpc_rfq_proto_enumTypes[1].Descriptor() + return file_rfqrpc_rfq_proto_enumTypes[2].Descriptor() } func (RfqPolicyType) Type() protoreflect.EnumType { - return &file_rfqrpc_rfq_proto_enumTypes[1] + return &file_rfqrpc_rfq_proto_enumTypes[2] } func (x RfqPolicyType) Number() protoreflect.EnumNumber { @@ -132,7 +183,7 @@ func (x RfqPolicyType) Number() protoreflect.EnumNumber { // Deprecated: Use RfqPolicyType.Descriptor instead. func (RfqPolicyType) EnumDescriptor() ([]byte, []int) { - return file_rfqrpc_rfq_proto_rawDescGZIP(), []int{1} + return file_rfqrpc_rfq_proto_rawDescGZIP(), []int{2} } type AssetSpecifier struct { @@ -370,6 +421,10 @@ type AddAssetBuyOrderRequest struct { // For buy orders this is the minimum acceptable rate (asset units per // BTC). If unset, no rate floor is enforced. AssetRateLimit *FixedPoint `protobuf:"bytes,9,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` + // The execution policy for this order. IOC (default) accepts any + // partial fill >= min threshold. FOK requires the rate to support + // the full max amount. + ExecutionPolicy ExecutionPolicy `protobuf:"varint,10,opt,name=execution_policy,json=executionPolicy,proto3,enum=rfqrpc.ExecutionPolicy" json:"execution_policy,omitempty"` } func (x *AddAssetBuyOrderRequest) Reset() { @@ -467,6 +522,13 @@ func (x *AddAssetBuyOrderRequest) GetAssetRateLimit() *FixedPoint { return nil } +func (x *AddAssetBuyOrderRequest) GetExecutionPolicy() ExecutionPolicy { + if x != nil { + return x.ExecutionPolicy + } + return ExecutionPolicy_EXECUTION_POLICY_IOC +} + type AddAssetBuyOrderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -607,6 +669,10 @@ type AddAssetSellOrderRequest struct { // For sell orders this is the maximum acceptable rate (asset units per // BTC). If unset, no rate ceiling is enforced. AssetRateLimit *FixedPoint `protobuf:"bytes,9,opt,name=asset_rate_limit,json=assetRateLimit,proto3" json:"asset_rate_limit,omitempty"` + // The execution policy for this order. IOC (default) accepts any + // partial fill >= min threshold. FOK requires the rate to support + // the full max amount. + ExecutionPolicy ExecutionPolicy `protobuf:"varint,10,opt,name=execution_policy,json=executionPolicy,proto3,enum=rfqrpc.ExecutionPolicy" json:"execution_policy,omitempty"` } func (x *AddAssetSellOrderRequest) Reset() { @@ -704,6 +770,13 @@ func (x *AddAssetSellOrderRequest) GetAssetRateLimit() *FixedPoint { return nil } +func (x *AddAssetSellOrderRequest) GetExecutionPolicy() ExecutionPolicy { + if x != nil { + return x.ExecutionPolicy + } + return ExecutionPolicy_EXECUTION_POLICY_IOC +} + type AddAssetSellOrderResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2218,7 +2291,7 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x65, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x22, - 0xb0, 0x03, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, + 0xf4, 0x03, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, @@ -2245,312 +2318,325 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x22, 0xfa, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, - 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x45, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x69, - 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x72, - 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, - 0x6f, 0x74, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xb9, 0x03, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x26, 0x0a, - 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, - 0x61, 0x78, 0x41, 0x6d, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x20, 0x0a, - 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, - 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, 0x73, 0x6b, 0x69, 0x70, - 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x73, 0x6b, 0x69, 0x70, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, - 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x41, 0x6d, 0x74, 0x12, 0x3c, 0x0a, - 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xfc, 0x01, 0x0a, 0x19, - 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, - 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x71, 0x75, 0x6f, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, - 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x42, 0x0a, - 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x18, 0x41, 0x64, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, - 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, - 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, - 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1b, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x77, 0x0a, 0x17, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, - 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, - 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x64, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x0a, 0x1e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3f, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, - 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xe8, 0x02, 0x0a, 0x14, 0x50, 0x65, - 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, - 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x61, 0x73, 0x6b, 0x5f, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, - 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x0c, 0x61, 0x73, 0x6b, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x32, - 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x70, 0x65, 0x63, 0x22, 0xe0, 0x02, 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, - 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, - 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, - 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x62, 0x69, 0x64, - 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x62, 0x69, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, - 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, - 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x6d, 0x69, 0x6e, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x73, 0x61, - 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, - 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, 0x6b, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x02, 0x69, 0x64, 0x22, 0x7f, 0x0a, 0x15, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, - 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, - 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x9e, 0x01, 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x62, 0x75, 0x79, - 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x09, 0x62, 0x75, 0x79, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, - 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, + 0x69, 0x74, 0x12, 0x42, 0x0a, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, + 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0xfa, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, + 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x6c, - 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x1f, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, - 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x19, 0x50, 0x65, 0x65, 0x72, + 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x00, 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, + 0x46, 0x0a, 0x0e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xfd, 0x03, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x61, 0x78, + 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, + 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x79, 0x12, 0x20, 0x0a, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x18, + 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, + 0x65, 0x6c, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, + 0x73, 0x6b, 0x69, 0x70, 0x41, 0x73, 0x73, 0x65, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x6f, + 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, 0x6c, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x41, 0x6d, + 0x74, 0x12, 0x3c, 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, + 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, + 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x42, 0x0a, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x22, 0xfc, 0x01, 0x0a, 0x19, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x46, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, + 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, + 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x69, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, + 0x52, 0x0c, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x46, + 0x0a, 0x0e, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x78, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, + 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, + 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, + 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x22, 0x1b, 0x0a, 0x19, + 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x0a, 0x17, 0x41, 0x64, 0x64, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, + 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x69, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x69, + 0x74, 0x73, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, + 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, + 0x0a, 0x1e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x3f, 0x0a, 0x09, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, + 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x22, 0xe8, 0x02, 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, + 0x69, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x61, 0x78, 0x5f, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x4d, 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x0e, + 0x61, 0x73, 0x6b, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, + 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x61, 0x73, 0x6b, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x36, + 0x0a, 0x17, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x15, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x55, 0x6e, 0x69, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, + 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x61, 0x63, + 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, 0xe0, 0x02, 0x0a, + 0x15, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, + 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x38, 0x0a, 0x0e, 0x62, 0x69, 0x64, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x72, + 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0c, 0x62, + 0x69, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x79, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x70, 0x6f, 0x72, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x14, 0x6d, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x69, + 0x63, 0x65, 0x5f, 0x6f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x69, 0x63, 0x65, 0x4f, + 0x72, 0x61, 0x63, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, + 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x22, + 0x6b, 0x0a, 0x14, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x22, 0x7f, 0x0a, 0x15, + 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x9e, 0x01, + 0x0a, 0x1f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, + 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3b, 0x0a, 0x0a, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x52, 0x09, 0x62, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x3e, + 0x0a, 0x0b, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, + 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, + 0x74, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x1f, + 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x8e, 0x01, 0x0a, 0x19, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x53, 0x0a, 0x17, 0x70, + 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, + 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, + 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x53, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, - 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, - 0x74, 0x65, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, - 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x1a, 0x50, 0x65, 0x65, + 0x22, 0x92, 0x01, 0x0a, 0x1a, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x56, 0x0a, + 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, + 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x15, + 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x43, 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, + 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, 0x69, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x08, 0x52, + 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x5a, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, + 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x70, + 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, + 0x6f, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, + 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, + 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, - 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x56, 0x0a, 0x18, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, - 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x43, 0x0a, - 0x0f, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x63, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x63, - 0x69, 0x64, 0x22, 0x8a, 0x02, 0x0a, 0x08, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x5a, 0x0a, 0x17, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, - 0x5f, 0x62, 0x75, 0x79, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x14, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x42, 0x75, 0x79, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x5d, 0x0a, 0x18, 0x70, - 0x65, 0x65, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x6c, - 0x6c, 0x5f, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x15, 0x70, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x53, 0x65, 0x6c, 0x6c, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x61, 0x63, - 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, - 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, - 0xe7, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, - 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x71, 0x0a, 0x19, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdd, 0x03, 0x0a, - 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, - 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, - 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x1e, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, - 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, - 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x1b, - 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x09, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, - 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, - 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, 0x6e, - 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, - 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x4f, - 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, - 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, - 0x65, 0x72, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x53, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, - 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, 0x72, 0x61, 0x74, 0x65, 0x2a, 0x8b, 0x01, 0x0a, - 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, - 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, - 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, - 0x16, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, 0x55, - 0x45, 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, - 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, - 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, - 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, - 0x71, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, - 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, - 0x41, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, - 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, - 0x45, 0x10, 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, - 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, - 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, - 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, - 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, + 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x68, 0x74, 0x6c, + 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x48, 0x74, 0x6c, 0x63, 0x42, 0x07, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xe7, 0x01, 0x0a, 0x18, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6d, 0x69, 0x6e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x65, + 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x66, + 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x52, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, + 0x69, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x71, 0x0a, 0x19, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, + 0x0a, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdd, 0x03, 0x0a, 0x0f, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, + 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x66, 0x71, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x72, 0x66, 0x71, 0x49, 0x64, 0x12, + 0x1c, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x08, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x49, 0x6e, 0x12, 0x1e, 0x0a, + 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x49, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x0a, + 0x07, 0x68, 0x74, 0x6c, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x68, 0x74, 0x6c, 0x63, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x6e, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x65, 0x74, 0x74, 0x6c, 0x65, 0x64, + 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, + 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x61, 0x6d, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6d, 0x74, 0x12, 0x1e, 0x0a, 0x0b, + 0x61, 0x6d, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x61, 0x6d, 0x74, 0x49, 0x6e, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0c, + 0x61, 0x6d, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x74, 0x4f, 0x75, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x36, + 0x0a, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x0a, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x26, 0x0a, 0x04, + 0x72, 0x61, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x66, 0x71, + 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x04, + 0x72, 0x61, 0x74, 0x65, 0x2a, 0x45, 0x0a, 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x58, 0x45, 0x43, 0x55, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x49, 0x4f, 0x43, 0x10, + 0x00, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x46, 0x4f, 0x4b, 0x10, 0x01, 0x2a, 0x8b, 0x01, 0x0a, 0x0f, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, + 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, 0x41, + 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x59, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, + 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, 0x4f, 0x52, 0x41, 0x43, 0x4c, 0x45, 0x5f, 0x51, 0x55, 0x45, + 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, 0x54, + 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x10, + 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, + 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, 0x71, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x46, + 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x41, + 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, + 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, 0x45, + 0x10, 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, + 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1f, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, + 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, + 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, + 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, + 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, + 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, - 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, - 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, - 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, - 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, - 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, - 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, - 0x66, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, - 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, - 0x66, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, - 0x0a, 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, - 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, - 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, + 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x27, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, + 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, + 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, 0x0a, + 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, + 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, + 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, + 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2565,86 +2651,89 @@ func file_rfqrpc_rfq_proto_rawDescGZIP() []byte { return file_rfqrpc_rfq_proto_rawDescData } -var file_rfqrpc_rfq_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_rfqrpc_rfq_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_rfqrpc_rfq_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_rfqrpc_rfq_proto_goTypes = []any{ - (QuoteRespStatus)(0), // 0: rfqrpc.QuoteRespStatus - (RfqPolicyType)(0), // 1: rfqrpc.RfqPolicyType - (*AssetSpecifier)(nil), // 2: rfqrpc.AssetSpecifier - (*FixedPoint)(nil), // 3: rfqrpc.FixedPoint - (*AddAssetBuyOrderRequest)(nil), // 4: rfqrpc.AddAssetBuyOrderRequest - (*AddAssetBuyOrderResponse)(nil), // 5: rfqrpc.AddAssetBuyOrderResponse - (*AddAssetSellOrderRequest)(nil), // 6: rfqrpc.AddAssetSellOrderRequest - (*AddAssetSellOrderResponse)(nil), // 7: rfqrpc.AddAssetSellOrderResponse - (*AddAssetSellOfferRequest)(nil), // 8: rfqrpc.AddAssetSellOfferRequest - (*AddAssetSellOfferResponse)(nil), // 9: rfqrpc.AddAssetSellOfferResponse - (*AddAssetBuyOfferRequest)(nil), // 10: rfqrpc.AddAssetBuyOfferRequest - (*AddAssetBuyOfferResponse)(nil), // 11: rfqrpc.AddAssetBuyOfferResponse - (*QueryPeerAcceptedQuotesRequest)(nil), // 12: rfqrpc.QueryPeerAcceptedQuotesRequest - (*AssetSpec)(nil), // 13: rfqrpc.AssetSpec - (*PeerAcceptedBuyQuote)(nil), // 14: rfqrpc.PeerAcceptedBuyQuote - (*PeerAcceptedSellQuote)(nil), // 15: rfqrpc.PeerAcceptedSellQuote - (*InvalidQuoteResponse)(nil), // 16: rfqrpc.InvalidQuoteResponse - (*RejectedQuoteResponse)(nil), // 17: rfqrpc.RejectedQuoteResponse - (*QueryPeerAcceptedQuotesResponse)(nil), // 18: rfqrpc.QueryPeerAcceptedQuotesResponse - (*SubscribeRfqEventNtfnsRequest)(nil), // 19: rfqrpc.SubscribeRfqEventNtfnsRequest - (*PeerAcceptedBuyQuoteEvent)(nil), // 20: rfqrpc.PeerAcceptedBuyQuoteEvent - (*PeerAcceptedSellQuoteEvent)(nil), // 21: rfqrpc.PeerAcceptedSellQuoteEvent - (*AcceptHtlcEvent)(nil), // 22: rfqrpc.AcceptHtlcEvent - (*RfqEvent)(nil), // 23: rfqrpc.RfqEvent - (*ForwardingHistoryRequest)(nil), // 24: rfqrpc.ForwardingHistoryRequest - (*ForwardingHistoryResponse)(nil), // 25: rfqrpc.ForwardingHistoryResponse - (*ForwardingEvent)(nil), // 26: rfqrpc.ForwardingEvent + (ExecutionPolicy)(0), // 0: rfqrpc.ExecutionPolicy + (QuoteRespStatus)(0), // 1: rfqrpc.QuoteRespStatus + (RfqPolicyType)(0), // 2: rfqrpc.RfqPolicyType + (*AssetSpecifier)(nil), // 3: rfqrpc.AssetSpecifier + (*FixedPoint)(nil), // 4: rfqrpc.FixedPoint + (*AddAssetBuyOrderRequest)(nil), // 5: rfqrpc.AddAssetBuyOrderRequest + (*AddAssetBuyOrderResponse)(nil), // 6: rfqrpc.AddAssetBuyOrderResponse + (*AddAssetSellOrderRequest)(nil), // 7: rfqrpc.AddAssetSellOrderRequest + (*AddAssetSellOrderResponse)(nil), // 8: rfqrpc.AddAssetSellOrderResponse + (*AddAssetSellOfferRequest)(nil), // 9: rfqrpc.AddAssetSellOfferRequest + (*AddAssetSellOfferResponse)(nil), // 10: rfqrpc.AddAssetSellOfferResponse + (*AddAssetBuyOfferRequest)(nil), // 11: rfqrpc.AddAssetBuyOfferRequest + (*AddAssetBuyOfferResponse)(nil), // 12: rfqrpc.AddAssetBuyOfferResponse + (*QueryPeerAcceptedQuotesRequest)(nil), // 13: rfqrpc.QueryPeerAcceptedQuotesRequest + (*AssetSpec)(nil), // 14: rfqrpc.AssetSpec + (*PeerAcceptedBuyQuote)(nil), // 15: rfqrpc.PeerAcceptedBuyQuote + (*PeerAcceptedSellQuote)(nil), // 16: rfqrpc.PeerAcceptedSellQuote + (*InvalidQuoteResponse)(nil), // 17: rfqrpc.InvalidQuoteResponse + (*RejectedQuoteResponse)(nil), // 18: rfqrpc.RejectedQuoteResponse + (*QueryPeerAcceptedQuotesResponse)(nil), // 19: rfqrpc.QueryPeerAcceptedQuotesResponse + (*SubscribeRfqEventNtfnsRequest)(nil), // 20: rfqrpc.SubscribeRfqEventNtfnsRequest + (*PeerAcceptedBuyQuoteEvent)(nil), // 21: rfqrpc.PeerAcceptedBuyQuoteEvent + (*PeerAcceptedSellQuoteEvent)(nil), // 22: rfqrpc.PeerAcceptedSellQuoteEvent + (*AcceptHtlcEvent)(nil), // 23: rfqrpc.AcceptHtlcEvent + (*RfqEvent)(nil), // 24: rfqrpc.RfqEvent + (*ForwardingHistoryRequest)(nil), // 25: rfqrpc.ForwardingHistoryRequest + (*ForwardingHistoryResponse)(nil), // 26: rfqrpc.ForwardingHistoryResponse + (*ForwardingEvent)(nil), // 27: rfqrpc.ForwardingEvent } var file_rfqrpc_rfq_proto_depIdxs = []int32{ - 2, // 0: rfqrpc.AddAssetBuyOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 3, // 1: rfqrpc.AddAssetBuyOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint - 14, // 2: rfqrpc.AddAssetBuyOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote - 16, // 3: rfqrpc.AddAssetBuyOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse - 17, // 4: rfqrpc.AddAssetBuyOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse - 2, // 5: rfqrpc.AddAssetSellOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 3, // 6: rfqrpc.AddAssetSellOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint - 15, // 7: rfqrpc.AddAssetSellOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedSellQuote - 16, // 8: rfqrpc.AddAssetSellOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse - 17, // 9: rfqrpc.AddAssetSellOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse - 2, // 10: rfqrpc.AddAssetSellOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 2, // 11: rfqrpc.AddAssetBuyOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 3, // 12: rfqrpc.PeerAcceptedBuyQuote.ask_asset_rate:type_name -> rfqrpc.FixedPoint - 13, // 13: rfqrpc.PeerAcceptedBuyQuote.asset_spec:type_name -> rfqrpc.AssetSpec - 3, // 14: rfqrpc.PeerAcceptedSellQuote.bid_asset_rate:type_name -> rfqrpc.FixedPoint - 13, // 15: rfqrpc.PeerAcceptedSellQuote.asset_spec:type_name -> rfqrpc.AssetSpec - 0, // 16: rfqrpc.InvalidQuoteResponse.status:type_name -> rfqrpc.QuoteRespStatus - 14, // 17: rfqrpc.QueryPeerAcceptedQuotesResponse.buy_quotes:type_name -> rfqrpc.PeerAcceptedBuyQuote - 15, // 18: rfqrpc.QueryPeerAcceptedQuotesResponse.sell_quotes:type_name -> rfqrpc.PeerAcceptedSellQuote - 14, // 19: rfqrpc.PeerAcceptedBuyQuoteEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote - 15, // 20: rfqrpc.PeerAcceptedSellQuoteEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuote - 20, // 21: rfqrpc.RfqEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuoteEvent - 21, // 22: rfqrpc.RfqEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuoteEvent - 22, // 23: rfqrpc.RfqEvent.accept_htlc:type_name -> rfqrpc.AcceptHtlcEvent - 2, // 24: rfqrpc.ForwardingHistoryRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier - 26, // 25: rfqrpc.ForwardingHistoryResponse.forwards:type_name -> rfqrpc.ForwardingEvent - 1, // 26: rfqrpc.ForwardingEvent.policy_type:type_name -> rfqrpc.RfqPolicyType - 13, // 27: rfqrpc.ForwardingEvent.asset_spec:type_name -> rfqrpc.AssetSpec - 3, // 28: rfqrpc.ForwardingEvent.rate:type_name -> rfqrpc.FixedPoint - 4, // 29: rfqrpc.Rfq.AddAssetBuyOrder:input_type -> rfqrpc.AddAssetBuyOrderRequest - 6, // 30: rfqrpc.Rfq.AddAssetSellOrder:input_type -> rfqrpc.AddAssetSellOrderRequest - 8, // 31: rfqrpc.Rfq.AddAssetSellOffer:input_type -> rfqrpc.AddAssetSellOfferRequest - 10, // 32: rfqrpc.Rfq.AddAssetBuyOffer:input_type -> rfqrpc.AddAssetBuyOfferRequest - 12, // 33: rfqrpc.Rfq.QueryPeerAcceptedQuotes:input_type -> rfqrpc.QueryPeerAcceptedQuotesRequest - 19, // 34: rfqrpc.Rfq.SubscribeRfqEventNtfns:input_type -> rfqrpc.SubscribeRfqEventNtfnsRequest - 24, // 35: rfqrpc.Rfq.ForwardingHistory:input_type -> rfqrpc.ForwardingHistoryRequest - 5, // 36: rfqrpc.Rfq.AddAssetBuyOrder:output_type -> rfqrpc.AddAssetBuyOrderResponse - 7, // 37: rfqrpc.Rfq.AddAssetSellOrder:output_type -> rfqrpc.AddAssetSellOrderResponse - 9, // 38: rfqrpc.Rfq.AddAssetSellOffer:output_type -> rfqrpc.AddAssetSellOfferResponse - 11, // 39: rfqrpc.Rfq.AddAssetBuyOffer:output_type -> rfqrpc.AddAssetBuyOfferResponse - 18, // 40: rfqrpc.Rfq.QueryPeerAcceptedQuotes:output_type -> rfqrpc.QueryPeerAcceptedQuotesResponse - 23, // 41: rfqrpc.Rfq.SubscribeRfqEventNtfns:output_type -> rfqrpc.RfqEvent - 25, // 42: rfqrpc.Rfq.ForwardingHistory:output_type -> rfqrpc.ForwardingHistoryResponse - 36, // [36:43] is the sub-list for method output_type - 29, // [29:36] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 3, // 0: rfqrpc.AddAssetBuyOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 4, // 1: rfqrpc.AddAssetBuyOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint + 0, // 2: rfqrpc.AddAssetBuyOrderRequest.execution_policy:type_name -> rfqrpc.ExecutionPolicy + 15, // 3: rfqrpc.AddAssetBuyOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote + 17, // 4: rfqrpc.AddAssetBuyOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse + 18, // 5: rfqrpc.AddAssetBuyOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse + 3, // 6: rfqrpc.AddAssetSellOrderRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 4, // 7: rfqrpc.AddAssetSellOrderRequest.asset_rate_limit:type_name -> rfqrpc.FixedPoint + 0, // 8: rfqrpc.AddAssetSellOrderRequest.execution_policy:type_name -> rfqrpc.ExecutionPolicy + 16, // 9: rfqrpc.AddAssetSellOrderResponse.accepted_quote:type_name -> rfqrpc.PeerAcceptedSellQuote + 17, // 10: rfqrpc.AddAssetSellOrderResponse.invalid_quote:type_name -> rfqrpc.InvalidQuoteResponse + 18, // 11: rfqrpc.AddAssetSellOrderResponse.rejected_quote:type_name -> rfqrpc.RejectedQuoteResponse + 3, // 12: rfqrpc.AddAssetSellOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 3, // 13: rfqrpc.AddAssetBuyOfferRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 4, // 14: rfqrpc.PeerAcceptedBuyQuote.ask_asset_rate:type_name -> rfqrpc.FixedPoint + 14, // 15: rfqrpc.PeerAcceptedBuyQuote.asset_spec:type_name -> rfqrpc.AssetSpec + 4, // 16: rfqrpc.PeerAcceptedSellQuote.bid_asset_rate:type_name -> rfqrpc.FixedPoint + 14, // 17: rfqrpc.PeerAcceptedSellQuote.asset_spec:type_name -> rfqrpc.AssetSpec + 1, // 18: rfqrpc.InvalidQuoteResponse.status:type_name -> rfqrpc.QuoteRespStatus + 15, // 19: rfqrpc.QueryPeerAcceptedQuotesResponse.buy_quotes:type_name -> rfqrpc.PeerAcceptedBuyQuote + 16, // 20: rfqrpc.QueryPeerAcceptedQuotesResponse.sell_quotes:type_name -> rfqrpc.PeerAcceptedSellQuote + 15, // 21: rfqrpc.PeerAcceptedBuyQuoteEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuote + 16, // 22: rfqrpc.PeerAcceptedSellQuoteEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuote + 21, // 23: rfqrpc.RfqEvent.peer_accepted_buy_quote:type_name -> rfqrpc.PeerAcceptedBuyQuoteEvent + 22, // 24: rfqrpc.RfqEvent.peer_accepted_sell_quote:type_name -> rfqrpc.PeerAcceptedSellQuoteEvent + 23, // 25: rfqrpc.RfqEvent.accept_htlc:type_name -> rfqrpc.AcceptHtlcEvent + 3, // 26: rfqrpc.ForwardingHistoryRequest.asset_specifier:type_name -> rfqrpc.AssetSpecifier + 27, // 27: rfqrpc.ForwardingHistoryResponse.forwards:type_name -> rfqrpc.ForwardingEvent + 2, // 28: rfqrpc.ForwardingEvent.policy_type:type_name -> rfqrpc.RfqPolicyType + 14, // 29: rfqrpc.ForwardingEvent.asset_spec:type_name -> rfqrpc.AssetSpec + 4, // 30: rfqrpc.ForwardingEvent.rate:type_name -> rfqrpc.FixedPoint + 5, // 31: rfqrpc.Rfq.AddAssetBuyOrder:input_type -> rfqrpc.AddAssetBuyOrderRequest + 7, // 32: rfqrpc.Rfq.AddAssetSellOrder:input_type -> rfqrpc.AddAssetSellOrderRequest + 9, // 33: rfqrpc.Rfq.AddAssetSellOffer:input_type -> rfqrpc.AddAssetSellOfferRequest + 11, // 34: rfqrpc.Rfq.AddAssetBuyOffer:input_type -> rfqrpc.AddAssetBuyOfferRequest + 13, // 35: rfqrpc.Rfq.QueryPeerAcceptedQuotes:input_type -> rfqrpc.QueryPeerAcceptedQuotesRequest + 20, // 36: rfqrpc.Rfq.SubscribeRfqEventNtfns:input_type -> rfqrpc.SubscribeRfqEventNtfnsRequest + 25, // 37: rfqrpc.Rfq.ForwardingHistory:input_type -> rfqrpc.ForwardingHistoryRequest + 6, // 38: rfqrpc.Rfq.AddAssetBuyOrder:output_type -> rfqrpc.AddAssetBuyOrderResponse + 8, // 39: rfqrpc.Rfq.AddAssetSellOrder:output_type -> rfqrpc.AddAssetSellOrderResponse + 10, // 40: rfqrpc.Rfq.AddAssetSellOffer:output_type -> rfqrpc.AddAssetSellOfferResponse + 12, // 41: rfqrpc.Rfq.AddAssetBuyOffer:output_type -> rfqrpc.AddAssetBuyOfferResponse + 19, // 42: rfqrpc.Rfq.QueryPeerAcceptedQuotes:output_type -> rfqrpc.QueryPeerAcceptedQuotesResponse + 24, // 43: rfqrpc.Rfq.SubscribeRfqEventNtfns:output_type -> rfqrpc.RfqEvent + 26, // 44: rfqrpc.Rfq.ForwardingHistory:output_type -> rfqrpc.ForwardingHistoryResponse + 38, // [38:45] is the sub-list for method output_type + 31, // [31:38] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name } func init() { file_rfqrpc_rfq_proto_init() } @@ -2980,7 +3069,7 @@ func file_rfqrpc_rfq_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rfqrpc_rfq_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 25, NumExtensions: 0, NumServices: 1, diff --git a/taprpc/rfqrpc/rfq.proto b/taprpc/rfqrpc/rfq.proto index f64ec2278..7ad4b995e 100644 --- a/taprpc/rfqrpc/rfq.proto +++ b/taprpc/rfqrpc/rfq.proto @@ -127,6 +127,17 @@ message FixedPoint { uint32 scale = 2; } +// ExecutionPolicy specifies how a quote request should be filled. +enum ExecutionPolicy { + // EXECUTION_POLICY_IOC is Immediate-Or-Cancel: accept any partial + // fill at or above the minimum threshold. This is the default. + EXECUTION_POLICY_IOC = 0; + + // EXECUTION_POLICY_FOK is Fill-Or-Kill: the accepted rate must + // support the full maximum amount or the quote is rejected. + EXECUTION_POLICY_FOK = 1; +} + message AddAssetBuyOrderRequest { // asset_specifier is the subject asset. AssetSpecifier asset_specifier = 1; @@ -170,6 +181,11 @@ message AddAssetBuyOrderRequest { // For buy orders this is the minimum acceptable rate (asset units per // BTC). If unset, no rate floor is enforced. FixedPoint asset_rate_limit = 9; + + // The execution policy for this order. IOC (default) accepts any + // partial fill >= min threshold. FOK requires the rate to support + // the full max amount. + ExecutionPolicy execution_policy = 10; } message AddAssetBuyOrderResponse { @@ -231,6 +247,11 @@ message AddAssetSellOrderRequest { // For sell orders this is the maximum acceptable rate (asset units per // BTC). If unset, no rate ceiling is enforced. FixedPoint asset_rate_limit = 9; + + // The execution policy for this order. IOC (default) accepts any + // partial fill >= min threshold. FOK requires the rate to support + // the full max amount. + ExecutionPolicy execution_policy = 10; } message AddAssetSellOrderResponse { diff --git a/taprpc/rfqrpc/rfq.swagger.json b/taprpc/rfqrpc/rfq.swagger.json index 9d79afb39..6cb432c8c 100644 --- a/taprpc/rfqrpc/rfq.swagger.json +++ b/taprpc/rfqrpc/rfq.swagger.json @@ -593,6 +593,10 @@ "asset_rate_limit": { "$ref": "#/definitions/rfqrpcFixedPoint", "description": "An optional rate limit constraint expressed as a fixed-point number.\nFor buy orders this is the minimum acceptable rate (asset units per\nBTC). If unset, no rate floor is enforced." + }, + "execution_policy": { + "$ref": "#/definitions/rfqrpcExecutionPolicy", + "description": "The execution policy for this order. IOC (default) accepts any\npartial fill \u003e= min threshold. FOK requires the rate to support\nthe full max amount." } } }, @@ -687,6 +691,10 @@ "asset_rate_limit": { "$ref": "#/definitions/rfqrpcFixedPoint", "description": "An optional rate limit constraint expressed as a fixed-point number.\nFor sell orders this is the maximum acceptable rate (asset units per\nBTC). If unset, no rate ceiling is enforced." + }, + "execution_policy": { + "$ref": "#/definitions/rfqrpcExecutionPolicy", + "description": "The execution policy for this order. IOC (default) accepts any\npartial fill \u003e= min threshold. FOK requires the rate to support\nthe full max amount." } } }, @@ -792,6 +800,15 @@ } } }, + "rfqrpcExecutionPolicy": { + "type": "string", + "enum": [ + "EXECUTION_POLICY_IOC", + "EXECUTION_POLICY_FOK" + ], + "default": "EXECUTION_POLICY_IOC", + "description": "ExecutionPolicy specifies how a quote request should be filled.\n\n - EXECUTION_POLICY_IOC: EXECUTION_POLICY_IOC is Immediate-Or-Cancel: accept any partial\nfill at or above the minimum threshold. This is the default.\n - EXECUTION_POLICY_FOK: EXECUTION_POLICY_FOK is Fill-Or-Kill: the accepted rate must\nsupport the full maximum amount or the quote is rejected." + }, "rfqrpcFixedPoint": { "type": "object", "properties": { From 194dac46ff3cc03a352bf8dc94d7ff30e927c3db Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:26 +0800 Subject: [PATCH 15/21] rfqmsg: add unit and property tests for execution policy Add roundtrip tests for FOK and absent execution policy on both BuyRequest and SellRequest. Add Validate() rejection test for unknown policy values. Add optionalExecutionPolicyGen and requireOptExecPolicyEq helpers to property tests; include execution policy in all roundtrip property checks. --- rfqmsg/buy_request_test.go | 71 +++++++++++++++++++++++++++++++++ rfqmsg/sell_request_test.go | 79 ++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/rfqmsg/buy_request_test.go b/rfqmsg/buy_request_test.go index 1ee7b2f57..5f3b9cc5b 100644 --- a/rfqmsg/buy_request_test.go +++ b/rfqmsg/buy_request_test.go @@ -44,6 +44,7 @@ func TestBuyRequestMinAmtRoundtrip(t *testing.T) { peer, spec, 100, fn.Some[uint64](50), fn.None[rfqmath.BigIntFixedPoint](), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -68,6 +69,7 @@ func TestBuyRequestRateLimitRoundtrip(t *testing.T) { req, err := NewBuyRequest( peer, spec, 200, fn.None[uint64](), fn.Some(limit), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -93,6 +95,7 @@ func TestBuyRequestNoOptionalFieldsRoundtrip(t *testing.T) { peer, spec, 300, fn.None[uint64](), fn.None[rfqmath.BigIntFixedPoint](), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -119,6 +122,7 @@ func TestBuyRequestAllFieldsRoundtrip(t *testing.T) { req, err := NewBuyRequest( peer, spec, 500, fn.Some[uint64](10), fn.Some(limit), fn.Some(rateHint), "metadata", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -194,3 +198,70 @@ func TestBuyRequestValidateZeroRateLimit(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "must be positive") } + +// TestBuyRequestExecutionPolicyRoundtrip verifies that a FOK +// execution policy survives a wire roundtrip. +func TestBuyRequestExecutionPolicyRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x05} + spec := asset.NewSpecifierFromId(asset.ID{0xEE}) + + req, err := NewBuyRequest( + peer, spec, 400, fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + fn.Some(ExecutionPolicyFOK), + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.ExecutionPolicy.IsSome()) + decoded.ExecutionPolicy.WhenSome( + func(v ExecutionPolicy) { + require.Equal(t, ExecutionPolicyFOK, v) + }, + ) +} + +// TestBuyRequestNoExecutionPolicyRoundtrip verifies that an absent +// execution policy stays None after a wire roundtrip. +func TestBuyRequestNoExecutionPolicyRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x06} + spec := asset.NewSpecifierFromId(asset.ID{0xFF}) + + req, err := NewBuyRequest( + peer, spec, 500, fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), + ) + require.NoError(t, err) + + decoded := buyRequestRoundtrip(t, req) + + require.True(t, decoded.ExecutionPolicy.IsNone()) +} + +// TestBuyRequestInvalidExecutionPolicy ensures Validate rejects +// an unknown execution policy value. +func TestBuyRequestInvalidExecutionPolicy(t *testing.T) { + t.Parallel() + + req := &BuyRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + AssetMaxAmt: 100, + AssetMinAmt: fn.None[uint64](), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + ExecutionPolicy: fn.Some(ExecutionPolicy(2)), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "execution policy") +} diff --git a/rfqmsg/sell_request_test.go b/rfqmsg/sell_request_test.go index 099170d62..09d25ff71 100644 --- a/rfqmsg/sell_request_test.go +++ b/rfqmsg/sell_request_test.go @@ -46,6 +46,7 @@ func TestSellRequestMinAmtRoundtrip(t *testing.T) { fn.Some(lnwire.MilliSatoshi(1000)), fn.None[rfqmath.BigIntFixedPoint](), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -55,7 +56,9 @@ func TestSellRequestMinAmtRoundtrip(t *testing.T) { decoded.PaymentMinAmt.WhenSome(func(v lnwire.MilliSatoshi) { require.Equal(t, lnwire.MilliSatoshi(1000), v) }) - require.Equal(t, lnwire.MilliSatoshi(5000), decoded.PaymentMaxAmt) + require.Equal( + t, lnwire.MilliSatoshi(5000), decoded.PaymentMaxAmt, + ) } // TestSellRequestRateLimitRoundtrip verifies that AssetRateLimit @@ -71,6 +74,7 @@ func TestSellRequestRateLimitRoundtrip(t *testing.T) { peer, spec, lnwire.MilliSatoshi(10000), fn.None[lnwire.MilliSatoshi](), fn.Some(limit), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -97,6 +101,7 @@ func TestSellRequestNoOptionalFieldsRoundtrip(t *testing.T) { fn.None[lnwire.MilliSatoshi](), fn.None[rfqmath.BigIntFixedPoint](), fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -126,6 +131,7 @@ func TestSellRequestAllFieldsRoundtrip(t *testing.T) { peer, spec, lnwire.MilliSatoshi(9000), fn.Some(lnwire.MilliSatoshi(2000)), fn.Some(limit), fn.Some(rateHint), "sell-meta", + fn.None[ExecutionPolicy](), ) require.NoError(t, err) @@ -205,3 +211,74 @@ func TestSellRequestValidateZeroRateLimit(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "must be positive") } + +// TestSellRequestExecutionPolicyRoundtrip verifies that a FOK +// execution policy survives a wire roundtrip. +func TestSellRequestExecutionPolicyRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x05} + spec := asset.NewSpecifierFromId(asset.ID{0xEE}) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(7000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + fn.Some(ExecutionPolicyFOK), + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.ExecutionPolicy.IsSome()) + decoded.ExecutionPolicy.WhenSome( + func(v ExecutionPolicy) { + require.Equal(t, ExecutionPolicyFOK, v) + }, + ) +} + +// TestSellRequestNoExecutionPolicyRoundtrip verifies that an +// absent execution policy stays None after a wire roundtrip. +func TestSellRequestNoExecutionPolicyRoundtrip(t *testing.T) { + t.Parallel() + + peer := route.Vertex{0x06} + spec := asset.NewSpecifierFromId(asset.ID{0xFF}) + + req, err := NewSellRequest( + peer, spec, lnwire.MilliSatoshi(8000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[AssetRate](), "", + fn.None[ExecutionPolicy](), + ) + require.NoError(t, err) + + decoded := sellRequestRoundtrip(t, req) + + require.True(t, decoded.ExecutionPolicy.IsNone()) +} + +// TestSellRequestInvalidExecutionPolicy ensures Validate rejects +// an unknown execution policy value. +func TestSellRequestInvalidExecutionPolicy(t *testing.T) { + t.Parallel() + + req := &SellRequest{ + Version: V1, + AssetSpecifier: asset.NewSpecifierFromId(asset.ID{1}), + PaymentMaxAmt: lnwire.MilliSatoshi(1000), + PaymentMinAmt: fn.None[lnwire.MilliSatoshi](), + AssetRateLimit: fn.None[rfqmath.BigIntFixedPoint](), + AssetRateHint: fn.None[AssetRate](), + ExecutionPolicy: fn.Some( + ExecutionPolicy(2), + ), + } + + err := req.Validate() + require.Error(t, err) + require.Contains(t, err.Error(), "execution policy") +} From 3e46b47953fefecb85304af65d89fa13f27a5a42 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:32 +0800 Subject: [PATCH 16/21] rfq: add TestCheckFOK for FOK enforcement Add table-driven test exercising checkFOK across buy/sell with no policy, IOC, FOK viable, and FOK rounds-to-zero cases. Update all existing NewBuyRequest/NewSellRequest calls with the new execution policy parameter. --- rfq/portfolio_pilot_test.go | 135 ++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/rfq/portfolio_pilot_test.go b/rfq/portfolio_pilot_test.go index 7956f1c3f..2517b25fc 100644 --- a/rfq/portfolio_pilot_test.go +++ b/rfq/portfolio_pilot_test.go @@ -127,6 +127,7 @@ func TestResolveRequest(t *testing.T) { fn.None[uint64](), fn.None[rfqmath.BigIntFixedPoint](), rateHint, "order-metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) return req @@ -146,6 +147,7 @@ func TestResolveRequest(t *testing.T) { fn.None[lnwire.MilliSatoshi](), fn.None[rfqmath.BigIntFixedPoint](), rateHint, "order-metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) return req @@ -555,6 +557,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -585,6 +588,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -612,6 +616,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -644,6 +649,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -672,6 +678,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -699,6 +706,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -729,6 +737,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -760,6 +769,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -788,6 +798,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -816,6 +827,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -853,6 +865,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.Some(limit), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -886,6 +899,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.Some(limit), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -919,6 +933,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.Some(limit), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -953,6 +968,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.Some(limit), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -985,6 +1001,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.Some(limit), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -1023,6 +1040,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -1060,6 +1078,7 @@ func TestVerifyAcceptQuote(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -1139,6 +1158,7 @@ func TestResolveRequestWithoutPriceOracleRejects(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -1180,6 +1200,7 @@ func TestVerifyAcceptQuoteWithoutPriceOracle(t *testing.T) { fn.None[rfqmath.BigIntFixedPoint](), fn.None[rfqmsg.AssetRate](), "metadata", + fn.None[rfqmsg.ExecutionPolicy](), ) require.NoError(t, err) @@ -1407,3 +1428,117 @@ func TestCheckMinFill(t *testing.T) { }) } } + +// TestCheckFOK exercises the checkFOK helper directly. +func TestCheckFOK(t *testing.T) { + t.Parallel() + + spec := asset.NewSpecifierFromId(asset.ID{0x01}) + + // Normal rate: 100 units/BTC. + normalRate := rfqmath.NewBigIntFixedPoint(100, 0) + + // Huge rate: 1e12 units/BTC. Max of 1 unit converts + // to ~0 msat. + hugeRate := rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ) + + tests := []struct { + name string + req rfqmsg.Request + rate rfqmath.BigIntFixedPoint + expect QuoteRespStatus + }{ + { + name: "buy: no policy", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: IOC policy", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + ExecutionPolicy: fn.Some( + rfqmsg.ExecutionPolicyIOC, + ), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: FOK viable", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 100, + ExecutionPolicy: fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "buy: FOK rounds to zero", + req: &rfqmsg.BuyRequest{ + AssetSpecifier: spec, + AssetMaxAmt: 1, + ExecutionPolicy: fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + }, + rate: hugeRate, + expect: FOKNotViableQuoteRespStatus, + }, + { + name: "sell: no policy", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: 1000, + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: FOK viable", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: lnwire.MilliSatoshi( + 50_000_000_000, + ), + ExecutionPolicy: fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + }, + rate: normalRate, + expect: ValidAcceptQuoteRespStatus, + }, + { + name: "sell: FOK rounds to zero", + req: &rfqmsg.SellRequest{ + AssetSpecifier: spec, + PaymentMaxAmt: lnwire.MilliSatoshi(1), + ExecutionPolicy: fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + }, + // 1 unit/BTC: 1 msat = 1e-11 BTC * 1 = 0 units. + rate: rfqmath.NewBigIntFixedPoint(1, 0), + expect: FOKNotViableQuoteRespStatus, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + status := checkFOK(tc.req, tc.rate) + require.Equal(t, tc.expect, status) + }) + } +} From 8eeaaff9c6a0cf33d613d3c63674e50c5c6a883c Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:40:48 +0800 Subject: [PATCH 17/21] itest: add execution policy integration tests Add FOK sub-tests to testRfqLimitConstraints: buy FOK accepted, sell FOK accepted, buy FOK rejected (extreme rate), and IOC default accepted. Add FOK sell scenario to custom channels limit constraints test with a real asset channel payment. --- .../custom_channels/limit_constraints_test.go | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/itest/custom_channels/limit_constraints_test.go b/itest/custom_channels/limit_constraints_test.go index 6d7539ff6..e013fc79d 100644 --- a/itest/custom_channels/limit_constraints_test.go +++ b/itest/custom_channels/limit_constraints_test.go @@ -246,6 +246,56 @@ func testCustomChannelsLimitConstraints(_ context.Context, logBalance(t.t, nodes, assetID, "after payment") t.Logf("Payment completed: %d asset units sent", numUnits) + // ----------------------------------------------------------------- + // Negotiate a sell order from Charlie with FOK policy. + // Rate limit ceiling is generous and the payment max is + // large enough that FOK conversion yields non-zero units. + // ----------------------------------------------------------------- + t.Logf("Negotiating sell order with FOK policy...") + + sellRespFOK, err := asTapd(charlie).RfqClient.AddAssetSellOrder( + ctxb, &rfqrpc.AddAssetSellOrderRequest{ + AssetSpecifier: &rfqrpc.AssetSpecifier{ + Id: &rfqrpc.AssetSpecifier_AssetId{ + AssetId: assetID, + }, + }, + PaymentMaxAmt: 180_000_000, + AssetRateLimit: &rfqrpc.FixedPoint{ + Coefficient: "100000000000000", + Scale: 2, + }, + ExecutionPolicy: rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK, + Expiry: uint64(inOneHour.Unix()), + PeerPubKey: dave.PubKey[:], + TimeoutSeconds: 10, + }, + ) + require.NoError(t.t, err, "sell order with FOK policy") + + acceptedFOK := sellRespFOK.GetAcceptedQuote() + require.NotNil( + t.t, acceptedFOK, "expected accepted FOK sell quote", + ) + t.Logf("FOK sell quote accepted: scid=%d", acceptedFOK.Scid) + + // Pay using the FOK quote. + invoiceRespFOK := createAssetInvoice( + t.t, dave, erin, invoiceAssetAmount, assetID, + ) + + var quoteIDFOK rfqmsg.ID + copy(quoteIDFOK[:], acceptedFOK.Id) + + numUnitsFOK, _ := payInvoiceWithAssets( + t.t, charlie, dave, invoiceRespFOK.PaymentRequest, + assetID, withRFQ(quoteIDFOK), + ) + require.Greater(t.t, numUnitsFOK, uint64(0)) + + logBalance(t.t, nodes, assetID, "after FOK payment") + t.Logf("FOK payment completed: %d units sent", numUnitsFOK) + // Close channels. closeAssetChannelAndAssert( t, net, charlie, dave, chanPointCD, From 0b222f62dd0cfa464c007ef6e5137e7df5c703a0 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Wed, 25 Mar 2026 16:57:00 +0800 Subject: [PATCH 18/21] rfqmsg+rfq+rpcserver: address review findings for execution policy Add FOK_NOT_VIABLE (7) to QuoteRespStatus enum in both rfqrpc and portfoliopilotrpc protos. Add mapping case in rpcUnmarshalQuoteRespStatus. Extract unmarshalExecutionPolicy helper that rejects unknown enum values instead of silently treating them as IOC. Add FOK cases to TestVerifyAcceptQuote for pipeline coverage. Annotate FOKNotViableRejectCode and ErrFOKNotViable as reserved for workstream D. --- rfq/portfolio_pilot_rpc.go | 2 + rfq/portfolio_pilot_test.go | 157 ++++++++++++++++++ rfqmsg/reject.go | 6 + rpcserver/rpcserver.go | 42 +++-- .../portfoliopilotrpc/portfolio_pilot.pb.go | 60 ++++--- .../portfoliopilotrpc/portfolio_pilot.proto | 4 + .../portfolio_pilot.swagger.json | 5 +- taprpc/rfqrpc/rfq.pb.go | 113 +++++++------ taprpc/rfqrpc/rfq.proto | 12 ++ taprpc/rfqrpc/rfq.swagger.json | 7 +- 10 files changed, 320 insertions(+), 88 deletions(-) diff --git a/rfq/portfolio_pilot_rpc.go b/rfq/portfolio_pilot_rpc.go index 476bd3373..5d13968ef 100644 --- a/rfq/portfolio_pilot_rpc.go +++ b/rfq/portfolio_pilot_rpc.go @@ -608,6 +608,8 @@ func rpcUnmarshalQuoteRespStatus( return MinFillNotMetQuoteRespStatus, nil case pilotrpc.QuoteRespStatus_RATE_BOUND_MISS: return RateBoundMissQuoteRespStatus, nil + case pilotrpc.QuoteRespStatus_FOK_NOT_VIABLE: + return FOKNotViableQuoteRespStatus, nil default: return 0, fmt.Errorf("unknown quote response status: %v", status) diff --git a/rfq/portfolio_pilot_test.go b/rfq/portfolio_pilot_test.go index 2517b25fc..3b6182857 100644 --- a/rfq/portfolio_pilot_test.go +++ b/rfq/portfolio_pilot_test.go @@ -1098,6 +1098,163 @@ func TestVerifyAcceptQuote(t *testing.T) { expectStatus: ValidAcceptQuoteRespStatus, expectErr: false, }, + + // --- FOK enforcement cases --- + + { + name: "buy accept: FOK viable", + makeAccept: func(t *testing.T) rfqmsg.Accept { + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 100, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRateMatch, + }, nil, + ) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, + { + name: "buy accept: FOK not viable", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Rate is huge: 1 unit converts + // to ~0 msat. + hugeRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ), + validExpiryFuture, + ) + buyReq, err := rfqmsg.NewBuyRequest( + peerID, assetSpec, 1, + fn.None[uint64](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + ) + require.NoError(t, err) + + return &rfqmsg.BuyAccept{ + Peer: peerID, + Request: *buyReq, + AssetRate: hugeRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + oracleRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1_000_000_000_000, 0, + ), + validExpiryFuture, + ) + expectQueryBuyPrice( + p, &OracleResponse{ + AssetRate: oracleRate, + }, nil, + ) + }, + expectStatus: FOKNotViableQuoteRespStatus, + expectErr: false, + }, + { + name: "sell accept: FOK viable", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // 50B msat at 100 units/BTC = + // 5 units (non-zero). + sellReq, err := rfqmsg.NewSellRequest( + peerID, assetSpec, + lnwire.MilliSatoshi(50_000_000_000), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + ) + require.NoError(t, err) + + return &rfqmsg.SellAccept{ + Peer: peerID, + Request: *sellReq, + AssetRate: peerRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + resp := OracleResponse{ + AssetRate: oracleRateMatch, + } + expectQuerySellPrice(p, &resp, nil) + }, + expectStatus: ValidAcceptQuoteRespStatus, + expectErr: false, + }, + { + name: "sell accept: FOK not viable", + makeAccept: func(t *testing.T) rfqmsg.Accept { + // Low rate (1 unit/BTC): 1 msat + // converts to 0 units. + lowRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1, 0, + ), + validExpiryFuture, + ) + sellReq, err := rfqmsg.NewSellRequest( + peerID, assetSpec, + lnwire.MilliSatoshi(1), + fn.None[lnwire.MilliSatoshi](), + fn.None[rfqmath.BigIntFixedPoint](), + fn.None[rfqmsg.AssetRate](), + "metadata", + fn.Some( + rfqmsg.ExecutionPolicyFOK, + ), + ) + require.NoError(t, err) + + return &rfqmsg.SellAccept{ + Peer: peerID, + Request: *sellReq, + AssetRate: lowRate, + } + }, + setupOracle: func(p *MockPriceOracle) { + oracleRate := rfqmsg.NewAssetRate( + rfqmath.NewBigIntFixedPoint( + 1, 0, + ), + validExpiryFuture, + ) + resp := OracleResponse{ + AssetRate: oracleRate, + } + expectQuerySellPrice(p, &resp, nil) + }, + expectStatus: FOKNotViableQuoteRespStatus, + expectErr: false, + }, } for _, tc := range tests { diff --git a/rfqmsg/reject.go b/rfqmsg/reject.go index 56c455213..8de809bf1 100644 --- a/rfqmsg/reject.go +++ b/rfqmsg/reject.go @@ -100,6 +100,9 @@ const ( // FOKNotViableRejectCode indicates that the FOK execution // policy could not be satisfied at the accepted rate. + // + // NOTE: Currently unused. Reserved for workstream D where the + // responder may reject via wire Reject message. FOKNotViableRejectCode RejectCode = 4 ) @@ -134,6 +137,9 @@ var ( // ErrFOKNotViable is the error for when the FOK execution // policy cannot be satisfied at the accepted rate. + // + // NOTE: Currently unused. Reserved for workstream D where the + // responder may reject via wire Reject message. ErrFOKNotViable = RejectErr{ Code: FOKNotViableRejectCode, Msg: "FOK not viable at accepted rate", diff --git a/rpcserver/rpcserver.go b/rpcserver/rpcserver.go index 2005159da..b7176db2e 100644 --- a/rpcserver/rpcserver.go +++ b/rpcserver/rpcserver.go @@ -8354,6 +8354,26 @@ func unmarshalOptionalFixedPoint( return fn.Some(result), nil } +// unmarshalExecutionPolicy converts the RPC execution policy enum to +// the internal optional type. IOC (0/default) maps to fn.None. FOK +// maps to fn.Some(ExecutionPolicyFOK). Unknown values are rejected. +func unmarshalExecutionPolicy( + p rfqrpc.ExecutionPolicy, +) (fn.Option[rfqmsg.ExecutionPolicy], error) { + + switch p { + case rfqrpc.ExecutionPolicy_EXECUTION_POLICY_IOC: + return fn.None[rfqmsg.ExecutionPolicy](), nil + + case rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK: + return fn.Some(rfqmsg.ExecutionPolicyFOK), nil + + default: + return fn.None[rfqmsg.ExecutionPolicy](), + fmt.Errorf("unknown execution policy: %v", p) + } +} + func unmarshalAssetBuyOrder( req *rfqrpc.AddAssetBuyOrderRequest) (*rfq.BuyOrder, error) { @@ -8414,11 +8434,12 @@ func unmarshalAssetBuyOrder( "limit: %w", err) } - // Unmarshal optional execution policy. Only FOK is - // explicitly set; IOC (0) is the default. - var execPolicy fn.Option[rfqmsg.ExecutionPolicy] - if req.ExecutionPolicy == rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK { - execPolicy = fn.Some(rfqmsg.ExecutionPolicyFOK) + // Unmarshal optional execution policy. + execPolicy, err := unmarshalExecutionPolicy( + req.ExecutionPolicy, + ) + if err != nil { + return nil, err } return &rfq.BuyOrder{ @@ -8657,11 +8678,12 @@ func unmarshalAssetSellOrder( "limit: %w", err) } - // Unmarshal optional execution policy. Only FOK is - // explicitly set; IOC (0) is the default. - var execPolicy fn.Option[rfqmsg.ExecutionPolicy] - if req.ExecutionPolicy == rfqrpc.ExecutionPolicy_EXECUTION_POLICY_FOK { - execPolicy = fn.Some(rfqmsg.ExecutionPolicyFOK) + // Unmarshal optional execution policy. + execPolicy, err := unmarshalExecutionPolicy( + req.ExecutionPolicy, + ) + if err != nil { + return nil, err } return &rfq.SellOrder{ diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go b/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go index 7d12ffa36..1de1b67b8 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.pb.go @@ -263,6 +263,9 @@ const ( // RATE_BOUND_MISS indicates that the accepted rate violated the // requester's rate limit constraint. QuoteRespStatus_RATE_BOUND_MISS QuoteRespStatus = 6 + // FOK_NOT_VIABLE indicates that the FOK execution policy could + // not be satisfied at the accepted rate. + QuoteRespStatus_FOK_NOT_VIABLE QuoteRespStatus = 7 ) // Enum value maps for QuoteRespStatus. @@ -275,6 +278,7 @@ var ( 4: "VALID_ACCEPT_QUOTE", 5: "MIN_FILL_NOT_MET", 6: "RATE_BOUND_MISS", + 7: "FOK_NOT_VIABLE", } QuoteRespStatus_value = map[string]int32{ "INVALID_ASSET_RATES": 0, @@ -284,6 +288,7 @@ var ( "VALID_ACCEPT_QUOTE": 4, "MIN_FILL_NOT_MET": 5, "RATE_BOUND_MISS": 6, + "FOK_NOT_VIABLE": 7, } ) @@ -1579,7 +1584,7 @@ var file_portfoliopilotrpc_portfolio_pilot_proto_rawDesc = []byte{ 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x49, 0x4e, 0x5f, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x49, 0x43, 0x45, 0x5f, - 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x10, 0x03, 0x2a, 0xb6, 0x01, 0x0a, + 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x10, 0x03, 0x2a, 0xca, 0x01, 0x0a, 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, @@ -1591,33 +1596,34 @@ var file_portfoliopilotrpc_portfolio_pilot_proto_rawDesc = []byte{ 0x50, 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x49, 0x4e, 0x5f, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x10, 0x05, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, - 0x49, 0x53, 0x53, 0x10, 0x06, 0x32, 0xd1, 0x02, 0x0a, 0x0e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x65, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, - 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x2e, 0x70, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x6e, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, - 0x75, 0x6f, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, - 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, - 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, - 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x68, 0x0a, 0x0f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, - 0x65, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, - 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, + 0x49, 0x53, 0x53, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x4f, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, + 0x5f, 0x56, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x32, 0xd1, 0x02, 0x0a, 0x0e, 0x50, 0x6f, + 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x50, 0x69, 0x6c, 0x6f, 0x74, 0x12, 0x65, 0x0a, 0x0e, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, + 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x6e, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x2b, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, + 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, + 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x0f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, + 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x2a, 0x2e, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, + 0x6f, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a, + 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, + 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x61, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, - 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x70, 0x69, 0x6c, 0x6f, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.proto b/taprpc/portfoliopilotrpc/portfolio_pilot.proto index c875aa17b..4d91c7fe2 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.proto +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.proto @@ -146,6 +146,10 @@ enum QuoteRespStatus { // RATE_BOUND_MISS indicates that the accepted rate violated the // requester's rate limit constraint. RATE_BOUND_MISS = 6; + + // FOK_NOT_VIABLE indicates that the FOK execution policy could + // not be satisfied at the accepted rate. + FOK_NOT_VIABLE = 7; } // RejectErr captures a rejection reason for a quote request. diff --git a/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json b/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json index 99c6e8f96..a2cc484f4 100644 --- a/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json +++ b/taprpc/portfoliopilotrpc/portfolio_pilot.swagger.json @@ -322,10 +322,11 @@ "PORTFOLIO_PILOT_ERR", "VALID_ACCEPT_QUOTE", "MIN_FILL_NOT_MET", - "RATE_BOUND_MISS" + "RATE_BOUND_MISS", + "FOK_NOT_VIABLE" ], "default": "INVALID_ASSET_RATES", - "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully.\n - MIN_FILL_NOT_MET: MIN_FILL_NOT_MET indicates that the minimum fill constraint was\nnot satisfiable at the accepted rate.\n - RATE_BOUND_MISS: RATE_BOUND_MISS indicates that the accepted rate violated the\nrequester's rate limit constraint." + "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully.\n - MIN_FILL_NOT_MET: MIN_FILL_NOT_MET indicates that the minimum fill constraint was\nnot satisfiable at the accepted rate.\n - RATE_BOUND_MISS: RATE_BOUND_MISS indicates that the accepted rate violated the\nrequester's rate limit constraint.\n - FOK_NOT_VIABLE: FOK_NOT_VIABLE indicates that the FOK execution policy could\nnot be satisfied at the accepted rate." }, "portfoliopilotrpcRejectCode": { "type": "string", diff --git a/taprpc/rfqrpc/rfq.pb.go b/taprpc/rfqrpc/rfq.pb.go index c49677437..20da0d8cc 100644 --- a/taprpc/rfqrpc/rfq.pb.go +++ b/taprpc/rfqrpc/rfq.pb.go @@ -90,6 +90,15 @@ const ( // VALID_ACCEPT_QUOTE indicates that the accepted quote passed all // validation checks successfully. QuoteRespStatus_VALID_ACCEPT_QUOTE QuoteRespStatus = 4 + // MIN_FILL_NOT_MET indicates that the minimum fill constraint was + // not satisfiable at the accepted rate. + QuoteRespStatus_MIN_FILL_NOT_MET QuoteRespStatus = 5 + // RATE_BOUND_MISS indicates that the accepted rate violated the + // requester's rate limit constraint. + QuoteRespStatus_RATE_BOUND_MISS QuoteRespStatus = 6 + // FOK_NOT_VIABLE indicates that the FOK execution policy could + // not be satisfied at the accepted rate. + QuoteRespStatus_FOK_NOT_VIABLE QuoteRespStatus = 7 ) // Enum value maps for QuoteRespStatus. @@ -100,6 +109,9 @@ var ( 2: "PRICE_ORACLE_QUERY_ERR", 3: "PORTFOLIO_PILOT_ERR", 4: "VALID_ACCEPT_QUOTE", + 5: "MIN_FILL_NOT_MET", + 6: "RATE_BOUND_MISS", + 7: "FOK_NOT_VIABLE", } QuoteRespStatus_value = map[string]int32{ "INVALID_ASSET_RATES": 0, @@ -107,6 +119,9 @@ var ( "PRICE_ORACLE_QUERY_ERR": 2, "PORTFOLIO_PILOT_ERR": 3, "VALID_ACCEPT_QUOTE": 4, + "MIN_FILL_NOT_MET": 5, + "RATE_BOUND_MISS": 6, + "FOK_NOT_VIABLE": 7, } ) @@ -2578,7 +2593,7 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x49, 0x4f, 0x43, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, - 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x46, 0x4f, 0x4b, 0x10, 0x01, 0x2a, 0x8b, 0x01, 0x0a, 0x0f, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x46, 0x4f, 0x4b, 0x10, 0x01, 0x2a, 0xca, 0x01, 0x0a, 0x0f, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x52, 0x41, 0x54, 0x45, 0x53, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, 0x41, @@ -2587,56 +2602,60 @@ var file_rfqrpc_rfq_proto_rawDesc = []byte{ 0x52, 0x59, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x4f, 0x52, 0x54, 0x46, 0x4f, 0x4c, 0x49, 0x4f, 0x5f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, - 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, 0x71, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x46, - 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x41, - 0x4c, 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, - 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, 0x45, - 0x10, 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1f, + 0x54, 0x5f, 0x51, 0x55, 0x4f, 0x54, 0x45, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x49, 0x4e, + 0x5f, 0x46, 0x49, 0x4c, 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x4d, 0x45, 0x54, 0x10, 0x05, 0x12, + 0x13, 0x0a, 0x0f, 0x52, 0x41, 0x54, 0x45, 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x4d, 0x49, + 0x53, 0x53, 0x10, 0x06, 0x12, 0x12, 0x0a, 0x0e, 0x46, 0x4f, 0x4b, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, + 0x56, 0x49, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x07, 0x2a, 0x47, 0x0a, 0x0d, 0x52, 0x66, 0x71, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x46, 0x51, + 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x41, 0x4c, + 0x45, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x46, 0x51, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, + 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x55, 0x52, 0x43, 0x48, 0x41, 0x53, 0x45, 0x10, + 0x01, 0x32, 0x82, 0x05, 0x0a, 0x03, 0x52, 0x66, 0x71, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, + 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, + 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, + 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, + 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, + 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x42, 0x75, 0x79, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x41, - 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, - 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, + 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x65, 0x6c, 0x6c, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, 0x66, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, 0x66, - 0x66, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x66, 0x71, - 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x75, 0x79, 0x4f, - 0x66, 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, + 0x66, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x17, 0x51, + 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, + 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, - 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, - 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x27, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, - 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, - 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, 0x72, - 0x70, 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, 0x0a, - 0x11, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, - 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, - 0x61, 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, + 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x65, 0x65, + 0x72, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x16, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, + 0x73, 0x12, 0x25, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x74, 0x66, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, + 0x63, 0x2e, 0x52, 0x66, 0x71, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x58, 0x0a, 0x11, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x12, 0x20, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x6f, 0x72, + 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, + 0x62, 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x2f, 0x74, 0x61, 0x70, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x66, 0x71, 0x72, 0x70, 0x63, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/taprpc/rfqrpc/rfq.proto b/taprpc/rfqrpc/rfq.proto index 7ad4b995e..67b50d7de 100644 --- a/taprpc/rfqrpc/rfq.proto +++ b/taprpc/rfqrpc/rfq.proto @@ -406,6 +406,18 @@ enum QuoteRespStatus { // VALID_ACCEPT_QUOTE indicates that the accepted quote passed all // validation checks successfully. VALID_ACCEPT_QUOTE = 4; + + // MIN_FILL_NOT_MET indicates that the minimum fill constraint was + // not satisfiable at the accepted rate. + MIN_FILL_NOT_MET = 5; + + // RATE_BOUND_MISS indicates that the accepted rate violated the + // requester's rate limit constraint. + RATE_BOUND_MISS = 6; + + // FOK_NOT_VIABLE indicates that the FOK execution policy could + // not be satisfied at the accepted rate. + FOK_NOT_VIABLE = 7; } // InvalidQuoteResponse is a message that is returned when a quote response is diff --git a/taprpc/rfqrpc/rfq.swagger.json b/taprpc/rfqrpc/rfq.swagger.json index 6cb432c8c..377e714fd 100644 --- a/taprpc/rfqrpc/rfq.swagger.json +++ b/taprpc/rfqrpc/rfq.swagger.json @@ -1080,10 +1080,13 @@ "INVALID_EXPIRY", "PRICE_ORACLE_QUERY_ERR", "PORTFOLIO_PILOT_ERR", - "VALID_ACCEPT_QUOTE" + "VALID_ACCEPT_QUOTE", + "MIN_FILL_NOT_MET", + "RATE_BOUND_MISS", + "FOK_NOT_VIABLE" ], "default": "INVALID_ASSET_RATES", - "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully." + "description": "QuoteRespStatus is an enum that represents the status of a quote response.\n\n - INVALID_ASSET_RATES: INVALID_ASSET_RATES indicates that at least one asset rate in the\nquote response is invalid.\n - INVALID_EXPIRY: INVALID_EXPIRY indicates that the expiry in the quote response is\ninvalid.\n - PRICE_ORACLE_QUERY_ERR: PRICE_ORACLE_QUERY_ERR indicates that an error occurred when querying the\nprice oracle whilst evaluating the quote response.\n - PORTFOLIO_PILOT_ERR: PORTFOLIO_PILOT_ERR indicates that an unexpected error occurred in the\nportfolio pilot while evaluating the quote response.\n - VALID_ACCEPT_QUOTE: VALID_ACCEPT_QUOTE indicates that the accepted quote passed all\nvalidation checks successfully.\n - MIN_FILL_NOT_MET: MIN_FILL_NOT_MET indicates that the minimum fill constraint was\nnot satisfiable at the accepted rate.\n - RATE_BOUND_MISS: RATE_BOUND_MISS indicates that the accepted rate violated the\nrequester's rate limit constraint.\n - FOK_NOT_VIABLE: FOK_NOT_VIABLE indicates that the FOK execution policy could\nnot be satisfied at the accepted rate." }, "rfqrpcRejectedQuoteResponse": { "type": "object", From ba96c91879dfac9143f5e68cb7c81f3c1f79a06e Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 3 Apr 2026 21:53:35 +0800 Subject: [PATCH 19/21] rfqmsg+itest: fix execution-policy build errors Add missing execPolicy argument to NewBuyRequest/NewSellRequest calls in request_property_test.go. Replace createAssetInvoice with regular BTC invoice in the FOK sell test, since Erin has no asset channel. --- .../custom_channels/limit_constraints_test.go | 12 ++++++++---- rfqmsg/request_property_test.go | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/itest/custom_channels/limit_constraints_test.go b/itest/custom_channels/limit_constraints_test.go index e013fc79d..03ad35c52 100644 --- a/itest/custom_channels/limit_constraints_test.go +++ b/itest/custom_channels/limit_constraints_test.go @@ -279,16 +279,20 @@ func testCustomChannelsLimitConstraints(_ context.Context, ) t.Logf("FOK sell quote accepted: scid=%d", acceptedFOK.Scid) - // Pay using the FOK quote. - invoiceRespFOK := createAssetInvoice( - t.t, dave, erin, invoiceAssetAmount, assetID, + // Pay using the FOK quote with a regular BTC invoice. + invoiceRespFOK, err := erin.LightningClient.AddInvoice( + ctxb, &lnrpc.Invoice{ + ValueMsat: invoiceMsat, + }, ) + require.NoError(t.t, err) var quoteIDFOK rfqmsg.ID copy(quoteIDFOK[:], acceptedFOK.Id) numUnitsFOK, _ := payInvoiceWithAssets( - t.t, charlie, dave, invoiceRespFOK.PaymentRequest, + t.t, charlie, dave, + invoiceRespFOK.PaymentRequest, assetID, withRFQ(quoteIDFOK), ) require.Greater(t.t, numUnitsFOK, uint64(0)) diff --git a/rfqmsg/request_property_test.go b/rfqmsg/request_property_test.go index f8a53b6c3..17ab7bb2f 100644 --- a/rfqmsg/request_property_test.go +++ b/rfqmsg/request_property_test.go @@ -126,9 +126,11 @@ func TestBuyRequestWireRoundtripProperty(t *testing.T) { t, "rateLimit", ) + noPolicy := fn.None[ExecutionPolicy]() req, err := NewBuyRequest( peer, spec, maxAmt, minAmt, - rateLimit, fn.None[AssetRate](), "", + rateLimit, fn.None[AssetRate](), + "", noPolicy, ) require.NoError(t, err) @@ -179,10 +181,12 @@ func TestSellRequestWireRoundtripProperty(t *testing.T) { t, "rateLimit", ) + noPolicy := fn.None[ExecutionPolicy]() req, err := NewSellRequest( peer, spec, lnwire.MilliSatoshi(maxAmt), minAmt, - rateLimit, fn.None[AssetRate](), "", + rateLimit, fn.None[AssetRate](), + "", noPolicy, ) require.NoError(t, err) @@ -420,11 +424,13 @@ func TestRateBoundEnforcementProperty(t *testing.T) { Draw(t, "maxAmt") limit := fixedPointGen().Draw(t, "limit") + noExec := fn.None[ExecutionPolicy]() req, err := NewBuyRequest( peer, spec, maxAmt, fn.None[uint64](), fn.Some(limit), - fn.None[AssetRate](), "", + fn.None[AssetRate](), + "", noExec, ) require.NoError(t, err) @@ -473,12 +479,14 @@ func TestRateBoundEnforcementProperty(t *testing.T) { ).Draw(t, "maxAmt") limit := fixedPointGen().Draw(t, "limit") + noExec := fn.None[ExecutionPolicy]() req, err := NewSellRequest( peer, spec, lnwire.MilliSatoshi(maxAmt), fn.None[lnwire.MilliSatoshi](), fn.Some(limit), - fn.None[AssetRate](), "", + fn.None[AssetRate](), + "", noExec, ) require.NoError(t, err) @@ -544,9 +552,10 @@ func TestBuyRequestRoundtripWithHintProperty(t *testing.T) { fp := fixedPointGen().Draw(t, "hintRate") hint := fn.Some(NewAssetRate(fp, expiry)) + noPolicy := fn.None[ExecutionPolicy]() req, err := NewBuyRequest( peer, spec, maxAmt, minAmt, - rateLimit, hint, "", + rateLimit, hint, "", noPolicy, ) require.NoError(t, err) From 8b18f7c99100781ac9b5ff60a0a4331bc19904bb Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 3 Apr 2026 22:37:42 +0800 Subject: [PATCH 20/21] rfq+rfqmsg: use NewBigIntFixedPoint helper, treat wire 0 as None Use rfqmath.NewBigIntFixedPoint in checkFOK and checkMinFill instead of verbose struct literals. Treat zero values for min fill amounts as unset (fn.None) at the wire deserialization boundary, since 0 is semantically meaningless as a minimum. Update property test generators to start from 1, matching the wire roundtrip invariant. --- rfq/portfolio_pilot.go | 17 +++++------------ rfqmsg/buy_request.go | 4 +++- rfqmsg/request_property_test.go | 8 ++++++-- rfqmsg/sell_request.go | 8 +++++--- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/rfq/portfolio_pilot.go b/rfq/portfolio_pilot.go index ba1c02eb8..f1f4c53ac 100644 --- a/rfq/portfolio_pilot.go +++ b/rfq/portfolio_pilot.go @@ -520,12 +520,9 @@ func checkFOK(req rfqmsg.Request, return ValidAcceptQuoteRespStatus } - units := rfqmath.FixedPoint[rfqmath.BigInt]{ - Coefficient: rfqmath.NewBigIntFromUint64( - r.AssetMaxAmt, - ), - Scale: 0, - } + units := rfqmath.NewBigIntFixedPoint( + r.AssetMaxAmt, 0, + ) msat := rfqmath.UnitsToMilliSatoshi( units, acceptedRate, ) @@ -565,13 +562,9 @@ func checkMinFill(req rfqmsg.Request, notMet := fn.MapOptionZ( r.AssetMinAmt, func(minAmt uint64) bool { - coeff := rfqmath.NewBigIntFromUint64( - minAmt, + units := rfqmath.NewBigIntFixedPoint( + minAmt, 0, ) - units := rfqmath.FixedPoint[rfqmath.BigInt]{ - Coefficient: coeff, - Scale: 0, - } msat := rfqmath.UnitsToMilliSatoshi( units, acceptedRate, ) diff --git a/rfqmsg/buy_request.go b/rfqmsg/buy_request.go index 313825df7..2f9ced8db 100644 --- a/rfqmsg/buy_request.go +++ b/rfqmsg/buy_request.go @@ -186,7 +186,9 @@ func NewBuyRequestFromWire(wireMsg WireMessage, var assetMinAmt fn.Option[uint64] msgData.MinInAsset.WhenSome( func(r tlv.RecordT[tlv.TlvType23, uint64]) { - assetMinAmt = fn.Some(r.Val) + if r.Val > 0 { + assetMinAmt = fn.Some(r.Val) + } }, ) diff --git a/rfqmsg/request_property_test.go b/rfqmsg/request_property_test.go index 17ab7bb2f..a43b638a6 100644 --- a/rfqmsg/request_property_test.go +++ b/rfqmsg/request_property_test.go @@ -21,7 +21,9 @@ import ( func optionalUint64Gen(bound uint64) *rapid.Generator[fn.Option[uint64]] { return rapid.Custom(func(t *rapid.T) fn.Option[uint64] { if rapid.Bool().Draw(t, "present") { - v := rapid.Uint64Range(0, bound).Draw( + // Start from 1: zero is treated as unset + // on the wire. + v := rapid.Uint64Range(1, bound).Draw( t, "value", ) return fn.Some(v) @@ -38,7 +40,9 @@ func optionalMsatGen( return rapid.Custom( func(t *rapid.T) fn.Option[lnwire.MilliSatoshi] { if rapid.Bool().Draw(t, "present") { - v := rapid.Uint64Range(0, bound).Draw( + // Start from 1: zero is treated as + // unset on the wire. + v := rapid.Uint64Range(1, bound).Draw( t, "value", ) return fn.Some( diff --git a/rfqmsg/sell_request.go b/rfqmsg/sell_request.go index af31a0875..870a9dbcf 100644 --- a/rfqmsg/sell_request.go +++ b/rfqmsg/sell_request.go @@ -181,9 +181,11 @@ func NewSellRequestFromWire(wireMsg WireMessage, var paymentMinAmt fn.Option[lnwire.MilliSatoshi] msgData.MinOutAsset.WhenSome( func(r tlv.RecordT[tlv.TlvType25, uint64]) { - paymentMinAmt = fn.Some( - lnwire.MilliSatoshi(r.Val), - ) + if r.Val > 0 { + paymentMinAmt = fn.Some( + lnwire.MilliSatoshi(r.Val), + ) + } }, ) From b20fcee6313ac2e394225aa6af9a58e54ad322c9 Mon Sep 17 00:00:00 2001 From: Jared Tobin Date: Fri, 3 Apr 2026 22:37:45 +0800 Subject: [PATCH 21/21] docs: add release note --- docs/release-notes/release-notes-0.8.0.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/release-notes/release-notes-0.8.0.md b/docs/release-notes/release-notes-0.8.0.md index f03fc676d..ea9f076ef 100644 --- a/docs/release-notes/release-notes-0.8.0.md +++ b/docs/release-notes/release-notes-0.8.0.md @@ -111,6 +111,14 @@ New fields are optional and backward-compatible; constraint validation only activates when they are present. +- [Execution Policy](https://github.com/lightninglabs/taproot-assets/pull/2049): + RFQ buy and sell orders can now specify an execution policy: IOC + (Immediate-Or-Cancel, the default) allows partial fills above the + minimum, while FOK (Fill-Or-Kill) requires the full requested amount + or rejects the quote. FOK viability is checked in `VerifyAcceptQuote` + with a new `FOK_NOT_VIABLE` reject code. New fields are optional and + backward-compatible. + ## Functional Enhancements - [Wallet Backup/Restore](https://github.com/lightninglabs/taproot-assets/pull/1980): @@ -163,6 +171,12 @@ `PortfolioPilot.ResolveRequest` for constraint forwarding. Add `RATE_BOUND_MISS` and `MIN_FILL_NOT_MET` to `QuoteRespStatus`. +- [PR#2049](https://github.com/lightninglabs/taproot-assets/pull/2049): + Add `execution_policy` enum (`EXECUTION_POLICY_IOC`, + `EXECUTION_POLICY_FOK`) to `AddAssetBuyOrder` and `AddAssetSellOrder` + requests, and to `PortfolioPilot.ResolveRequest` for constraint + forwarding. Add `FOK_NOT_VIABLE` to `QuoteRespStatus`. + ## tapcli Additions - [Wallet Backup CLI](https://github.com/lightninglabs/taproot-assets/pull/1980): @@ -350,6 +364,9 @@ Add unit, property-based, and integration tests for limit-order constraint fields. +- [PR#2049](https://github.com/lightninglabs/taproot-assets/pull/2049): + Add unit, property-based, and integration tests for execution policy. + ## Database - [forwards table](https://github.com/lightninglabs/taproot-assets/pull/1921):