Merge pull request #278 from carlaKC/205-suggestions

liquidity: add swap suggestions endpoint
pull/287/head
Carla Kirk-Cohen 4 years ago committed by GitHub
commit d3ede8d9e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -170,3 +170,31 @@ func setParam(ctx *cli.Context) error {
return err
}
var suggestSwapCommand = cli.Command{
Name: "suggestswaps",
Usage: "show a list of suggested swaps",
Description: "Displays a list of suggested swaps that aim to obtain " +
"the liquidity balance as specified by the rules set in " +
"the liquidity manager.",
Action: suggestSwap,
}
func suggestSwap(ctx *cli.Context) error {
client, cleanup, err := getClient(ctx)
if err != nil {
return err
}
defer cleanup()
resp, err := client.SuggestSwaps(
context.Background(), &looprpc.SuggestSwapsRequest{},
)
if err != nil {
return err
}
printJSON(resp)
return nil
}

@ -110,7 +110,7 @@ func main() {
loopOutCommand, loopInCommand, termsCommand,
monitorCommand, quoteCommand, listAuthCommand,
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
setLiquidityParamCommand,
setLiquidityParamCommand, suggestSwapCommand,
}
err := app.Run(os.Args)

@ -0,0 +1,33 @@
package liquidity
import (
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnwire"
)
// balances summarizes the state of the balances of a channel. Channel reserve,
// fees and pending htlc balances are not included in these balances.
type balances struct {
// capacity is the total capacity of the channel.
capacity btcutil.Amount
// incoming is the remote balance of the channel.
incoming btcutil.Amount
// outgoing is the local balance of the channel.
outgoing btcutil.Amount
// channelID is the channel that has these balances.
channelID lnwire.ShortChannelID
}
// newBalances creates a balances struct from lndclient channel information.
func newBalances(info lndclient.ChannelInfo) *balances {
return &balances{
capacity: info.Capacity,
incoming: info.RemoteBalance,
outgoing: info.LocalBalance,
channelID: lnwire.NewShortChanIDFromInt(info.ChannelID),
}
}

@ -9,6 +9,7 @@ import (
"strings"
"sync"
"github.com/lightninglabs/lndclient"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -23,6 +24,9 @@ type Config struct {
// LoopOutRestrictions returns the restrictions that the server applies
// to loop out swaps.
LoopOutRestrictions func(ctx context.Context) (*Restrictions, error)
// Lnd provides us with access to lnd's main rpc.
Lnd lndclient.LightningClient
}
// Parameters is a set of parameters provided by the user which guide
@ -131,3 +135,51 @@ func cloneParameters(params Parameters) Parameters {
return paramCopy
}
// SuggestSwaps returns a set of swap suggestions based on our current liquidity
// balance for the set of rules configured for the manager, failing if there are
// no rules set.
func (m *Manager) SuggestSwaps(ctx context.Context) (
[]*LoopOutRecommendation, error) {
m.paramsLock.Lock()
defer m.paramsLock.Unlock()
// If we have no rules set, exit early to avoid unnecessary calls to
// lnd and the server.
if len(m.params.ChannelRules) == 0 {
return nil, nil
}
channels, err := m.cfg.Lnd.ListChannels(ctx)
if err != nil {
return nil, err
}
// Get the current server side restrictions.
outRestrictions, err := m.cfg.LoopOutRestrictions(ctx)
if err != nil {
return nil, err
}
var suggestions []*LoopOutRecommendation
for _, channel := range channels {
channelID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
rule, ok := m.params.ChannelRules[channelID]
if !ok {
continue
}
balance := newBalances(channel)
suggestion := rule.suggestSwap(balance, outRestrictions)
// We can have nil suggestions in the case where no action is
// required, so only add non-nil suggestions.
if suggestion != nil {
suggestions = append(suggestions, suggestion)
}
}
return suggestions, nil
}

@ -4,6 +4,8 @@ import (
"context"
"testing"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/test"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
)
@ -16,6 +18,7 @@ func newTestConfig() *Config {
return NewRestrictions(1, 10000), nil
},
Lnd: test.NewMockLnd().Client,
}
}
@ -64,3 +67,89 @@ func TestParameters(t *testing.T) {
err = manager.SetParameters(expected)
require.Equal(t, ErrZeroChannelID, err)
}
// TestSuggestSwaps tests getting of swap suggestions.
func TestSuggestSwaps(t *testing.T) {
var (
chanID1 = lnwire.NewShortChanIDFromInt(1)
chanID2 = lnwire.NewShortChanIDFromInt(2)
)
tests := []struct {
name string
channels []lndclient.ChannelInfo
parameters Parameters
swaps []*LoopOutRecommendation
}{
{
name: "no rules",
channels: nil,
parameters: newParameters(),
},
{
name: "loop out",
channels: []lndclient.ChannelInfo{
{
ChannelID: 1,
Capacity: 1000,
LocalBalance: 1000,
RemoteBalance: 0,
},
},
parameters: Parameters{
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
chanID1: NewThresholdRule(
10, 10,
),
},
},
swaps: []*LoopOutRecommendation{
{
Channel: chanID1,
Amount: 500,
},
},
},
{
name: "no rule for channel",
channels: []lndclient.ChannelInfo{
{
ChannelID: 1,
Capacity: 1000,
LocalBalance: 0,
RemoteBalance: 1000,
},
},
parameters: Parameters{
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
chanID2: NewThresholdRule(10, 10),
},
},
swaps: nil,
},
}
for _, testCase := range tests {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
cfg := newTestConfig()
// Create a mock lnd with the set of channels set in our
// test case.
mock := test.NewMockLnd()
mock.Channels = testCase.channels
cfg.Lnd = mock.Client
manager := NewManager(cfg)
// Set our test case parameters.
err := manager.SetParameters(testCase.parameters)
require.NoError(t, err)
swaps, err := manager.SuggestSwaps(context.Background())
require.NoError(t, err)
require.Equal(t, testCase.swaps, swaps)
})
}
}

@ -0,0 +1,26 @@
package liquidity
import (
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwire"
)
// LoopOutRecommendation contains the information required to recommend a loop
// out.
type LoopOutRecommendation struct {
// Amount is the total amount to swap.
Amount btcutil.Amount
// Channel is the target outgoing channel.
Channel lnwire.ShortChannelID
}
// newLoopOutRecommendation creates a new loop out swap.
func newLoopOutRecommendation(amount btcutil.Amount,
channelID lnwire.ShortChannelID) *LoopOutRecommendation {
return &LoopOutRecommendation{
Amount: amount,
Channel: channelID,
}
}

@ -3,6 +3,8 @@ package liquidity
import (
"errors"
"fmt"
"github.com/btcsuite/btcutil"
)
var (
@ -59,3 +61,88 @@ func (r *ThresholdRule) validate() error {
return nil
}
// suggestSwap suggests a swap based on the liquidity thresholds configured,
// returning nil if no swap is recommended.
func (r *ThresholdRule) suggestSwap(channel *balances,
outRestrictions *Restrictions) *LoopOutRecommendation {
// Examine our total balance and required ratios to decide whether we
// need to swap.
amount := loopOutSwapAmount(
channel, r.MinimumIncoming, r.MinimumOutgoing,
)
// Limit our swap amount by the minimum/maximum thresholds set.
switch {
case amount < outRestrictions.Minimum:
return nil
case amount > outRestrictions.Maximum:
return newLoopOutRecommendation(
outRestrictions.Maximum, channel.channelID,
)
default:
return newLoopOutRecommendation(
amount, channel.channelID,
)
}
}
// loopOutSwapAmount determines whether we can perform a loop out swap, and
// returns the amount we need to swap to reach the desired liquidity balance
// specified by the incoming and outgoing thresholds.
func loopOutSwapAmount(balances *balances, incomingThresholdPercent,
outgoingThresholdPercent int) btcutil.Amount {
minimumIncoming := btcutil.Amount(uint64(
balances.capacity) *
uint64(incomingThresholdPercent) / 100,
)
minimumOutgoing := btcutil.Amount(
uint64(balances.capacity) *
uint64(outgoingThresholdPercent) / 100,
)
switch {
// If we have sufficient incoming capacity, we do not need to loop out.
case balances.incoming >= minimumIncoming:
return 0
// If we are already below the threshold set for outgoing capacity, we
// cannot take any further action.
case balances.outgoing <= minimumOutgoing:
return 0
}
// Express our minimum outgoing amount as a maximum incoming amount.
// We will use this value to limit the amount that we swap, so that we
// do not dip below our outgoing threshold.
maximumIncoming := balances.capacity - minimumOutgoing
// Calculate the midpoint between our minimum and maximum incoming
// values. We will aim to swap this amount so that we do not tip our
// outgoing balance beneath the desired level.
midpoint := (minimumIncoming + maximumIncoming) / 2
// Calculate the amount of incoming balance we need to shift to reach
// this desired midpoint.
required := midpoint - balances.incoming
// Since we can have pending htlcs on our channel, we check the amount
// of outbound capacity that we can shift before we fall below our
// threshold.
available := balances.outgoing - minimumOutgoing
// If we do not have enough balance available to reach our midpoint, we
// take no action. This is the case when we have a large portion of
// pending htlcs.
if available < required {
return 0
}
return required
}

@ -3,6 +3,7 @@ package liquidity
import (
"testing"
"github.com/btcsuite/btcutil"
"github.com/stretchr/testify/require"
)
@ -91,3 +92,164 @@ func TestValidateThreshold(t *testing.T) {
})
}
}
// TestLoopOutAmount tests assessing of a set of balances to determine whether
// we should perform a loop out.
func TestLoopOutAmount(t *testing.T) {
tests := []struct {
name string
minIncoming int
minOutgoing int
balances *balances
amt btcutil.Amount
}{
{
name: "insufficient surplus",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 20,
},
minOutgoing: 40,
minIncoming: 40,
amt: 0,
},
{
name: "loop out",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 80,
},
minOutgoing: 20,
minIncoming: 60,
amt: 50,
},
{
name: "pending htlcs",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 30,
},
minOutgoing: 20,
minIncoming: 60,
amt: 0,
},
{
name: "loop in",
balances: &balances{
capacity: 100,
incoming: 50,
outgoing: 50,
},
minOutgoing: 60,
minIncoming: 30,
amt: 0,
},
{
name: "liquidity ok",
balances: &balances{
capacity: 100,
incoming: 50,
outgoing: 50,
},
minOutgoing: 40,
minIncoming: 40,
amt: 0,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
amt := loopOutSwapAmount(
test.balances, test.minIncoming,
test.minOutgoing,
)
require.Equal(t, test.amt, amt)
})
}
}
// TestSuggestSwaps tests swap suggestions for the threshold rule. It does not
// many different values because we have separate tests for swap amount
// calculation.
func TestSuggestSwap(t *testing.T) {
tests := []struct {
name string
rule *ThresholdRule
channel *balances
outRestrictions *Restrictions
swap *LoopOutRecommendation
}{
{
name: "liquidity ok",
rule: NewThresholdRule(10, 10),
outRestrictions: NewRestrictions(10, 100),
channel: &balances{
capacity: 100,
incoming: 50,
outgoing: 50,
},
},
{
name: "loop out",
rule: NewThresholdRule(40, 40),
outRestrictions: NewRestrictions(10, 100),
channel: &balances{
capacity: 100,
incoming: 0,
outgoing: 100,
},
swap: &LoopOutRecommendation{Amount: 50},
},
{
name: "amount below minimum",
rule: NewThresholdRule(40, 40),
outRestrictions: NewRestrictions(200, 300),
channel: &balances{
capacity: 100,
incoming: 0,
outgoing: 100,
},
swap: nil,
},
{
name: "amount above maximum",
rule: NewThresholdRule(40, 40),
outRestrictions: NewRestrictions(10, 20),
channel: &balances{
capacity: 100,
incoming: 0,
outgoing: 100,
},
swap: &LoopOutRecommendation{Amount: 20},
},
{
name: "loop in",
rule: NewThresholdRule(10, 10),
outRestrictions: NewRestrictions(10, 100),
channel: &balances{
capacity: 100,
incoming: 100,
outgoing: 0,
},
swap: nil,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
swap := test.rule.suggestSwap(
test.channel, test.outRestrictions,
)
require.Equal(t, test.swap, swap)
})
}
}

@ -632,6 +632,32 @@ func rpcToRule(rule *looprpc.LiquidityRule) (*liquidity.ThresholdRule, error) {
}
// SuggestSwaps provides a list of suggested swaps based on lnd's current
// channel balances and rules set by the liquidity manager.
func (s *swapClientServer) SuggestSwaps(ctx context.Context,
_ *looprpc.SuggestSwapsRequest) (*looprpc.SuggestSwapsResponse, error) {
swaps, err := s.liquidityMgr.SuggestSwaps(ctx)
if err != nil {
return nil, err
}
var loopOut []*looprpc.LoopOutRequest
for _, swap := range swaps {
loopOut = append(loopOut, &looprpc.LoopOutRequest{
Amt: int64(swap.Amount),
OutgoingChanSet: []uint64{
swap.Channel.ToUint64(),
},
})
}
return &looprpc.SuggestSwapsResponse{
LoopOut: loopOut,
}, nil
}
// processStatusUpdates reads updates on the status channel and processes them.
//
// NOTE: This must run inside a goroutine as it blocks until the main context

@ -46,6 +46,7 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager {
outTerms.MinSwapAmount, outTerms.MaxSwapAmount,
), nil
},
Lnd: client.LndServices.Client,
}
return liquidity.NewManager(mngrCfg)

@ -1732,6 +1732,78 @@ func (m *SetLiquidityParamsResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_SetLiquidityParamsResponse proto.InternalMessageInfo
type SuggestSwapsRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SuggestSwapsRequest) Reset() { *m = SuggestSwapsRequest{} }
func (m *SuggestSwapsRequest) String() string { return proto.CompactTextString(m) }
func (*SuggestSwapsRequest) ProtoMessage() {}
func (*SuggestSwapsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_014de31d7ac8c57c, []int{22}
}
func (m *SuggestSwapsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SuggestSwapsRequest.Unmarshal(m, b)
}
func (m *SuggestSwapsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SuggestSwapsRequest.Marshal(b, m, deterministic)
}
func (m *SuggestSwapsRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SuggestSwapsRequest.Merge(m, src)
}
func (m *SuggestSwapsRequest) XXX_Size() int {
return xxx_messageInfo_SuggestSwapsRequest.Size(m)
}
func (m *SuggestSwapsRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SuggestSwapsRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SuggestSwapsRequest proto.InternalMessageInfo
type SuggestSwapsResponse struct {
//
//The set of recommended loop outs.
LoopOut []*LoopOutRequest `protobuf:"bytes,1,rep,name=loop_out,json=loopOut,proto3" json:"loop_out,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SuggestSwapsResponse) Reset() { *m = SuggestSwapsResponse{} }
func (m *SuggestSwapsResponse) String() string { return proto.CompactTextString(m) }
func (*SuggestSwapsResponse) ProtoMessage() {}
func (*SuggestSwapsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_014de31d7ac8c57c, []int{23}
}
func (m *SuggestSwapsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SuggestSwapsResponse.Unmarshal(m, b)
}
func (m *SuggestSwapsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SuggestSwapsResponse.Marshal(b, m, deterministic)
}
func (m *SuggestSwapsResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SuggestSwapsResponse.Merge(m, src)
}
func (m *SuggestSwapsResponse) XXX_Size() int {
return xxx_messageInfo_SuggestSwapsResponse.Size(m)
}
func (m *SuggestSwapsResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SuggestSwapsResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SuggestSwapsResponse proto.InternalMessageInfo
func (m *SuggestSwapsResponse) GetLoopOut() []*LoopOutRequest {
if m != nil {
return m.LoopOut
}
return nil
}
func init() {
proto.RegisterEnum("looprpc.SwapType", SwapType_name, SwapType_value)
proto.RegisterEnum("looprpc.SwapState", SwapState_name, SwapState_value)
@ -1759,142 +1831,148 @@ func init() {
proto.RegisterType((*LiquidityRule)(nil), "looprpc.LiquidityRule")
proto.RegisterType((*SetLiquidityParamsRequest)(nil), "looprpc.SetLiquidityParamsRequest")
proto.RegisterType((*SetLiquidityParamsResponse)(nil), "looprpc.SetLiquidityParamsResponse")
proto.RegisterType((*SuggestSwapsRequest)(nil), "looprpc.SuggestSwapsRequest")
proto.RegisterType((*SuggestSwapsResponse)(nil), "looprpc.SuggestSwapsResponse")
}
func init() { proto.RegisterFile("client.proto", fileDescriptor_014de31d7ac8c57c) }
var fileDescriptor_014de31d7ac8c57c = []byte{
// 2073 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xdd, 0x6e, 0x23, 0x49,
0x15, 0x1e, 0xff, 0xc5, 0xf6, 0x71, 0xbb, 0xdd, 0xa9, 0xcc, 0x26, 0x8e, 0x37, 0x68, 0x32, 0x3d,
0x3b, 0x90, 0x0d, 0xbb, 0x63, 0x36, 0x7b, 0xc5, 0x68, 0x41, 0xf2, 0x38, 0xce, 0xc6, 0x43, 0x62,
0x9b, 0xb6, 0x33, 0xab, 0x41, 0x48, 0xad, 0x8a, 0x5d, 0x89, 0x5b, 0xf4, 0xdf, 0x74, 0x97, 0x67,
0x12, 0xad, 0x00, 0x89, 0x17, 0xd8, 0x0b, 0xde, 0x80, 0x67, 0xe0, 0x0e, 0x1e, 0x81, 0x2b, 0x78,
0x05, 0x24, 0xc4, 0x05, 0x77, 0x3c, 0x00, 0xaa, 0x53, 0xdd, 0xed, 0x6e, 0xc7, 0x09, 0xe2, 0x82,
0xbb, 0xf4, 0x77, 0xbe, 0x3a, 0x55, 0xe7, 0xff, 0x38, 0xa0, 0x4c, 0x6d, 0x8b, 0xb9, 0xfc, 0x85,
0x1f, 0x78, 0xdc, 0x23, 0x65, 0xdb, 0xf3, 0xfc, 0xc0, 0x9f, 0xb6, 0xf6, 0xae, 0x3d, 0xef, 0xda,
0x66, 0x6d, 0xea, 0x5b, 0x6d, 0xea, 0xba, 0x1e, 0xa7, 0xdc, 0xf2, 0xdc, 0x50, 0xd2, 0xf4, 0xef,
0x8a, 0xa0, 0x9e, 0x79, 0x9e, 0x3f, 0x5c, 0x70, 0x83, 0xbd, 0x5b, 0xb0, 0x90, 0x13, 0x0d, 0x0a,
0xd4, 0xe1, 0xcd, 0xdc, 0x7e, 0xee, 0xa0, 0x60, 0x88, 0x3f, 0x09, 0x81, 0xe2, 0x8c, 0x85, 0xbc,
0x99, 0xdf, 0xcf, 0x1d, 0x54, 0x0d, 0xfc, 0x9b, 0xb4, 0xe1, 0xb1, 0x43, 0x6f, 0xcc, 0xf0, 0x03,
0xf5, 0xcd, 0xc0, 0x5b, 0x70, 0xcb, 0xbd, 0x36, 0xaf, 0x18, 0x6b, 0x16, 0xf0, 0xd8, 0xa6, 0x43,
0x6f, 0xc6, 0x1f, 0xa8, 0x6f, 0x48, 0xc9, 0x09, 0x63, 0xe4, 0x4b, 0xd8, 0x16, 0x07, 0xfc, 0x80,
0xf9, 0xf4, 0x36, 0x73, 0xa4, 0x88, 0x47, 0xb6, 0x1c, 0x7a, 0x33, 0x42, 0x61, 0xea, 0xd0, 0x3e,
0x28, 0xc9, 0x2d, 0x82, 0x5a, 0x42, 0x2a, 0x44, 0xda, 0x05, 0xe3, 0x13, 0x50, 0x53, 0x6a, 0xc5,
0xc3, 0x37, 0x90, 0xa3, 0x24, 0xea, 0x3a, 0x0e, 0x27, 0x3a, 0xd4, 0x05, 0xcb, 0xb1, 0x5c, 0x16,
0xa0, 0xa2, 0x32, 0x92, 0x6a, 0x0e, 0xbd, 0x39, 0x17, 0x98, 0xd0, 0xf4, 0x19, 0x68, 0xc2, 0x67,
0xa6, 0xb7, 0xe0, 0xe6, 0x74, 0x4e, 0x5d, 0x97, 0xd9, 0xcd, 0xca, 0x7e, 0xee, 0xa0, 0xf8, 0x2a,
0xdf, 0xcc, 0x19, 0xaa, 0x2d, 0xbd, 0xd4, 0x95, 0x12, 0x72, 0x08, 0x9b, 0xde, 0x82, 0x5f, 0x7b,
0xc2, 0x08, 0xc1, 0x36, 0x43, 0xc6, 0x9b, 0xb5, 0xfd, 0xc2, 0x41, 0xd1, 0x68, 0xc4, 0x02, 0xc1,
0x1d, 0x33, 0x2e, 0xb8, 0xe1, 0x07, 0xc6, 0x7c, 0x73, 0xea, 0xb9, 0x57, 0x26, 0xa7, 0xc1, 0x35,
0xe3, 0xcd, 0xea, 0x7e, 0xee, 0xa0, 0x64, 0x34, 0x50, 0xd0, 0xf5, 0xdc, 0xab, 0x09, 0xc2, 0xe4,
0x73, 0x20, 0x73, 0x6e, 0x4f, 0x91, 0x6a, 0x05, 0x8e, 0x0c, 0x56, 0xb3, 0x8e, 0xe4, 0x4d, 0x21,
0xe9, 0xa6, 0x05, 0xe4, 0x25, 0xec, 0xa2, 0x73, 0xfc, 0xc5, 0xa5, 0x6d, 0x4d, 0x11, 0x34, 0x67,
0x8c, 0xce, 0x6c, 0xcb, 0x65, 0x4d, 0x10, 0xaf, 0x37, 0x76, 0x04, 0x61, 0xb4, 0x94, 0x1f, 0x47,
0x62, 0xf2, 0x18, 0x4a, 0x36, 0xbd, 0x64, 0x76, 0x53, 0xc1, 0xb8, 0xca, 0x0f, 0xfd, 0x1f, 0x39,
0xa8, 0x8b, 0x8c, 0xe8, 0xbb, 0xf7, 0x27, 0xc4, 0x6a, 0x58, 0xf2, 0x77, 0xc2, 0x72, 0xc7, 0xe1,
0x85, 0xbb, 0x0e, 0xdf, 0x85, 0x8a, 0x4d, 0x43, 0x6e, 0xce, 0x3d, 0x1f, 0x73, 0x40, 0x31, 0xca,
0xe2, 0xfb, 0xd4, 0xf3, 0xc9, 0x33, 0xa8, 0xb3, 0x1b, 0xce, 0x02, 0x97, 0xda, 0xa6, 0x30, 0x1a,
0x03, 0x5f, 0x31, 0x94, 0x18, 0x3c, 0xe5, 0xf6, 0x94, 0x1c, 0x80, 0x96, 0xb8, 0x2a, 0xf6, 0xea,
0x06, 0x3a, 0x4a, 0x8d, 0x1d, 0x15, 0x39, 0x35, 0xb1, 0xb4, 0x9c, 0xb6, 0xf4, 0x9f, 0x39, 0x50,
0x30, 0x49, 0x59, 0xe8, 0x7b, 0x6e, 0xc8, 0x08, 0x81, 0xbc, 0x35, 0x43, 0x3b, 0xab, 0x18, 0xf3,
0xbc, 0x35, 0x13, 0x8f, 0xb4, 0x66, 0xe6, 0xe5, 0x2d, 0x67, 0x21, 0xda, 0xa0, 0x18, 0x65, 0x6b,
0xf6, 0x4a, 0x7c, 0x92, 0xe7, 0xa0, 0xe0, 0xfd, 0x74, 0x36, 0x0b, 0x58, 0x18, 0xca, 0xf2, 0xc0,
0x83, 0x35, 0x81, 0x77, 0x24, 0x4c, 0x5e, 0xc0, 0x56, 0x9a, 0x66, 0xba, 0xfe, 0xd1, 0x87, 0x70,
0x8e, 0x16, 0x57, 0x65, 0x48, 0x23, 0xe6, 0x00, 0x05, 0xe4, 0xb3, 0x28, 0x03, 0x62, 0xbe, 0xa4,
0x97, 0x90, 0xae, 0xa5, 0xe8, 0x23, 0x64, 0x3f, 0x07, 0x35, 0x64, 0xc1, 0x7b, 0x16, 0x98, 0x0e,
0x0b, 0x43, 0x7a, 0xcd, 0xd0, 0x05, 0x55, 0xa3, 0x2e, 0xd1, 0x73, 0x09, 0xea, 0x1a, 0xa8, 0xe7,
0x9e, 0x6b, 0x71, 0x2f, 0x88, 0xa2, 0xaa, 0xff, 0xb1, 0x08, 0x20, 0xac, 0x1f, 0x73, 0xca, 0x17,
0xe1, 0xda, 0xaa, 0x17, 0xde, 0xc8, 0xdf, 0xeb, 0x8d, 0xda, 0xaa, 0x37, 0x8a, 0xfc, 0xd6, 0x97,
0x81, 0x56, 0x8f, 0x36, 0x5f, 0x44, 0xfd, 0xe7, 0x85, 0xb8, 0x63, 0x72, 0xeb, 0x33, 0x03, 0xc5,
0xe4, 0x00, 0x4a, 0x21, 0xa7, 0x5c, 0x56, 0xbd, 0x7a, 0x44, 0x32, 0x3c, 0xf1, 0x16, 0x66, 0x48,
0x02, 0xf9, 0x09, 0xa8, 0x57, 0xd4, 0xb2, 0x17, 0x01, 0x33, 0x03, 0x46, 0x43, 0xcf, 0x6d, 0xaa,
0x78, 0x64, 0x3b, 0x39, 0x72, 0x22, 0xc5, 0x06, 0x4a, 0x8d, 0xfa, 0x55, 0xfa, 0x93, 0xfc, 0x00,
0x1a, 0x96, 0x6b, 0x71, 0x4b, 0xd6, 0x04, 0xb7, 0x9c, 0xb8, 0x7b, 0xa8, 0x4b, 0x78, 0x62, 0x39,
0xe2, 0x45, 0x1a, 0xa6, 0xe1, 0xc2, 0x9f, 0x51, 0xce, 0x24, 0x53, 0xf6, 0x10, 0x55, 0xe0, 0x17,
0x08, 0x23, 0x73, 0x35, 0xe0, 0xe5, 0xf5, 0x01, 0x5f, 0x1f, 0x40, 0xe5, 0x9e, 0x00, 0xde, 0x93,
0x1e, 0xf5, 0xfb, 0xd2, 0xe3, 0x09, 0xd4, 0xa6, 0x5e, 0xc8, 0x4d, 0x19, 0x5f, 0xec, 0x50, 0x05,
0x03, 0x04, 0x34, 0x46, 0x84, 0x3c, 0x05, 0x05, 0x09, 0x9e, 0x3b, 0x9d, 0x53, 0xcb, 0xc5, 0x46,
0x53, 0x30, 0xf0, 0xd0, 0x50, 0x42, 0xa2, 0xbc, 0x24, 0xe5, 0xea, 0x4a, 0x72, 0x40, 0xf6, 0x4c,
0xe4, 0x44, 0xd8, 0xb2, 0x68, 0x1a, 0xe9, 0xa2, 0x21, 0xa0, 0x9d, 0x59, 0x21, 0x17, 0xd1, 0x0a,
0xe3, 0x54, 0xfa, 0x29, 0x6c, 0xa6, 0xb0, 0xa8, 0x98, 0x3e, 0x85, 0x92, 0xe8, 0x0f, 0x61, 0x33,
0xb7, 0x5f, 0x38, 0xa8, 0x1d, 0x6d, 0xdd, 0x09, 0xf4, 0x22, 0x34, 0x24, 0x43, 0x7f, 0x0a, 0x0d,
0x01, 0xf6, 0xdd, 0x2b, 0x2f, 0xee, 0x39, 0x6a, 0x52, 0x8a, 0x8a, 0x48, 0x3c, 0x5d, 0x05, 0x65,
0xc2, 0x02, 0x27, 0xb9, 0xf2, 0xb7, 0xd0, 0xe8, 0xbb, 0x11, 0x12, 0x5d, 0xf8, 0x7d, 0x68, 0x38,
0x96, 0x2b, 0x9b, 0x12, 0x75, 0xbc, 0x85, 0xcb, 0xa3, 0x80, 0xd7, 0x1d, 0xcb, 0x15, 0xfa, 0x3b,
0x08, 0x22, 0x2f, 0x6e, 0x5e, 0x11, 0x6f, 0x23, 0xe2, 0xc9, 0xfe, 0x25, 0x79, 0xaf, 0x8b, 0x95,
0x9c, 0x96, 0x7f, 0x5d, 0xac, 0xe4, 0xb5, 0xc2, 0xeb, 0x62, 0xa5, 0xa0, 0x15, 0x5f, 0x17, 0x2b,
0x45, 0xad, 0xf4, 0xba, 0x58, 0x29, 0x6b, 0x15, 0xfd, 0x2f, 0x39, 0xd0, 0x86, 0x0b, 0xfe, 0x7f,
0x7d, 0x02, 0x0e, 0x37, 0xcb, 0x35, 0xa7, 0x36, 0x7f, 0x6f, 0xce, 0x98, 0xcd, 0x29, 0x86, 0xbb,
0x64, 0x28, 0x8e, 0xe5, 0x76, 0x6d, 0xfe, 0xfe, 0x58, 0x60, 0xf1, 0x08, 0x4c, 0xb1, 0xaa, 0x11,
0x8b, 0xde, 0x24, 0xac, 0xff, 0x62, 0xce, 0x1f, 0x72, 0xa0, 0xfc, 0x7c, 0xe1, 0x71, 0x76, 0x7f,
0xd3, 0xc7, 0xc4, 0x5b, 0x76, 0xda, 0x3c, 0xde, 0x01, 0xd3, 0x65, 0x97, 0xbd, 0xd3, 0xb4, 0x0b,
0x6b, 0x9a, 0xf6, 0x83, 0x03, 0xab, 0xf8, 0xe0, 0xc0, 0xd2, 0xbf, 0xcb, 0x89, 0xa8, 0x47, 0xcf,
0x8c, 0x5c, 0xbe, 0x0f, 0x4a, 0x3c, 0x86, 0xcc, 0x90, 0xc6, 0x0f, 0x86, 0x50, 0xce, 0xa1, 0x31,
0xc5, 0x4d, 0x05, 0x0b, 0x0c, 0x6f, 0x0c, 0xe7, 0x09, 0x33, 0xda, 0x54, 0x84, 0x6c, 0x24, 0x45,
0xd1, 0x81, 0xef, 0x01, 0xa4, 0x7c, 0x59, 0x42, 0x3b, 0xab, 0xd3, 0x94, 0x23, 0xa5, 0x0b, 0x8b,
0x5a, 0x49, 0xff, 0xab, 0xcc, 0x82, 0xff, 0xf5, 0x49, 0x9f, 0x80, 0xba, 0x5c, 0x58, 0x90, 0x23,
0x27, 0xa8, 0xe2, 0xc7, 0x1b, 0x8b, 0x60, 0xfd, 0x30, 0xea, 0x23, 0x72, 0x77, 0xc8, 0x3e, 0xbb,
0x21, 0x24, 0x63, 0x21, 0x88, 0x54, 0xe2, 0x8e, 0x21, 0xfc, 0x4a, 0x6f, 0x1d, 0xe6, 0x72, 0x13,
0x17, 0x36, 0x39, 0x55, 0x1b, 0xe8, 0x4f, 0x89, 0x1f, 0x8b, 0xd8, 0x3e, 0x6c, 0xa0, 0xde, 0x80,
0xfa, 0xc4, 0xfb, 0x15, 0x73, 0x93, 0x62, 0xfb, 0x0a, 0xd4, 0x18, 0x88, 0x4c, 0x3c, 0x84, 0x0d,
0x8e, 0x48, 0x54, 0xdd, 0xcb, 0x36, 0x7e, 0x16, 0x52, 0x8e, 0x64, 0x23, 0x62, 0xe8, 0x7f, 0xca,
0x43, 0x35, 0x41, 0x45, 0x92, 0x5c, 0xd2, 0x90, 0x99, 0x0e, 0x9d, 0xd2, 0xc0, 0xf3, 0xdc, 0xa8,
0xc6, 0x15, 0x01, 0x9e, 0x47, 0x98, 0x68, 0x61, 0xb1, 0x1d, 0x73, 0x1a, 0xce, 0xd1, 0x3b, 0x8a,
0x51, 0x8b, 0xb0, 0x53, 0x1a, 0xce, 0xc9, 0xa7, 0xa0, 0xc5, 0x14, 0x3f, 0x60, 0x96, 0x23, 0x26,
0x9f, 0x9c, 0xcf, 0x8d, 0x08, 0x1f, 0x45, 0xb0, 0x68, 0xf0, 0xb2, 0xc8, 0x4c, 0x9f, 0x5a, 0x33,
0xd3, 0x11, 0x5e, 0x94, 0x3b, 0xa7, 0x2a, 0xf1, 0x11, 0xb5, 0x66, 0xe7, 0x21, 0xe5, 0xe4, 0x0b,
0xf8, 0x28, 0xb5, 0x98, 0xa6, 0xe8, 0xb2, 0x8a, 0x49, 0x90, 0x6c, 0xa6, 0xc9, 0x91, 0xa7, 0xa0,
0x88, 0x89, 0x61, 0x4e, 0x03, 0x46, 0x39, 0x9b, 0x45, 0x75, 0x5c, 0x13, 0x58, 0x57, 0x42, 0xa4,
0x09, 0x65, 0x76, 0xe3, 0x5b, 0x01, 0x9b, 0xe1, 0xc4, 0xa8, 0x18, 0xf1, 0xa7, 0x38, 0x1c, 0x72,
0x2f, 0xa0, 0xd7, 0xcc, 0x74, 0xa9, 0xc3, 0xb0, 0xba, 0xab, 0x46, 0x2d, 0xc2, 0x06, 0xd4, 0x61,
0xfa, 0xc7, 0xb0, 0xfb, 0x35, 0xe3, 0x67, 0xd6, 0xbb, 0x85, 0x35, 0xb3, 0xf8, 0xed, 0x88, 0x06,
0x74, 0xd9, 0x05, 0xbb, 0xb0, 0x95, 0x95, 0x30, 0xce, 0x02, 0x31, 0x80, 0x4a, 0xc1, 0xc2, 0x66,
0x71, 0x70, 0x96, 0x03, 0x33, 0x21, 0x1b, 0x0b, 0x9b, 0x19, 0x92, 0xa4, 0xff, 0x59, 0x2c, 0x7c,
0x69, 0x01, 0xe6, 0x87, 0x5c, 0x73, 0xcd, 0xa8, 0x09, 0x17, 0x8d, 0x6a, 0x84, 0xf4, 0x67, 0xe4,
0x45, 0x34, 0xe9, 0xf3, 0x38, 0x8e, 0x5b, 0xeb, 0xb5, 0xa7, 0x46, 0xfe, 0xe7, 0x40, 0x2c, 0x77,
0xea, 0x39, 0xc2, 0xad, 0x7c, 0x1e, 0xb0, 0x70, 0xee, 0xd9, 0x33, 0x0c, 0x56, 0xdd, 0xd8, 0x8c,
0x25, 0x93, 0x58, 0x20, 0xe8, 0xc9, 0x66, 0xbd, 0xa4, 0x17, 0x25, 0x3d, 0x96, 0x24, 0x74, 0xfd,
0x2d, 0xec, 0x8e, 0xef, 0x73, 0x10, 0xf9, 0x0a, 0xc0, 0x4f, 0xfc, 0x82, 0x96, 0xd4, 0x8e, 0xf6,
0xee, 0x3e, 0x78, 0xe9, 0x3b, 0x23, 0xc5, 0xd7, 0xf7, 0xa0, 0xb5, 0x4e, 0xb5, 0xac, 0x81, 0xc3,
0xe7, 0x50, 0x89, 0x77, 0x1b, 0xa2, 0x40, 0xe5, 0x6c, 0x38, 0x1c, 0x99, 0xc3, 0x8b, 0x89, 0xf6,
0x88, 0xd4, 0xa0, 0x8c, 0x5f, 0xfd, 0x81, 0x96, 0x3b, 0x0c, 0xa1, 0x9a, 0xac, 0x36, 0xa4, 0x0e,
0xd5, 0xfe, 0xa0, 0x3f, 0xe9, 0x77, 0x26, 0xbd, 0x63, 0xed, 0x11, 0xf9, 0x08, 0x36, 0x47, 0x46,
0xaf, 0x7f, 0xde, 0xf9, 0xba, 0x67, 0x1a, 0xbd, 0x37, 0xbd, 0xce, 0x59, 0xef, 0x58, 0xcb, 0x11,
0x02, 0xea, 0xe9, 0xe4, 0xac, 0x6b, 0x8e, 0x2e, 0x5e, 0x9d, 0xf5, 0xc7, 0xa7, 0xbd, 0x63, 0x2d,
0x2f, 0x74, 0x8e, 0x2f, 0xba, 0xdd, 0xde, 0x78, 0xac, 0x15, 0x08, 0xc0, 0xc6, 0x49, 0xa7, 0x2f,
0xc8, 0x45, 0xb2, 0x05, 0x8d, 0xfe, 0xe0, 0xcd, 0xb0, 0xdf, 0xed, 0x99, 0xe3, 0xde, 0x64, 0x22,
0xc0, 0xd2, 0xe1, 0xbf, 0x72, 0x50, 0xcf, 0x6c, 0x47, 0x64, 0x07, 0xb6, 0xc4, 0x91, 0x0b, 0x43,
0xdc, 0xd4, 0x19, 0x0f, 0x07, 0xe6, 0x60, 0x38, 0xe8, 0x69, 0x8f, 0xc8, 0xc7, 0xb0, 0xb3, 0x22,
0x18, 0x9e, 0x9c, 0x74, 0x4f, 0x3b, 0xe2, 0xf1, 0xa4, 0x05, 0xdb, 0x2b, 0xc2, 0x49, 0xff, 0xbc,
0x27, 0xac, 0xcc, 0x93, 0x7d, 0xd8, 0x5b, 0x91, 0x8d, 0xbf, 0xe9, 0xf5, 0x46, 0x09, 0xa3, 0x40,
0x9e, 0xc3, 0xd3, 0x15, 0x46, 0x7f, 0x30, 0xbe, 0x38, 0x39, 0xe9, 0x77, 0xfb, 0xbd, 0xc1, 0xc4,
0x7c, 0xd3, 0x39, 0xbb, 0xe8, 0x69, 0x45, 0xb2, 0x07, 0xcd, 0xd5, 0x4b, 0x7a, 0xe7, 0xa3, 0xa1,
0xd1, 0x31, 0xde, 0x6a, 0x25, 0xf2, 0x0c, 0x9e, 0xdc, 0x51, 0xd2, 0x1d, 0x1a, 0x46, 0xaf, 0x3b,
0x31, 0x3b, 0xe7, 0xc3, 0x8b, 0xc1, 0x44, 0xdb, 0x38, 0x6c, 0x8b, 0x0d, 0x64, 0x25, 0xfb, 0x84,
0xcb, 0x2e, 0x06, 0x3f, 0x1b, 0x0c, 0xbf, 0x19, 0x68, 0x8f, 0x84, 0xe7, 0x27, 0xa7, 0x46, 0x6f,
0x7c, 0x3a, 0x3c, 0x3b, 0xd6, 0x72, 0x47, 0xff, 0xae, 0xc8, 0xed, 0xb7, 0x8b, 0xbf, 0x99, 0x89,
0x01, 0xe5, 0xe8, 0x57, 0x30, 0xd9, 0x59, 0xa6, 0x47, 0xe6, 0x77, 0x71, 0xeb, 0xa3, 0xcc, 0x06,
0x13, 0xa7, 0x81, 0xbe, 0xf3, 0xbb, 0xbf, 0xfd, 0xfd, 0xf7, 0xf9, 0x4d, 0x5d, 0x69, 0xbf, 0xff,
0xa2, 0x2d, 0x18, 0x6d, 0x6f, 0xc1, 0x5f, 0xe6, 0x0e, 0xc9, 0x10, 0x36, 0xe4, 0xef, 0x28, 0xb2,
0x9d, 0x51, 0x99, 0xfc, 0xb0, 0xba, 0x4f, 0xe3, 0x36, 0x6a, 0xd4, 0xf4, 0x5a, 0xa2, 0xd1, 0x72,
0x85, 0xc2, 0x1f, 0x43, 0x39, 0xda, 0xe1, 0x53, 0x8f, 0xcc, 0x6e, 0xf5, 0xad, 0x75, 0x6b, 0xd6,
0x8f, 0x72, 0xe4, 0x17, 0x50, 0x4d, 0x36, 0x34, 0xb2, 0x9b, 0x2a, 0x80, 0xec, 0x26, 0xd7, 0x6a,
0xad, 0x13, 0x65, 0x9f, 0x45, 0xd4, 0xe4, 0x59, 0xb8, 0xbd, 0x91, 0x0b, 0x59, 0x07, 0x62, 0x7b,
0x23, 0xcd, 0xcc, 0xf5, 0xa9, 0x85, 0x6e, 0xed, 0xc3, 0xf4, 0x16, 0xaa, 0x7c, 0x4c, 0x48, 0x46,
0x65, 0xfb, 0x5b, 0x6b, 0xf6, 0x6b, 0xf2, 0x4b, 0x50, 0xa2, 0x00, 0xe0, 0x8e, 0x45, 0x96, 0xce,
0x4a, 0x2f, 0x82, 0xad, 0xa5, 0x31, 0xab, 0xdb, 0xd8, 0x1a, 0xed, 0xde, 0x82, 0xb7, 0x39, 0x6a,
0xbb, 0x4c, 0xb4, 0xe3, 0xec, 0x4e, 0x69, 0x4f, 0x6f, 0x41, 0x59, 0xed, 0x99, 0x29, 0xaf, 0xef,
0xa3, 0xf6, 0x16, 0x69, 0x66, 0xb4, 0xbf, 0x13, 0x9c, 0xf6, 0xb7, 0xd4, 0xe1, 0xc2, 0x02, 0x55,
0xb4, 0x6e, 0x0c, 0xf9, 0x83, 0x36, 0x2c, 0xbd, 0xb6, 0xb2, 0xd3, 0xea, 0xbb, 0x78, 0xc9, 0x16,
0xd9, 0x4c, 0xa5, 0x42, 0x62, 0xc1, 0x52, 0xfb, 0x83, 0x36, 0xa4, 0xb5, 0x67, 0x4d, 0x78, 0x82,
0xda, 0x77, 0xc9, 0x4e, 0x5a, 0x7b, 0xda, 0x82, 0xb7, 0x50, 0x17, 0x77, 0xc4, 0xc3, 0x3b, 0x4c,
0x65, 0x72, 0x66, 0x43, 0x68, 0xed, 0xdc, 0xc1, 0xb3, 0xd5, 0x41, 0x1a, 0x78, 0x45, 0x48, 0x79,
0x5b, 0x6e, 0x05, 0x84, 0x03, 0xb9, 0x3b, 0xd7, 0x88, 0x9e, 0xe8, 0xb9, 0x77, 0xe8, 0xb5, 0x1e,
0xec, 0xdf, 0xfa, 0x1e, 0x5e, 0xb8, 0x4d, 0x1e, 0xe3, 0x85, 0x31, 0xa1, 0xed, 0x4b, 0xfd, 0xbf,
0x01, 0x32, 0x7e, 0xe8, 0xd6, 0x7b, 0x27, 0x49, 0xeb, 0xd9, 0x83, 0x9c, 0xac, 0x43, 0xf5, 0xb5,
0x97, 0xbf, 0xcc, 0x1d, 0x5e, 0x6e, 0xe0, 0x7f, 0xdd, 0xbe, 0xfc, 0x4f, 0x00, 0x00, 0x00, 0xff,
0xff, 0x46, 0x7a, 0xcd, 0x4b, 0xac, 0x13, 0x00, 0x00,
// 2133 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcd, 0x6e, 0x23, 0xc7,
0xf1, 0x5f, 0x7e, 0x89, 0x64, 0x71, 0x48, 0x8e, 0x5a, 0xbb, 0x12, 0x45, 0xcb, 0x58, 0xed, 0xac,
0xf7, 0xff, 0x97, 0x15, 0x5b, 0x8c, 0xe5, 0x53, 0x0c, 0x27, 0x00, 0x97, 0xa2, 0x2c, 0x2a, 0x12,
0xc9, 0x0c, 0xa9, 0x35, 0x36, 0x08, 0x30, 0x68, 0x91, 0x2d, 0x71, 0x10, 0xce, 0xc7, 0xce, 0x34,
0x77, 0x25, 0x18, 0x49, 0x80, 0xbc, 0x80, 0x0f, 0x79, 0x83, 0x3c, 0x43, 0x6e, 0xc9, 0x23, 0xe4,
0x94, 0x1c, 0x73, 0x0d, 0x10, 0xe4, 0x90, 0x77, 0x08, 0xba, 0xba, 0x67, 0x38, 0xa4, 0x48, 0x05,
0x39, 0xe4, 0x26, 0xfe, 0xea, 0xd7, 0x55, 0x5d, 0x55, 0x5d, 0x1f, 0x23, 0xd0, 0x46, 0x53, 0x9b,
0xb9, 0xfc, 0xc8, 0x0f, 0x3c, 0xee, 0x91, 0xfc, 0xd4, 0xf3, 0xfc, 0xc0, 0x1f, 0xd5, 0xf7, 0x6e,
0x3d, 0xef, 0x76, 0xca, 0x1a, 0xd4, 0xb7, 0x1b, 0xd4, 0x75, 0x3d, 0x4e, 0xb9, 0xed, 0xb9, 0xa1,
0xa4, 0x19, 0xdf, 0x67, 0xa1, 0x72, 0xe1, 0x79, 0x7e, 0x6f, 0xc6, 0x4d, 0xf6, 0x6e, 0xc6, 0x42,
0x4e, 0x74, 0xc8, 0x50, 0x87, 0xd7, 0x52, 0xfb, 0xa9, 0x83, 0x8c, 0x29, 0xfe, 0x24, 0x04, 0xb2,
0x63, 0x16, 0xf2, 0x5a, 0x7a, 0x3f, 0x75, 0x50, 0x34, 0xf1, 0x6f, 0xd2, 0x80, 0xa7, 0x0e, 0xbd,
0xb3, 0xc2, 0x0f, 0xd4, 0xb7, 0x02, 0x6f, 0xc6, 0x6d, 0xf7, 0xd6, 0xba, 0x61, 0xac, 0x96, 0xc1,
0x63, 0x9b, 0x0e, 0xbd, 0x1b, 0x7c, 0xa0, 0xbe, 0x29, 0x25, 0xa7, 0x8c, 0x91, 0x2f, 0x61, 0x5b,
0x1c, 0xf0, 0x03, 0xe6, 0xd3, 0xfb, 0x85, 0x23, 0x59, 0x3c, 0xb2, 0xe5, 0xd0, 0xbb, 0x3e, 0x0a,
0x13, 0x87, 0xf6, 0x41, 0x8b, 0xad, 0x08, 0x6a, 0x0e, 0xa9, 0xa0, 0xb4, 0x0b, 0xc6, 0x27, 0x50,
0x49, 0xa8, 0x15, 0x17, 0xdf, 0x40, 0x8e, 0x16, 0xab, 0x6b, 0x3a, 0x9c, 0x18, 0x50, 0x16, 0x2c,
0xc7, 0x76, 0x59, 0x80, 0x8a, 0xf2, 0x48, 0x2a, 0x39, 0xf4, 0xee, 0x52, 0x60, 0x42, 0xd3, 0x67,
0xa0, 0x8b, 0x98, 0x59, 0xde, 0x8c, 0x5b, 0xa3, 0x09, 0x75, 0x5d, 0x36, 0xad, 0x15, 0xf6, 0x53,
0x07, 0xd9, 0xd7, 0xe9, 0x5a, 0xca, 0xac, 0x4c, 0x65, 0x94, 0x5a, 0x52, 0x42, 0x0e, 0x61, 0xd3,
0x9b, 0xf1, 0x5b, 0x4f, 0x38, 0x21, 0xd8, 0x56, 0xc8, 0x78, 0xad, 0xb4, 0x9f, 0x39, 0xc8, 0x9a,
0xd5, 0x48, 0x20, 0xb8, 0x03, 0xc6, 0x05, 0x37, 0xfc, 0xc0, 0x98, 0x6f, 0x8d, 0x3c, 0xf7, 0xc6,
0xe2, 0x34, 0xb8, 0x65, 0xbc, 0x56, 0xdc, 0x4f, 0x1d, 0xe4, 0xcc, 0x2a, 0x0a, 0x5a, 0x9e, 0x7b,
0x33, 0x44, 0x98, 0x7c, 0x0e, 0x64, 0xc2, 0xa7, 0x23, 0xa4, 0xda, 0x81, 0x23, 0x93, 0x55, 0x2b,
0x23, 0x79, 0x53, 0x48, 0x5a, 0x49, 0x01, 0xf9, 0x0a, 0x76, 0x31, 0x38, 0xfe, 0xec, 0x7a, 0x6a,
0x8f, 0x10, 0xb4, 0xc6, 0x8c, 0x8e, 0xa7, 0xb6, 0xcb, 0x6a, 0x20, 0x6e, 0x6f, 0xee, 0x08, 0x42,
0x7f, 0x2e, 0x3f, 0x51, 0x62, 0xf2, 0x14, 0x72, 0x53, 0x7a, 0xcd, 0xa6, 0x35, 0x0d, 0xf3, 0x2a,
0x7f, 0x18, 0xff, 0x48, 0x41, 0x59, 0xbc, 0x88, 0x8e, 0xbb, 0xfe, 0x41, 0x2c, 0xa7, 0x25, 0xfd,
0x20, 0x2d, 0x0f, 0x02, 0x9e, 0x79, 0x18, 0xf0, 0x5d, 0x28, 0x4c, 0x69, 0xc8, 0xad, 0x89, 0xe7,
0xe3, 0x1b, 0xd0, 0xcc, 0xbc, 0xf8, 0x7d, 0xe6, 0xf9, 0xe4, 0x25, 0x94, 0xd9, 0x1d, 0x67, 0x81,
0x4b, 0xa7, 0x96, 0x70, 0x1a, 0x13, 0x5f, 0x30, 0xb5, 0x08, 0x3c, 0xe3, 0xd3, 0x11, 0x39, 0x00,
0x3d, 0x0e, 0x55, 0x14, 0xd5, 0x0d, 0x0c, 0x54, 0x25, 0x0a, 0x94, 0x0a, 0x6a, 0xec, 0x69, 0x3e,
0xe9, 0xe9, 0x3f, 0x53, 0xa0, 0xe1, 0x23, 0x65, 0xa1, 0xef, 0xb9, 0x21, 0x23, 0x04, 0xd2, 0xf6,
0x18, 0xfd, 0x2c, 0x62, 0xce, 0xd3, 0xf6, 0x58, 0x5c, 0xd2, 0x1e, 0x5b, 0xd7, 0xf7, 0x9c, 0x85,
0xe8, 0x83, 0x66, 0xe6, 0xed, 0xf1, 0x6b, 0xf1, 0x93, 0xbc, 0x02, 0x0d, 0xed, 0xd3, 0xf1, 0x38,
0x60, 0x61, 0x28, 0xcb, 0x03, 0x0f, 0x96, 0x04, 0xde, 0x94, 0x30, 0x39, 0x82, 0xad, 0x24, 0xcd,
0x72, 0xfd, 0xe3, 0x0f, 0xe1, 0x04, 0x3d, 0x2e, 0xca, 0x94, 0x2a, 0x66, 0x17, 0x05, 0xe4, 0x33,
0xf5, 0x02, 0x22, 0xbe, 0xa4, 0xe7, 0x90, 0xae, 0x27, 0xe8, 0x7d, 0x64, 0xbf, 0x82, 0x4a, 0xc8,
0x82, 0xf7, 0x2c, 0xb0, 0x1c, 0x16, 0x86, 0xf4, 0x96, 0x61, 0x08, 0x8a, 0x66, 0x59, 0xa2, 0x97,
0x12, 0x34, 0x74, 0xa8, 0x5c, 0x7a, 0xae, 0xcd, 0xbd, 0x40, 0x65, 0xd5, 0xf8, 0x43, 0x16, 0x40,
0x78, 0x3f, 0xe0, 0x94, 0xcf, 0xc2, 0x95, 0x55, 0x2f, 0xa2, 0x91, 0x5e, 0x1b, 0x8d, 0xd2, 0x72,
0x34, 0xb2, 0xfc, 0xde, 0x97, 0x89, 0xae, 0x1c, 0x6f, 0x1e, 0xa9, 0xfe, 0x73, 0x24, 0x6c, 0x0c,
0xef, 0x7d, 0x66, 0xa2, 0x98, 0x1c, 0x40, 0x2e, 0xe4, 0x94, 0xcb, 0xaa, 0xaf, 0x1c, 0x93, 0x05,
0x9e, 0xb8, 0x0b, 0x33, 0x25, 0x81, 0xfc, 0x18, 0x2a, 0x37, 0xd4, 0x9e, 0xce, 0x02, 0x66, 0x05,
0x8c, 0x86, 0x9e, 0x5b, 0xab, 0xe0, 0x91, 0xed, 0xf8, 0xc8, 0xa9, 0x14, 0x9b, 0x28, 0x35, 0xcb,
0x37, 0xc9, 0x9f, 0xe4, 0xff, 0xa1, 0x6a, 0xbb, 0x36, 0xb7, 0x65, 0x4d, 0x70, 0xdb, 0x89, 0xba,
0x47, 0x65, 0x0e, 0x0f, 0x6d, 0x47, 0xdc, 0x48, 0xc7, 0x67, 0x38, 0xf3, 0xc7, 0x94, 0x33, 0xc9,
0x94, 0x3d, 0xa4, 0x22, 0xf0, 0x2b, 0x84, 0x91, 0xb9, 0x9c, 0xf0, 0xfc, 0xea, 0x84, 0xaf, 0x4e,
0xa0, 0xb6, 0x26, 0x81, 0x6b, 0x9e, 0x47, 0x79, 0xdd, 0xf3, 0x78, 0x0e, 0xa5, 0x91, 0x17, 0x72,
0x4b, 0xe6, 0x17, 0x3b, 0x54, 0xc6, 0x04, 0x01, 0x0d, 0x10, 0x21, 0x2f, 0x40, 0x43, 0x82, 0xe7,
0x8e, 0x26, 0xd4, 0x76, 0xb1, 0xd1, 0x64, 0x4c, 0x3c, 0xd4, 0x93, 0x90, 0x28, 0x2f, 0x49, 0xb9,
0xb9, 0x91, 0x1c, 0x90, 0x3d, 0x13, 0x39, 0x0a, 0x9b, 0x17, 0x4d, 0x35, 0x59, 0x34, 0x04, 0xf4,
0x0b, 0x3b, 0xe4, 0x22, 0x5b, 0x61, 0xf4, 0x94, 0x7e, 0x02, 0x9b, 0x09, 0x4c, 0x15, 0xd3, 0xa7,
0x90, 0x13, 0xfd, 0x21, 0xac, 0xa5, 0xf6, 0x33, 0x07, 0xa5, 0xe3, 0xad, 0x07, 0x89, 0x9e, 0x85,
0xa6, 0x64, 0x18, 0x2f, 0xa0, 0x2a, 0xc0, 0x8e, 0x7b, 0xe3, 0x45, 0x3d, 0xa7, 0x12, 0x97, 0xa2,
0x26, 0x1e, 0x9e, 0x51, 0x01, 0x6d, 0xc8, 0x02, 0x27, 0x36, 0xf9, 0x1b, 0xa8, 0x76, 0x5c, 0x85,
0x28, 0x83, 0xff, 0x07, 0x55, 0xc7, 0x76, 0x65, 0x53, 0xa2, 0x8e, 0x37, 0x73, 0xb9, 0x4a, 0x78,
0xd9, 0xb1, 0x5d, 0xa1, 0xbf, 0x89, 0x20, 0xf2, 0xa2, 0xe6, 0xa5, 0x78, 0x1b, 0x8a, 0x27, 0xfb,
0x97, 0xe4, 0x9d, 0x67, 0x0b, 0x29, 0x3d, 0x7d, 0x9e, 0x2d, 0xa4, 0xf5, 0xcc, 0x79, 0xb6, 0x90,
0xd1, 0xb3, 0xe7, 0xd9, 0x42, 0x56, 0xcf, 0x9d, 0x67, 0x0b, 0x79, 0xbd, 0x60, 0xfc, 0x39, 0x05,
0x7a, 0x6f, 0xc6, 0xff, 0xa7, 0x57, 0xc0, 0xe1, 0x66, 0xbb, 0xd6, 0x68, 0xca, 0xdf, 0x5b, 0x63,
0x36, 0xe5, 0x14, 0xd3, 0x9d, 0x33, 0x35, 0xc7, 0x76, 0x5b, 0x53, 0xfe, 0xfe, 0x44, 0x60, 0xd1,
0x08, 0x4c, 0xb0, 0x8a, 0x8a, 0x45, 0xef, 0x62, 0xd6, 0x7f, 0x70, 0xe7, 0xf7, 0x29, 0xd0, 0x7e,
0x36, 0xf3, 0x38, 0x5b, 0xdf, 0xf4, 0xf1, 0xe1, 0xcd, 0x3b, 0x6d, 0x1a, 0x6d, 0xc0, 0x68, 0xde,
0x65, 0x1f, 0x34, 0xed, 0xcc, 0x8a, 0xa6, 0xfd, 0xe8, 0xc0, 0xca, 0x3e, 0x3a, 0xb0, 0x8c, 0xef,
0x53, 0x22, 0xeb, 0xea, 0x9a, 0x2a, 0xe4, 0xfb, 0xa0, 0x45, 0x63, 0xc8, 0x0a, 0x69, 0x74, 0x61,
0x08, 0xe5, 0x1c, 0x1a, 0x50, 0xdc, 0x54, 0xb0, 0xc0, 0xd0, 0x62, 0x38, 0x89, 0x99, 0x6a, 0x53,
0x11, 0xb2, 0xbe, 0x14, 0xa9, 0x03, 0x1f, 0x03, 0x24, 0x62, 0x99, 0x43, 0x3f, 0x8b, 0xa3, 0x44,
0x20, 0x65, 0x08, 0xb3, 0x7a, 0xce, 0xf8, 0x8b, 0x7c, 0x05, 0xff, 0xed, 0x95, 0x3e, 0x81, 0xca,
0x7c, 0x61, 0x41, 0x8e, 0x9c, 0xa0, 0x9a, 0x1f, 0x6d, 0x2c, 0x82, 0xf5, 0x03, 0xd5, 0x47, 0xe4,
0xee, 0xb0, 0x78, 0xed, 0xaa, 0x90, 0x0c, 0x84, 0x40, 0xa9, 0xc4, 0x1d, 0x43, 0xc4, 0x95, 0xde,
0x3b, 0xcc, 0xe5, 0x16, 0x2e, 0x6c, 0x72, 0xaa, 0x56, 0x31, 0x9e, 0x12, 0x3f, 0x11, 0xb9, 0x7d,
0xdc, 0x41, 0xa3, 0x0a, 0xe5, 0xa1, 0xf7, 0x4b, 0xe6, 0xc6, 0xc5, 0xf6, 0x35, 0x54, 0x22, 0x40,
0xb9, 0x78, 0x08, 0x1b, 0x1c, 0x11, 0x55, 0xdd, 0xf3, 0x36, 0x7e, 0x11, 0x52, 0x8e, 0x64, 0x53,
0x31, 0x8c, 0x3f, 0xa6, 0xa1, 0x18, 0xa3, 0xe2, 0x91, 0x5c, 0xd3, 0x90, 0x59, 0x0e, 0x1d, 0xd1,
0xc0, 0xf3, 0x5c, 0x55, 0xe3, 0x9a, 0x00, 0x2f, 0x15, 0x26, 0x5a, 0x58, 0xe4, 0xc7, 0x84, 0x86,
0x13, 0x8c, 0x8e, 0x66, 0x96, 0x14, 0x76, 0x46, 0xc3, 0x09, 0xf9, 0x14, 0xf4, 0x88, 0xe2, 0x07,
0xcc, 0x76, 0xc4, 0xe4, 0x93, 0xf3, 0xb9, 0xaa, 0xf0, 0xbe, 0x82, 0x45, 0x83, 0x97, 0x45, 0x66,
0xf9, 0xd4, 0x1e, 0x5b, 0x8e, 0x88, 0xa2, 0xdc, 0x39, 0x2b, 0x12, 0xef, 0x53, 0x7b, 0x7c, 0x19,
0x52, 0x4e, 0xbe, 0x80, 0x67, 0x89, 0xc5, 0x34, 0x41, 0x97, 0x55, 0x4c, 0x82, 0x78, 0x33, 0x8d,
0x8f, 0xbc, 0x00, 0x4d, 0x4c, 0x0c, 0x6b, 0x14, 0x30, 0xca, 0xd9, 0x58, 0xd5, 0x71, 0x49, 0x60,
0x2d, 0x09, 0x91, 0x1a, 0xe4, 0xd9, 0x9d, 0x6f, 0x07, 0x6c, 0x8c, 0x13, 0xa3, 0x60, 0x46, 0x3f,
0xc5, 0xe1, 0x90, 0x7b, 0x01, 0xbd, 0x65, 0x96, 0x4b, 0x1d, 0x86, 0xd5, 0x5d, 0x34, 0x4b, 0x0a,
0xeb, 0x52, 0x87, 0x19, 0x1f, 0xc1, 0xee, 0x37, 0x8c, 0x5f, 0xd8, 0xef, 0x66, 0xf6, 0xd8, 0xe6,
0xf7, 0x7d, 0x1a, 0xd0, 0x79, 0x17, 0x6c, 0xc1, 0xd6, 0xa2, 0x84, 0x71, 0x16, 0x88, 0x01, 0x94,
0x0b, 0x66, 0x53, 0x16, 0x25, 0x67, 0x3e, 0x30, 0x63, 0xb2, 0x39, 0x9b, 0x32, 0x53, 0x92, 0x8c,
0x3f, 0x89, 0x85, 0x2f, 0x29, 0xc0, 0xf7, 0x21, 0xd7, 0x5c, 0x4b, 0x35, 0xe1, 0xac, 0x59, 0x54,
0x48, 0x67, 0x4c, 0x8e, 0xd4, 0xa4, 0x4f, 0xe3, 0x38, 0xae, 0xaf, 0xd6, 0x9e, 0x18, 0xf9, 0x9f,
0x03, 0xb1, 0xdd, 0x91, 0xe7, 0x88, 0xb0, 0xf2, 0x49, 0xc0, 0xc2, 0x89, 0x37, 0x1d, 0x63, 0xb2,
0xca, 0xe6, 0x66, 0x24, 0x19, 0x46, 0x02, 0x41, 0x8f, 0x37, 0xeb, 0x39, 0x3d, 0x2b, 0xe9, 0x91,
0x24, 0xa6, 0x1b, 0x6f, 0x61, 0x77, 0xb0, 0x2e, 0x40, 0xe4, 0x6b, 0x00, 0x3f, 0x8e, 0x0b, 0x7a,
0x52, 0x3a, 0xde, 0x7b, 0x78, 0xe1, 0x79, 0xec, 0xcc, 0x04, 0xdf, 0xd8, 0x83, 0xfa, 0x2a, 0xd5,
0xb2, 0x06, 0x8c, 0x67, 0xb0, 0x35, 0x98, 0xdd, 0xde, 0xb2, 0xa5, 0x61, 0x78, 0x0e, 0x4f, 0x17,
0x61, 0x55, 0x32, 0xc7, 0x50, 0x88, 0x3e, 0x2f, 0x54, 0x5e, 0x76, 0xe6, 0x17, 0x59, 0xf8, 0x02,
0x33, 0xf3, 0xea, 0x5b, 0xe3, 0xf0, 0x15, 0x14, 0xa2, 0xf5, 0x89, 0x68, 0x50, 0xb8, 0xe8, 0xf5,
0xfa, 0x56, 0xef, 0x6a, 0xa8, 0x3f, 0x21, 0x25, 0xc8, 0xe3, 0xaf, 0x4e, 0x57, 0x4f, 0x1d, 0x86,
0x50, 0x8c, 0xb7, 0x27, 0x52, 0x86, 0x62, 0xa7, 0xdb, 0x19, 0x76, 0x9a, 0xc3, 0xf6, 0x89, 0xfe,
0x84, 0x3c, 0x83, 0xcd, 0xbe, 0xd9, 0xee, 0x5c, 0x36, 0xbf, 0x69, 0x5b, 0x66, 0xfb, 0x4d, 0xbb,
0x79, 0xd1, 0x3e, 0xd1, 0x53, 0x84, 0x40, 0xe5, 0x6c, 0x78, 0xd1, 0xb2, 0xfa, 0x57, 0xaf, 0x2f,
0x3a, 0x83, 0xb3, 0xf6, 0x89, 0x9e, 0x16, 0x3a, 0x07, 0x57, 0xad, 0x56, 0x7b, 0x30, 0xd0, 0x33,
0x04, 0x60, 0xe3, 0xb4, 0xd9, 0x11, 0xe4, 0x2c, 0xd9, 0x82, 0x6a, 0xa7, 0xfb, 0xa6, 0xd7, 0x69,
0xb5, 0xad, 0x41, 0x7b, 0x38, 0x14, 0x60, 0xee, 0xf0, 0x5f, 0x29, 0x28, 0x2f, 0x2c, 0x60, 0x64,
0x07, 0xb6, 0xc4, 0x91, 0x2b, 0x53, 0x58, 0x6a, 0x0e, 0x7a, 0x5d, 0xab, 0xdb, 0xeb, 0xb6, 0xf5,
0x27, 0xe4, 0x23, 0xd8, 0x59, 0x12, 0xf4, 0x4e, 0x4f, 0x5b, 0x67, 0x4d, 0x71, 0x79, 0x52, 0x87,
0xed, 0x25, 0xe1, 0xb0, 0x73, 0xd9, 0x16, 0x5e, 0xa6, 0xc9, 0x3e, 0xec, 0x2d, 0xc9, 0x06, 0xdf,
0xb6, 0xdb, 0xfd, 0x98, 0x91, 0x21, 0xaf, 0xe0, 0xc5, 0x12, 0xa3, 0xd3, 0x1d, 0x5c, 0x9d, 0x9e,
0x76, 0x5a, 0x9d, 0x76, 0x77, 0x68, 0xbd, 0x69, 0x5e, 0x5c, 0xb5, 0xf5, 0x2c, 0xd9, 0x83, 0xda,
0xb2, 0x91, 0xf6, 0x65, 0xbf, 0x67, 0x36, 0xcd, 0xb7, 0x7a, 0x8e, 0xbc, 0x84, 0xe7, 0x0f, 0x94,
0xb4, 0x7a, 0xa6, 0xd9, 0x6e, 0x0d, 0xad, 0xe6, 0x65, 0xef, 0xaa, 0x3b, 0xd4, 0x37, 0x0e, 0x1b,
0x62, 0xc9, 0x59, 0x7a, 0xe0, 0x22, 0x64, 0x57, 0xdd, 0x9f, 0x76, 0x7b, 0xdf, 0x76, 0xf5, 0x27,
0x22, 0xf2, 0xc3, 0x33, 0xb3, 0x3d, 0x38, 0xeb, 0x5d, 0x9c, 0xe8, 0xa9, 0xe3, 0xbf, 0x15, 0xe5,
0x82, 0xdd, 0xc2, 0xcf, 0x72, 0x62, 0x42, 0x5e, 0xa5, 0x99, 0xac, 0x4b, 0x7c, 0xfd, 0xd9, 0xc2,
0x92, 0x14, 0xbf, 0xb4, 0x9d, 0xdf, 0xfe, 0xf5, 0xef, 0xbf, 0x4b, 0x6f, 0x1a, 0x5a, 0xe3, 0xfd,
0x17, 0x0d, 0xc1, 0x68, 0x78, 0x33, 0xfe, 0x55, 0xea, 0x90, 0xf4, 0x60, 0x43, 0x7e, 0xaa, 0x91,
0xed, 0x05, 0x95, 0xf1, 0xb7, 0xdb, 0x3a, 0x8d, 0xdb, 0xa8, 0x51, 0x37, 0x4a, 0xb1, 0x46, 0xdb,
0x15, 0x0a, 0x7f, 0x04, 0x79, 0xf5, 0x99, 0x90, 0xb8, 0xe4, 0xe2, 0x87, 0x43, 0x7d, 0xd5, 0x26,
0xf7, 0xc3, 0x14, 0xf9, 0x39, 0x14, 0xe3, 0x25, 0x90, 0xec, 0x26, 0x6a, 0x6c, 0xb1, 0x3e, 0xea,
0xf5, 0x55, 0xa2, 0xc5, 0x6b, 0x91, 0x4a, 0x7c, 0x2d, 0x5c, 0x10, 0xc9, 0x95, 0xac, 0x03, 0xb1,
0x20, 0x92, 0xda, 0x82, 0xf9, 0xc4, 0xce, 0xb8, 0xf2, 0x62, 0x46, 0x1d, 0x55, 0x3e, 0x25, 0x64,
0x41, 0x65, 0xe3, 0x3b, 0x7b, 0xfc, 0x2b, 0xf2, 0x0b, 0xd0, 0x54, 0x02, 0x70, 0x8d, 0x23, 0xf3,
0x60, 0x25, 0x77, 0xcd, 0xfa, 0xdc, 0x99, 0xe5, 0x85, 0x6f, 0x85, 0x76, 0x6f, 0xc6, 0x1b, 0x1c,
0xb5, 0x5d, 0xc7, 0xda, 0x71, 0x3d, 0x48, 0x68, 0x4f, 0x2e, 0x5a, 0x8b, 0xda, 0x17, 0x16, 0x09,
0x63, 0x1f, 0xb5, 0xd7, 0x49, 0x6d, 0x41, 0xfb, 0x3b, 0xc1, 0x69, 0x7c, 0x47, 0x1d, 0x2e, 0x3c,
0xa8, 0x88, 0xe9, 0x80, 0x29, 0x7f, 0xd4, 0x87, 0x79, 0xd4, 0x96, 0xd6, 0x66, 0x63, 0x17, 0x8d,
0x6c, 0x91, 0xcd, 0xc4, 0x53, 0x88, 0x3d, 0x98, 0x6b, 0x7f, 0xd4, 0x87, 0xa4, 0xf6, 0x45, 0x17,
0x9e, 0xa3, 0xf6, 0x5d, 0xb2, 0x93, 0xd4, 0x9e, 0xf4, 0xe0, 0x2d, 0x94, 0x85, 0x8d, 0x68, 0x3f,
0x08, 0x13, 0x2f, 0x79, 0x61, 0x09, 0xa9, 0xef, 0x3c, 0xc0, 0x17, 0xab, 0x83, 0x54, 0xd1, 0x44,
0x48, 0x79, 0x43, 0x2e, 0x1e, 0x84, 0x03, 0x79, 0x38, 0x3a, 0x89, 0x11, 0xeb, 0x59, 0x3b, 0x57,
0xeb, 0x8f, 0x8e, 0x08, 0x63, 0x0f, 0x0d, 0x6e, 0x93, 0xa7, 0x68, 0x30, 0x22, 0x34, 0x7c, 0xa9,
0xff, 0xd7, 0x40, 0x06, 0x8f, 0x59, 0x5d, 0x3b, 0xac, 0xea, 0x2f, 0x1f, 0xe5, 0x2c, 0x06, 0xd4,
0x58, 0x69, 0x5c, 0x94, 0x30, 0x03, 0x2d, 0x39, 0x7f, 0xc8, 0xdc, 0x97, 0x15, 0xd3, 0xaa, 0xfe,
0xf1, 0x1a, 0xa9, 0xb2, 0x56, 0x43, 0x6b, 0x84, 0xe8, 0xc2, 0x1a, 0x9d, 0x71, 0xaf, 0x11, 0x4a,
0xda, 0xf5, 0x06, 0xfe, 0xff, 0xf0, 0xcb, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x65, 0x6d, 0xf3,
0xf6, 0x76, 0x14, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -1958,6 +2036,12 @@ type SwapClientClient interface {
//this call fully overwrites our existing parameters.
//[EXPERIMENTAL]: endpoint is subject to change.
SetLiquidityParams(ctx context.Context, in *SetLiquidityParamsRequest, opts ...grpc.CallOption) (*SetLiquidityParamsResponse, error)
//
//SuggestSwaps returns a list of recommended swaps based on the current
//state of your node's channels and it's liquidity manager parameters.
//Note that only loop out suggestions are currently supported.
//[EXPERIMENTAL]: endpoint is subject to change.
SuggestSwaps(ctx context.Context, in *SuggestSwapsRequest, opts ...grpc.CallOption) (*SuggestSwapsResponse, error)
}
type swapClientClient struct {
@ -2099,6 +2183,15 @@ func (c *swapClientClient) SetLiquidityParams(ctx context.Context, in *SetLiquid
return out, nil
}
func (c *swapClientClient) SuggestSwaps(ctx context.Context, in *SuggestSwapsRequest, opts ...grpc.CallOption) (*SuggestSwapsResponse, error) {
out := new(SuggestSwapsResponse)
err := c.cc.Invoke(ctx, "/looprpc.SwapClient/SuggestSwaps", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// SwapClientServer is the server API for SwapClient service.
type SwapClientServer interface {
//* loop: `out`
@ -2150,6 +2243,12 @@ type SwapClientServer interface {
//this call fully overwrites our existing parameters.
//[EXPERIMENTAL]: endpoint is subject to change.
SetLiquidityParams(context.Context, *SetLiquidityParamsRequest) (*SetLiquidityParamsResponse, error)
//
//SuggestSwaps returns a list of recommended swaps based on the current
//state of your node's channels and it's liquidity manager parameters.
//Note that only loop out suggestions are currently supported.
//[EXPERIMENTAL]: endpoint is subject to change.
SuggestSwaps(context.Context, *SuggestSwapsRequest) (*SuggestSwapsResponse, error)
}
// UnimplementedSwapClientServer can be embedded to have forward compatible implementations.
@ -2192,6 +2291,9 @@ func (*UnimplementedSwapClientServer) GetLiquidityParams(ctx context.Context, re
func (*UnimplementedSwapClientServer) SetLiquidityParams(ctx context.Context, req *SetLiquidityParamsRequest) (*SetLiquidityParamsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetLiquidityParams not implemented")
}
func (*UnimplementedSwapClientServer) SuggestSwaps(ctx context.Context, req *SuggestSwapsRequest) (*SuggestSwapsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SuggestSwaps not implemented")
}
func RegisterSwapClientServer(s *grpc.Server, srv SwapClientServer) {
s.RegisterService(&_SwapClient_serviceDesc, srv)
@ -2416,6 +2518,24 @@ func _SwapClient_SetLiquidityParams_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler)
}
func _SwapClient_SuggestSwaps_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SuggestSwapsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SwapClientServer).SuggestSwaps(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/looprpc.SwapClient/SuggestSwaps",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SwapClientServer).SuggestSwaps(ctx, req.(*SuggestSwapsRequest))
}
return interceptor(ctx, in, info, handler)
}
var _SwapClient_serviceDesc = grpc.ServiceDesc{
ServiceName: "looprpc.SwapClient",
HandlerType: (*SwapClientServer)(nil),
@ -2464,6 +2584,10 @@ var _SwapClient_serviceDesc = grpc.ServiceDesc{
MethodName: "SetLiquidityParams",
Handler: _SwapClient_SetLiquidityParams_Handler,
},
{
MethodName: "SuggestSwaps",
Handler: _SwapClient_SuggestSwaps_Handler,
},
},
Streams: []grpc.StreamDesc{
{

@ -415,6 +415,24 @@ func local_request_SwapClient_SetLiquidityParams_0(ctx context.Context, marshale
}
func request_SwapClient_SuggestSwaps_0(ctx context.Context, marshaler runtime.Marshaler, client SwapClientClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SuggestSwapsRequest
var metadata runtime.ServerMetadata
msg, err := client.SuggestSwaps(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_SwapClient_SuggestSwaps_0(ctx context.Context, marshaler runtime.Marshaler, server SwapClientServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq SuggestSwapsRequest
var metadata runtime.ServerMetadata
msg, err := server.SuggestSwaps(ctx, &protoReq)
return msg, metadata, err
}
// RegisterSwapClientHandlerServer registers the http handlers for service SwapClient to "mux".
// UnaryRPC :call SwapClientServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -640,6 +658,26 @@ func RegisterSwapClientHandlerServer(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_SwapClient_SuggestSwaps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_SwapClient_SuggestSwaps_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_SwapClient_SuggestSwaps_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -901,6 +939,26 @@ func RegisterSwapClientHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_SwapClient_SuggestSwaps_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_SwapClient_SuggestSwaps_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_SwapClient_SuggestSwaps_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -926,6 +984,8 @@ var (
pattern_SwapClient_GetLiquidityParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "liquidity", "params"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_SwapClient_SetLiquidityParams_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "liquidity", "params"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_SwapClient_SuggestSwaps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "auto", "suggest"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
@ -950,4 +1010,6 @@ var (
forward_SwapClient_GetLiquidityParams_0 = runtime.ForwardResponseMessage
forward_SwapClient_SetLiquidityParams_0 = runtime.ForwardResponseMessage
forward_SwapClient_SuggestSwaps_0 = runtime.ForwardResponseMessage
)

@ -128,6 +128,18 @@ service SwapClient {
body: "*"
};
}
/*
SuggestSwaps returns a list of recommended swaps based on the current
state of your node's channels and it's liquidity manager parameters.
Note that only loop out suggestions are currently supported.
[EXPERIMENTAL]: endpoint is subject to change.
*/
rpc SuggestSwaps(SuggestSwapsRequest) returns (SuggestSwapsResponse){
option (google.api.http) = {
get: "/v1/auto/suggest"
};
}
}
message LoopOutRequest {
@ -743,3 +755,14 @@ message SetLiquidityParamsRequest{
}
message SetLiquidityParamsResponse{}
message SuggestSwapsRequest{
}
message SuggestSwapsResponse{
/*
The set of recommended loop outs.
*/
repeated LoopOutRequest loop_out= 1;
}

@ -11,6 +11,29 @@
"application/json"
],
"paths": {
"/v1/auto/suggest": {
"get": {
"summary": "SuggestSwaps returns a list of recommended swaps based on the current\nstate of your node's channels and it's liquidity manager parameters.\nNote that only loop out suggestions are currently supported.\n[EXPERIMENTAL]: endpoint is subject to change.",
"operationId": "SuggestSwaps",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/looprpcSuggestSwapsResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"tags": [
"SwapClient"
]
}
},
"/v1/liquidity/params": {
"get": {
"summary": "GetLiquidityParams gets the parameters that the daemon's liquidity manager\nis currently configured with. This may be nil if nothing is configured.\n[EXPERIMENTAL]: endpoint is subject to change.",
@ -698,6 +721,18 @@
"looprpcSetLiquidityParamsResponse": {
"type": "object"
},
"looprpcSuggestSwapsResponse": {
"type": "object",
"properties": {
"loop_out": {
"type": "array",
"items": {
"$ref": "#/definitions/looprpcLoopOutRequest"
},
"description": "The set of recommended loop outs."
}
}
},
"looprpcSwapResponse": {
"type": "object",
"properties": {

Loading…
Cancel
Save