Merge pull request #419 from carlaKC/autoloop-4-loopin

liquidity: add loopin to autoloop
pull/450/head
Harsha Goli 2 years ago committed by GitHub
commit be6eae3bda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -48,6 +48,14 @@ var setLiquidityRuleCommand = cli.Command{
Description: "Update or remove the liquidity rule for a channel/peer.",
ArgsUsage: "{shortchanid | peerpubkey}",
Flags: []cli.Flag{
cli.StringFlag{
Name: "type",
Usage: "the type of swap to perform, set to 'out' " +
"for acquiring inbound liquidity or 'in' for " +
"acquiring outbound liquidity.",
Value: "out",
},
cli.IntFlag{
Name: "incoming_threshold",
Usage: "the minimum percentage of incoming liquidity " +
@ -168,7 +176,18 @@ func setRule(ctx *cli.Context) error {
newRule := &looprpc.LiquidityRule{
ChannelId: chanID,
Type: looprpc.LiquidityRuleType_THRESHOLD,
SwapType: looprpc.SwapType_LOOP_OUT,
}
if ctx.IsSet("type") {
switch ctx.String("type") {
case "in":
newRule.SwapType = looprpc.SwapType_LOOP_IN
case "out":
newRule.SwapType = looprpc.SwapType_LOOP_OUT
default:
return errors.New("please set type to in or out")
}
}
if pubkeyRule {
@ -292,6 +311,11 @@ var setParamsCommand = cli.Command{
Usage: "the maximum amount in satoshis that the " +
"autoloop client will dispatch per-swap",
},
cli.IntFlag{
Name: "htlc_conf",
Usage: "the confirmation target for loop in on-chain " +
"htlcs",
},
},
Action: setParams,
}
@ -422,6 +446,11 @@ func setParams(ctx *cli.Context) error {
flagSet = true
}
if ctx.IsSet("htlc_conf") {
params.HtlcConfTarget = int32(ctx.Int("htlc_conf"))
flagSet = true
}
if !flagSet {
return fmt.Errorf("at least one flag required to set params")
}

@ -9,6 +9,16 @@ following command:
loop setparams --autoloop=true
```
At present, autoloop can be configured to either acquire incoming liquidity
using loop out, or acquire outgoing liquidity using loop in. It cannot support
automated swaps in both directions. To set the type of swaps you would like
to automatically dispatch, use:
```
loop setparams --type={in|out}
```
Autoloop will perform loop out swaps *by default*.
Swaps that are dispatched by the autolooper can be identified in the output of
`ListSwaps` by their label field, which will contain: `[reserved]: autoloop-out`.
@ -286,7 +296,9 @@ following reasons will be displayed:
* Fee insufficient: if the fees that a swap will cost are more than the
percentage of total swap amount that we allow, this reason will be displayed.
See [fees](#fees) to update this value.
* Loop in unreachable: if the client node is unreachable by the server
off-chain, this reason will be displayed. Try improving the connectivity of
your node so that it is reachable by the loop server.
Further details for all of these reasons can be found in loopd's debug level
logs.

@ -50,13 +50,22 @@ func TestAutoLoopDisabled(t *testing.T) {
// loop in/out swaps. We expect a swap for our channel to be suggested,
// but do not expect any swaps to be executed, since autoloop is
// disabled by default.
c.autoloop(1, chan1Rec.Amount+1, nil, quotes, nil)
step := &autoloopStep{
minAmt: 1,
maxAmt: chan1Rec.Amount + 1,
quotesOut: quotes,
}
c.autoloop(step)
// Trigger another autoloop, this time setting our server restrictions
// to have a minimum swap amount greater than the amount that we need
// to swap. In this case we don't even expect to get a quote, because
// our suggested swap is beneath the minimum swap size.
c.autoloop(chan1Rec.Amount+1, chan1Rec.Amount+2, nil, nil, nil)
step = &autoloopStep{
minAmt: chan1Rec.Amount + 1,
maxAmt: chan1Rec.Amount + 2,
}
c.autoloop(step)
c.stop()
}
@ -99,6 +108,7 @@ func TestAutoLoopEnabled(t *testing.T) {
chanID1: chanRule,
chanID2: chanRule,
},
HtlcConfTarget: defaultHtlcConfTarget,
}
)
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
@ -191,7 +201,13 @@ func TestAutoLoopEnabled(t *testing.T) {
// Tick our autolooper with no existing swaps, we expect a loop out
// swap to be dispatched for each channel.
c.autoloop(1, amt+1, nil, quotes, loopOuts)
step := &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
quotesOut: quotes,
expectedOut: loopOuts,
}
c.autoloop(step)
// Tick again with both of our swaps in progress. We haven't shifted our
// channel balances at all, so swaps should still be suggested, but we
@ -201,7 +217,12 @@ func TestAutoLoopEnabled(t *testing.T) {
existingSwapFromRequest(chan2Swap, testTime, nil),
}
c.autoloop(1, amt+1, existing, nil, nil)
step = &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
existingOut: existing,
}
c.autoloop(step)
// Now, we update our channel 2 swap to have failed due to off chain
// failure and our first swap to have succeeded.
@ -254,7 +275,14 @@ func TestAutoLoopEnabled(t *testing.T) {
// We tick again, this time we expect another swap on channel 1 (which
// still has balances which reflect that we need to swap), but nothing
// for channel 2, since it has had a failure.
c.autoloop(1, amt+1, existing, quotes, loopOuts)
step = &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
existingOut: existing,
quotesOut: quotes,
expectedOut: loopOuts,
}
c.autoloop(step)
// Now, we progress our time so that we have sufficiently backed off
// for channel 2, and could perform another swap.
@ -268,7 +296,13 @@ func TestAutoLoopEnabled(t *testing.T) {
existingSwapFromRequest(chan2Swap, testTime, failedOffChain),
}
c.autoloop(1, amt+1, existing, quotes, nil)
step = &autoloopStep{
minAmt: 1,
maxAmt: amt + 1,
existingOut: existing,
quotesOut: quotes,
}
c.autoloop(step)
c.stop()
}
@ -318,6 +352,7 @@ func TestCompositeRules(t *testing.T) {
PeerRules: map[route.Vertex]*SwapRule{
peer2: chanRule,
},
HtlcConfTarget: defaultHtlcConfTarget,
}
)
@ -425,8 +460,357 @@ func TestCompositeRules(t *testing.T) {
// swap to be dispatched for each of our rules. We set our server side
// maximum to be greater than the swap amount for our peer swap (which
// is the larger of the two swaps).
c.autoloop(1, peerAmount+1, nil, quotes, loopOuts)
step := &autoloopStep{
minAmt: 1,
maxAmt: peerAmount + 1,
quotesOut: quotes,
expectedOut: loopOuts,
}
c.autoloop(step)
c.stop()
}
// TestAutoLoopInEnabled tests dispatch of autoloop in swaps.
func TestAutoLoopInEnabled(t *testing.T) {
defer test.Guard(t)()
var (
chan1 = lndclient.ChannelInfo{
ChannelID: chanID1.ToUint64(),
PubKeyBytes: peer1,
Capacity: 100000,
RemoteBalance: 100000,
LocalBalance: 0,
}
chan2 = lndclient.ChannelInfo{
ChannelID: chanID2.ToUint64(),
PubKeyBytes: peer2,
Capacity: 200000,
RemoteBalance: 200000,
LocalBalance: 0,
}
channels = []lndclient.ChannelInfo{
chan1, chan2,
}
// Create a rule which will loop in, with no inbound liquidity
// reserve.
rule = &SwapRule{
ThresholdRule: NewThresholdRule(0, 60),
Type: swap.TypeIn,
}
// Under these rules, we'll have the following recommended
// swaps:
peer1ExpectedAmt btcutil.Amount = 80000
peer2ExpectedAmt btcutil.Amount = 160000
// Set our per-swap budget to 5% of swap amount.
swapFeePPM uint64 = 50000
htlcConfTarget int32 = 10
// Calculate the maximum amount we'll pay for each swap and
// set our budget to be able to accommodate both.
peer1MaxFee = ppmToSat(peer1ExpectedAmt, swapFeePPM)
peer2MaxFee = ppmToSat(peer2ExpectedAmt, swapFeePPM)
params = Parameters{
Autoloop: true,
AutoFeeBudget: peer1MaxFee + peer2MaxFee + 1,
AutoFeeStartDate: testTime,
MaxAutoInFlight: 2,
FailureBackOff: time.Hour,
FeeLimit: NewFeePortion(swapFeePPM),
ChannelRules: make(map[lnwire.ShortChannelID]*SwapRule),
PeerRules: map[route.Vertex]*SwapRule{
peer1: rule,
peer2: rule,
},
HtlcConfTarget: htlcConfTarget,
SweepConfTarget: loop.DefaultSweepConfTarget,
}
)
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
c.start()
// Calculate our maximum allowed fees and create quotes that fall within
// our budget.
var (
quote1 = &loop.LoopInQuote{
SwapFee: peer1MaxFee / 4,
MinerFee: peer1MaxFee / 8,
}
quote2Unaffordable = &loop.LoopInQuote{
SwapFee: peer2MaxFee * 2,
MinerFee: peer2MaxFee * 2,
}
quoteRequest1 = &loop.LoopInQuoteRequest{
Amount: peer1ExpectedAmt,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer1,
}
quoteRequest2 = &loop.LoopInQuoteRequest{
Amount: peer2ExpectedAmt,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer2,
}
peer1Swap = &loop.LoopInRequest{
Amount: peer1ExpectedAmt,
MaxSwapFee: quote1.SwapFee,
MaxMinerFee: quote1.MinerFee,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer1,
ExternalHtlc: false,
Label: labels.AutoloopLabel(swap.TypeIn),
Initiator: autoloopSwapInitiator,
}
)
// Tick our autolooper with no existing swaps. Both of our peers
// require swaps, but one of our peer's quotes is too expensive.
step := &autoloopStep{
minAmt: 1,
maxAmt: peer2ExpectedAmt + 1,
quotesIn: []quoteInRequestResp{
{
request: quoteRequest1,
quote: quote1,
},
{
request: quoteRequest2,
quote: quote2Unaffordable,
},
},
expectedIn: []loopInRequestResp{
{
request: peer1Swap,
response: &loop.LoopInSwapInfo{
SwapHash: lntypes.Hash{1},
},
},
},
}
c.autoloop(step)
// Now, we tick again with our first swap in progress. This time, we
// provide a quote for our second swap which is more affordable, so we
// expect it to be dispatched.
var (
quote2Affordable = &loop.LoopInQuote{
SwapFee: peer2MaxFee / 8,
MinerFee: peer2MaxFee / 2,
}
peer2Swap = &loop.LoopInRequest{
Amount: peer2ExpectedAmt,
MaxSwapFee: quote2Affordable.SwapFee,
MaxMinerFee: quote2Affordable.MinerFee,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer2,
ExternalHtlc: false,
Label: labels.AutoloopLabel(swap.TypeIn),
Initiator: autoloopSwapInitiator,
}
existing = []*loopdb.LoopIn{
existingInFromRequest(peer1Swap, testTime, nil),
}
)
step = &autoloopStep{
minAmt: 1,
maxAmt: peer2ExpectedAmt + 1,
quotesIn: []quoteInRequestResp{
{
request: quoteRequest2,
quote: quote2Affordable,
},
},
existingIn: existing,
expectedIn: []loopInRequestResp{
{
request: peer2Swap,
response: &loop.LoopInSwapInfo{
SwapHash: lntypes.Hash{2},
},
},
},
}
c.autoloop(step)
c.stop()
}
// TestAutoloopBothTypes tests dispatching of a loop out and loop in swap at the
// same time.
func TestAutoloopBothTypes(t *testing.T) {
defer test.Guard(t)()
var (
chan1 = lndclient.ChannelInfo{
ChannelID: chanID1.ToUint64(),
PubKeyBytes: peer1,
Capacity: 1000000,
LocalBalance: 1000000,
}
chan2 = lndclient.ChannelInfo{
ChannelID: chanID2.ToUint64(),
PubKeyBytes: peer2,
Capacity: 200000,
RemoteBalance: 200000,
LocalBalance: 0,
}
channels = []lndclient.ChannelInfo{
chan1, chan2,
}
// Create a rule which will loop out, with no outbound liquidity
// reserve.
outRule = &SwapRule{
ThresholdRule: NewThresholdRule(40, 0),
Type: swap.TypeOut,
}
// Create a rule which will loop in, with no inbound liquidity
// reserve.
inRule = &SwapRule{
ThresholdRule: NewThresholdRule(0, 60),
Type: swap.TypeIn,
}
// Under this rule, we expect a loop in swap.
loopOutAmt btcutil.Amount = 700000
loopInAmount btcutil.Amount = 160000
// Set our per-swap budget to 5% of swap amount.
swapFeePPM uint64 = 50000
htlcConfTarget int32 = 10
// Calculate the maximum amount we'll pay for our loop in.
loopOutMaxFee = ppmToSat(loopOutAmt, swapFeePPM)
loopInMaxFee = ppmToSat(loopInAmount, swapFeePPM)
params = Parameters{
Autoloop: true,
AutoFeeBudget: loopOutMaxFee + loopInMaxFee + 1,
AutoFeeStartDate: testTime,
MaxAutoInFlight: 2,
FailureBackOff: time.Hour,
FeeLimit: NewFeePortion(swapFeePPM),
ChannelRules: map[lnwire.ShortChannelID]*SwapRule{
chanID1: outRule,
},
PeerRules: map[route.Vertex]*SwapRule{
peer2: inRule,
},
HtlcConfTarget: htlcConfTarget,
SweepConfTarget: loop.DefaultSweepConfTarget,
}
)
c := newAutoloopTestCtx(t, params, channels, testRestrictions)
c.start()
// Calculate our maximum allowed fees and create quotes that fall within
// our budget.
var (
loopOutQuote = &loop.LoopOutQuote{
SwapFee: loopOutMaxFee / 4,
PrepayAmount: loopOutMaxFee / 4,
}
loopOutQuoteReq = &loop.LoopOutQuoteRequest{
Amount: loopOutAmt,
SweepConfTarget: params.SweepConfTarget,
SwapPublicationDeadline: testTime,
}
prepayMaxFee, routeMaxFee,
minerFee = params.FeeLimit.loopOutFees(
loopOutAmt, loopOutQuote,
)
loopOutSwap = &loop.OutRequest{
Amount: loopOutAmt,
MaxSwapRoutingFee: routeMaxFee,
MaxPrepayRoutingFee: prepayMaxFee,
MaxSwapFee: loopOutQuote.SwapFee,
MaxPrepayAmount: loopOutQuote.PrepayAmount,
MaxMinerFee: minerFee,
SweepConfTarget: params.SweepConfTarget,
OutgoingChanSet: loopdb.ChannelSet{
chanID1.ToUint64(),
},
Label: labels.AutoloopLabel(swap.TypeOut),
Initiator: autoloopSwapInitiator,
}
loopinQuote = &loop.LoopInQuote{
SwapFee: loopInMaxFee / 4,
MinerFee: loopInMaxFee / 8,
}
loopInQuoteReq = &loop.LoopInQuoteRequest{
Amount: loopInAmount,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer2,
}
loopInSwap = &loop.LoopInRequest{
Amount: loopInAmount,
MaxSwapFee: loopinQuote.SwapFee,
MaxMinerFee: loopinQuote.MinerFee,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer2,
ExternalHtlc: false,
Label: labels.AutoloopLabel(swap.TypeIn),
Initiator: autoloopSwapInitiator,
}
)
step := &autoloopStep{
minAmt: 1,
maxAmt: loopOutAmt + 1,
quotesOut: []quoteRequestResp{
{
request: loopOutQuoteReq,
quote: loopOutQuote,
},
},
quotesIn: []quoteInRequestResp{
{
request: loopInQuoteReq,
quote: loopinQuote,
},
},
expectedOut: []loopOutRequestResp{
{
request: loopOutSwap,
response: &loop.LoopOutSwapInfo{
SwapHash: lntypes.Hash{1},
},
},
},
expectedIn: []loopInRequestResp{
{
request: loopInSwap,
response: &loop.LoopInSwapInfo{
SwapHash: lntypes.Hash{2},
},
},
},
}
c.autoloop(step)
c.stop()
}
@ -455,3 +839,24 @@ func existingSwapFromRequest(request *loop.OutRequest, initTime time.Time,
},
}
}
func existingInFromRequest(in *loop.LoopInRequest, initTime time.Time,
events []*loopdb.LoopEvent) *loopdb.LoopIn {
return &loopdb.LoopIn{
Loop: loopdb.Loop{
Events: events,
},
Contract: &loopdb.LoopInContract{
SwapContract: loopdb.SwapContract{
MaxSwapFee: in.MaxSwapFee,
MaxMinerFee: in.MaxMinerFee,
InitiationTime: initTime,
Label: in.Label,
},
HtlcConfTarget: in.HtlcConfTarget,
LastHop: in.LastHop,
ExternalHtlc: in.ExternalHtlc,
},
}
}

@ -27,10 +27,21 @@ type autoloopTestCtx struct {
// quotes is a channel that we get loop out quote requests on.
quotes chan *loop.LoopOutQuote
// quoteRequestIn is a channel that requests for loop in quotes are
// pushed into.
quoteRequestIn chan *loop.LoopInQuoteRequest
// quotesIn is a channel that we get loop in quote responses on.
quotesIn chan *loop.LoopInQuote
// loopOutRestrictions is a channel that we get the server's
// restrictions on.
loopOutRestrictions chan *Restrictions
// loopInRestrictions is a channel that we get the server's
// loop in restrictions on.
loopInRestrictions chan *Restrictions
// loopOuts is a channel that we get existing loop out swaps on.
loopOuts chan []*loopdb.LoopOut
@ -47,6 +58,13 @@ type autoloopTestCtx struct {
// loopOut is a channel that we return loop out responses on.
loopOut chan *loop.LoopOutSwapInfo
// inRequest is a channel that requests to dispatch loop in swaps are
// pushed into.
inRequest chan *loop.LoopInRequest
// loopIn is a channel that we return loop in responses on.
loopIn chan *loop.LoopInSwapInfo
// errChan is a channel that we send run errors into.
errChan chan error
@ -80,14 +98,18 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
quoteRequest: make(chan *loop.LoopOutQuoteRequest),
quotes: make(chan *loop.LoopOutQuote),
quoteRequestIn: make(chan *loop.LoopInQuoteRequest),
quotesIn: make(chan *loop.LoopInQuote),
loopOutRestrictions: make(chan *Restrictions),
loopInRestrictions: make(chan *Restrictions),
loopOuts: make(chan []*loopdb.LoopOut),
loopIns: make(chan []*loopdb.LoopIn),
restrictions: make(chan *Restrictions),
outRequest: make(chan *loop.OutRequest),
loopOut: make(chan *loop.LoopOutSwapInfo),
errChan: make(chan error, 1),
inRequest: make(chan *loop.LoopInRequest),
loopIn: make(chan *loop.LoopInSwapInfo),
errChan: make(chan error, 1),
}
// Set lnd's channels to equal the set of channels we want for our
@ -96,10 +118,14 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
cfg := &Config{
AutoloopTicker: ticker.NewForce(DefaultAutoloopTicker),
Restrictions: func(context.Context, swap.Type) (*Restrictions,
Restrictions: func(_ context.Context, swapType swap.Type) (*Restrictions,
error) {
return <-testCtx.loopOutRestrictions, nil
if swapType == swap.TypeOut {
return <-testCtx.loopOutRestrictions, nil
}
return <-testCtx.loopInRestrictions, nil
},
ListLoopOut: func() ([]*loopdb.LoopOut, error) {
return <-testCtx.loopOuts, nil
@ -123,6 +149,20 @@ func newAutoloopTestCtx(t *testing.T, parameters Parameters,
return <-testCtx.loopOut, nil
},
LoopInQuote: func(_ context.Context,
req *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error) {
testCtx.quoteRequestIn <- req
return <-testCtx.quotesIn, nil
},
LoopIn: func(_ context.Context,
req *loop.LoopInRequest) (*loop.LoopInSwapInfo, error) {
testCtx.inRequest <- req
return <-testCtx.loopIn, nil
},
MinimumConfirmations: loop.DefaultSweepConfTarget,
Lnd: &testCtx.lnd.LndServices,
Clock: testCtx.testClock,
@ -177,31 +217,70 @@ type loopOutRequestResp struct {
response *loop.LoopOutSwapInfo
}
// quoteInRequestResp pairs an expected loop in quote request with the response
// we would like to provide the manager with.
type quoteInRequestResp struct {
request *loop.LoopInQuoteRequest
quote *loop.LoopInQuote
}
// loopInRequestResp pairs and expected loop in request with the response we
// would like the mocked server to respond with.
type loopInRequestResp struct {
request *loop.LoopInRequest
response *loop.LoopInSwapInfo
}
// autoloopStep contains all of the information to required to step
// through an autoloop tick.
type autoloopStep struct {
minAmt btcutil.Amount
maxAmt btcutil.Amount
existingOut []*loopdb.LoopOut
existingIn []*loopdb.LoopIn
quotesOut []quoteRequestResp
quotesIn []quoteInRequestResp
expectedOut []loopOutRequestResp
expectedIn []loopInRequestResp
}
// autoloop walks our test context through the process of triggering our
// autoloop functionality, providing mocked values as required. The set of
// quotes provided indicates that we expect swap suggestions to be made (since
// we will query for a quote for each suggested swap). The set of expected
// swaps indicates whether we expect any of these swap suggestions to actually
// be dispatched by the autolooper.
func (c *autoloopTestCtx) autoloop(minAmt, maxAmt btcutil.Amount,
existingOut []*loopdb.LoopOut, quotes []quoteRequestResp,
expectedSwaps []loopOutRequestResp) {
func (c *autoloopTestCtx) autoloop(step *autoloopStep) {
// Tick our autoloop ticker to force assessing whether we want to loop.
c.manager.cfg.AutoloopTicker.Force <- testTime
// Send a mocked response from the server with the swap size limits.
c.loopOutRestrictions <- NewRestrictions(minAmt, maxAmt)
c.loopOutRestrictions <- NewRestrictions(step.minAmt, step.maxAmt)
c.loopInRestrictions <- NewRestrictions(step.minAmt, step.maxAmt)
// Provide the liquidity manager with our desired existing set of swaps.
c.loopOuts <- existingOut
c.loopIns <- nil
c.loopOuts <- step.existingOut
c.loopIns <- step.existingIn
// Assert that we query the server for a quote for each of our
// recommended swaps. Note that this differs from our set of expected
// swaps because we may get quotes for suggested swaps but then just
// log them.
for _, expected := range quotes {
for _, expected := range step.quotesIn {
request := <-c.quoteRequestIn
assert.Equal(
c.t, expected.request.Amount, request.Amount,
)
assert.Equal(
c.t, expected.request.HtlcConfTarget,
request.HtlcConfTarget,
)
c.quotesIn <- expected.quote
}
for _, expected := range step.quotesOut {
request := <-c.quoteRequest
assert.Equal(
c.t, expected.request.Amount, request.Amount,
@ -214,7 +293,7 @@ func (c *autoloopTestCtx) autoloop(minAmt, maxAmt btcutil.Amount,
}
// Assert that we dispatch the expected set of swaps.
for _, expected := range expectedSwaps {
for _, expected := range step.expectedOut {
actual := <-c.outRequest
// Set our destination address to nil so that we do not need to
@ -224,4 +303,12 @@ func (c *autoloopTestCtx) autoloop(minAmt, maxAmt btcutil.Amount,
assert.Equal(c.t, expected.request, actual)
c.loopOut <- expected.response
}
for _, expected := range step.expectedIn {
actual := <-c.inRequest
assert.Equal(c.t, expected.request, actual)
c.loopIn <- expected.response
}
}

@ -90,6 +90,10 @@ const (
)
var (
// defaultHtlcConfTarget is the default confirmation target we use for
// loop in swap htlcs, set to the same default at the client.
defaultHtlcConfTarget = loop.DefaultHtlcConfTarget
// defaultBudget is the default autoloop budget we set. This budget will
// only be used for automatically dispatched swaps if autoloop is
// explicitly enabled, so we are happy to set a non-zero value here. The
@ -107,6 +111,7 @@ var (
PeerRules: make(map[route.Vertex]*SwapRule),
FailureBackOff: defaultFailureBackoff,
SweepConfTarget: defaultConfTarget,
HtlcConfTarget: defaultHtlcConfTarget,
FeeLimit: defaultFeePortion(),
}
@ -170,10 +175,18 @@ type Config struct {
LoopOutQuote func(ctx context.Context,
request *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, error)
// LoopInQuote provides a quote for a loop in swap.
LoopInQuote func(ctx context.Context,
request *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error)
// LoopOut dispatches a loop out.
LoopOut func(ctx context.Context, request *loop.OutRequest) (
*loop.LoopOutSwapInfo, error)
// LoopIn dispatches a loop in swap.
LoopIn func(ctx context.Context,
request *loop.LoopInRequest) (*loop.LoopInSwapInfo, error)
// Clock allows easy mocking of time in unit tests.
Clock clock.Clock
@ -212,6 +225,10 @@ type Parameters struct {
// transaction in. This value affects the on chain fees we will pay.
SweepConfTarget int32
// HtlcConfTarget is the confirmation target that we use for publishing
// loop in swap htlcs on chain.
HtlcConfTarget int32
// FeeLimit controls the fee limit we place on swaps.
FeeLimit FeeLimit
@ -249,10 +266,11 @@ func (p Parameters) String() string {
}
return fmt.Sprintf("rules: %v, failure backoff: %v, sweep "+
"sweep conf target: %v, fees: %v, auto budget: %v, budget "+
"start: %v, max auto in flight: %v, minimum swap size=%v, "+
"maximum swap size=%v", strings.Join(ruleList, ","),
p.FailureBackOff, p.SweepConfTarget, p.FeeLimit,
"sweep conf target: %v, htlc conf target: %v,fees: %v, "+
"auto budget: %v, budget start: %v, max auto in flight: %v, "+
"minimum swap size=%v, maximum swap size=%v",
strings.Join(ruleList, ","), p.FailureBackOff,
p.SweepConfTarget, p.HtlcConfTarget, p.FeeLimit,
p.AutoFeeBudget, p.AutoFeeStartDate, p.MaxAutoInFlight,
p.ClientRestrictions.Minimum, p.ClientRestrictions.Maximum)
}
@ -310,6 +328,11 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo,
return ErrZeroChannelID
}
if rule.Type == swap.TypeIn {
return errors.New("channel level rules not supported for " +
"loop in swaps, only peer-level rules allowed")
}
if err := rule.validate(); err != nil {
return fmt.Errorf("channel: %v has invalid rule: %v",
channel.ToUint64(), err)
@ -329,6 +352,10 @@ func (p Parameters) validate(minConfs int32, openChans []lndclient.ChannelInfo,
minConfs)
}
if p.HtlcConfTarget < 1 {
return fmt.Errorf("htlc confirmation target must be > 0")
}
if err := p.FeeLimit.validate(); err != nil {
return err
}
@ -508,7 +535,7 @@ func (m *Manager) autoloop(ctx context.Context) error {
// If we don't actually have dispatch of swaps enabled, log
// suggestions.
if !m.params.Autoloop {
log.Debugf("recommended autoloop: %v sats over "+
log.Debugf("recommended autoloop out: %v sats over "+
"%v", swap.Amount, swap.OutgoingChanSet)
continue
@ -526,6 +553,27 @@ func (m *Manager) autoloop(ctx context.Context) error {
loopOut.HtlcAddressP2WSH)
}
for _, in := range suggestion.InSwaps {
// If we don't actually have dispatch of swaps enabled, log
// suggestions.
if !m.params.Autoloop {
log.Debugf("recommended autoloop in: %v sats over "+
"%v", in.Amount, in.LastHop)
continue
}
in := in
loopIn, err := m.cfg.LoopIn(ctx, &in)
if err != nil {
return err
}
log.Infof("loop in automatically dispatched: hash: %v, "+
"address: %v", loopIn.SwapHash,
loopIn.HtlcAddressNP2WSH)
}
return nil
}
@ -546,6 +594,9 @@ type Suggestions struct {
// OutSwaps is the set of loop out swaps that we suggest executing.
OutSwaps []loop.OutRequest
// InSwaps is the set of loop in swaps that we suggest executing.
InSwaps []loop.LoopInRequest
// DisqualifiedChans maps the set of channels that we do not recommend
// swaps on to the reason that we did not recommend a swap.
DisqualifiedChans map[lnwire.ShortChannelID]Reason
@ -563,13 +614,17 @@ func newSuggestions() *Suggestions {
}
func (s *Suggestions) addSwap(swap swapSuggestion) error {
out, ok := swap.(*loopOutSwapSuggestion)
if !ok {
switch t := swap.(type) {
case *loopOutSwapSuggestion:
s.OutSwaps = append(s.OutSwaps, t.OutRequest)
case *loopInSwapSuggestion:
s.InSwaps = append(s.InSwaps, t.LoopInRequest)
default:
return fmt.Errorf("unexpected swap type: %T", swap)
}
s.OutSwaps = append(s.OutSwaps, out.OutRequest)
return nil
}
@ -624,6 +679,11 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
return nil, err
}
inRestrictions, err := m.getSwapRestrictions(ctx, swap.TypeIn)
if err != nil {
return nil, err
}
// List our current set of swaps so that we can determine which channels
// are already being utilized by swaps. Note that these calls may race
// with manual initiation of swaps.
@ -708,7 +768,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
suggestion, err := m.suggestSwap(
ctx, traffic, balances, rule, outRestrictions,
autoloop,
inRestrictions, autoloop,
)
var reasonErr *reasonError
if errors.As(err, &reasonErr) {
@ -734,7 +794,7 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
suggestion, err := m.suggestSwap(
ctx, traffic, balance, rule, outRestrictions,
autoloop,
inRestrictions, autoloop,
)
var reasonErr *reasonError
@ -830,18 +890,24 @@ func (m *Manager) SuggestSwaps(ctx context.Context, autoloop bool) (
// swap request for the rule provided.
func (m *Manager) suggestSwap(ctx context.Context, traffic *swapTraffic,
balance *balances, rule *SwapRule, outRestrictions *Restrictions,
autoloop bool) (swapSuggestion, error) {
inRestrictions *Restrictions, autoloop bool) (swapSuggestion, error) {
var (
builder swapBuilder
restrictions *Restrictions
)
// Get an appropriate builder and set of restrictions based on our swap
// type.
switch rule.Type {
case swap.TypeOut:
builder = newLoopOutBuilder(m.cfg)
restrictions = outRestrictions
case swap.TypeIn:
builder = newLoopInBuilder(m.cfg)
restrictions = inRestrictions
default:
return nil, fmt.Errorf("unsupported swap type: %v", rule.Type)
}
@ -863,7 +929,7 @@ func (m *Manager) suggestSwap(ctx context.Context, traffic *swapTraffic,
// Next, get the amount that we need to swap for this entity, skipping
// over it if no change in liquidity is required.
amount := rule.swapAmount(balance, restrictions)
amount := rule.swapAmount(balance, restrictions, rule.Type)
if amount == 0 {
return nil, newReasonError(ReasonLiquidityOk)
}

@ -16,6 +16,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
@ -1292,6 +1293,19 @@ func TestInFlightLimit(t *testing.T) {
}
}
type mockServer struct {
mock.Mock
}
// Restrictions mocks a call to the server to get swap size restrictions.
func (m *mockServer) Restrictions(ctx context.Context, swapType swap.Type) (
*Restrictions, error) {
args := m.Called(ctx, swapType)
return args.Get(0).(*Restrictions), args.Error(1)
}
// TestSizeRestrictions tests the use of client-set size restrictions on swaps.
func TestSizeRestrictions(t *testing.T) {
var (
@ -1321,9 +1335,7 @@ func TestSizeRestrictions(t *testing.T) {
// has configured.
clientRestrictions Restrictions
// server holds the server's mocked responses to our terms
// endpoint.
serverRestrictions []Restrictions
prepareMock func(m *mockServer)
// suggestions is the set of suggestions we expect.
suggestions *Suggestions
@ -1336,9 +1348,6 @@ func TestSizeRestrictions(t *testing.T) {
clientRestrictions: Restrictions{
Minimum: 7000,
},
serverRestrictions: []Restrictions{
serverRestrictions, serverRestrictions,
},
suggestions: &Suggestions{
OutSwaps: []loop.OutRequest{
chan1Rec,
@ -1352,9 +1361,6 @@ func TestSizeRestrictions(t *testing.T) {
clientRestrictions: Restrictions{
Minimum: 8000,
},
serverRestrictions: []Restrictions{
serverRestrictions, serverRestrictions,
},
suggestions: &Suggestions{
DisqualifiedChans: map[lnwire.ShortChannelID]Reason{
chanID1: ReasonLiquidityOk,
@ -1367,9 +1373,6 @@ func TestSizeRestrictions(t *testing.T) {
clientRestrictions: Restrictions{
Maximum: 7000,
},
serverRestrictions: []Restrictions{
serverRestrictions, serverRestrictions,
},
suggestions: &Suggestions{
OutSwaps: []loop.OutRequest{
outSwap,
@ -1387,12 +1390,26 @@ func TestSizeRestrictions(t *testing.T) {
Minimum: 6500,
Maximum: 9000,
},
serverRestrictions: []Restrictions{
serverRestrictions,
{
Minimum: 5000,
Maximum: 6000,
},
prepareMock: func(m *mockServer) {
restrictions := serverRestrictions
m.On(
"Restrictions", mock.Anything,
swap.TypeOut,
).Return(
&restrictions, nil,
).Once()
m.On(
"Restrictions", mock.Anything,
swap.TypeOut,
).Return(
&Restrictions{
Minimum: 5000,
Maximum: 6000,
}, nil,
).Once()
},
suggestions: nil,
expectedError: ErrMaxExceedsServer,
@ -1415,27 +1432,32 @@ func TestSizeRestrictions(t *testing.T) {
chanID1: chanRule,
}
// callCount tracks the number of calls we make to
// our restrictions endpoint.
var callCount int
cfg.Restrictions = func(_ context.Context, _ swap.Type) (
*Restrictions, error) {
// Use a mock that has our expected calls for the test
// case set to provide server restrictions.
mockServer := &mockServer{}
restrictions := testCase.serverRestrictions[callCount]
callCount++
// If the test wants us to prime the mock, use its
// function, otherwise just return our default
// restrictions.
if testCase.prepareMock != nil {
testCase.prepareMock(mockServer)
} else {
restrictions := serverRestrictions
return &restrictions, nil
mockServer.On(
"Restrictions", mock.Anything,
mock.Anything,
).Return(&restrictions, nil)
}
cfg.Restrictions = mockServer.Restrictions
testSuggestSwaps(
t, newSuggestSwapsSetup(cfg, lnd, params),
testCase.suggestions, testCase.expectedError,
)
require.Equal(
t, callCount, len(testCase.serverRestrictions),
"too many restrictions provided by mock",
)
mockServer.AssertExpectations(t)
})
}
}

@ -0,0 +1,126 @@
package liquidity
import (
"context"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/loop"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// Compile-time assertion that loopInBuilder satisfies the swapBuilder
// interface.
var _ swapBuilder = (*loopInBuilder)(nil)
func newLoopInBuilder(cfg *Config) *loopInBuilder {
return &loopInBuilder{
cfg: cfg,
}
}
type loopInBuilder struct {
// cfg contains all the external functionality we require to create
// swaps.
cfg *Config
}
// swapType returns the swap type that the builder is responsible for creating.
func (b *loopInBuilder) swapType() swap.Type {
return swap.TypeIn
}
// maySwap checks whether we can currently execute a swap, examining the
// current on-chain fee conditions against relevant to our swap type against
// our fee restrictions.
//
// For loop in, we cannot check any upfront costs because we do not know how
// many inputs will be used for our on-chain htlc before it is made, so we can't
// make nay estimations.
func (b *loopInBuilder) maySwap(_ context.Context, _ Parameters) error {
return nil
}
// inUse examines our current swap traffic to determine whether we should
// suggest the builder's type of swap for the peer and channels suggested.
func (b *loopInBuilder) inUse(traffic *swapTraffic, peer route.Vertex,
channels []lnwire.ShortChannelID) error {
for _, chanID := range channels {
if traffic.ongoingLoopOut[chanID] {
log.Debugf("Channel: %v not eligible for suggestions, "+
"ongoing loop out utilizing channel", chanID)
return newReasonError(ReasonLoopOut)
}
}
if traffic.ongoingLoopIn[peer] {
log.Debugf("Peer: %x not eligible for suggestions ongoing "+
"loop in utilizing peer", peer)
return newReasonError(ReasonLoopIn)
}
lastFail, recentFail := traffic.failedLoopIn[peer]
if recentFail {
log.Debugf("Peer: %v not eligible for suggestions, "+
"was part of a failed swap at: %v", peer,
lastFail)
return newReasonError(ReasonFailureBackoff)
}
return nil
}
// buildSwap creates a swap for the target peer/channels provided. The autoloop
// boolean indicates whether this swap will actually be executed.
//
// For loop in, we do not add the autoloop label for dry runs.
func (b *loopInBuilder) buildSwap(ctx context.Context, pubkey route.Vertex,
_ []lnwire.ShortChannelID, amount btcutil.Amount,
autoloop bool, params Parameters) (swapSuggestion, error) {
quote, err := b.cfg.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
Amount: amount,
LastHop: &pubkey,
HtlcConfTarget: params.HtlcConfTarget,
})
if err != nil {
// If the server fails our quote, we're not reachable right
// now, so we want to catch this error and fail with a
// structured error so that we know why we can't swap.
status, ok := status.FromError(err)
if ok && status.Code() == codes.FailedPrecondition {
return nil, newReasonError(ReasonLoopInUnreachable)
}
return nil, err
}
if err := params.FeeLimit.loopInLimits(amount, quote); err != nil {
return nil, err
}
request := loop.LoopInRequest{
Amount: amount,
MaxSwapFee: quote.SwapFee,
MaxMinerFee: quote.MinerFee,
HtlcConfTarget: params.HtlcConfTarget,
LastHop: &pubkey,
Initiator: autoloopSwapInitiator,
}
if autoloop {
request.Label = labels.AutoloopLabel(swap.TypeIn)
}
return &loopInSwapSuggestion{
LoopInRequest: request,
}, nil
}

@ -0,0 +1,193 @@
package liquidity
import (
"context"
"errors"
"testing"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/loop"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// TestLoopinInUse tests that the loop in swap builder prevents dispatching
// swaps for peers when there is already a swap running for that peer.
func TestLoopinInUse(t *testing.T) {
var (
peer1 = route.Vertex{1}
chan1 = lnwire.NewShortChanIDFromInt(1)
peer2 = route.Vertex{2}
chan2 = lnwire.NewShortChanIDFromInt(2)
)
tests := []struct {
name string
ongoingLoopOut *lnwire.ShortChannelID
ongoingLoopIn *route.Vertex
failedLoopIn *route.Vertex
expectedErr error
}{
{
name: "swap allowed",
ongoingLoopIn: &peer2,
ongoingLoopOut: &chan2,
failedLoopIn: &peer2,
expectedErr: nil,
},
{
name: "conflicts with loop out",
ongoingLoopOut: &chan1,
expectedErr: newReasonError(ReasonLoopOut),
},
{
name: "conflicts with loop in",
ongoingLoopIn: &peer1,
expectedErr: newReasonError(ReasonLoopIn),
},
{
name: "previous failed loopin",
failedLoopIn: &peer1,
expectedErr: newReasonError(ReasonFailureBackoff),
},
}
for _, testCase := range tests {
traffic := newSwapTraffic()
if testCase.ongoingLoopOut != nil {
traffic.ongoingLoopOut[*testCase.ongoingLoopOut] = true
}
if testCase.ongoingLoopIn != nil {
traffic.ongoingLoopIn[*testCase.ongoingLoopIn] = true
}
if testCase.failedLoopIn != nil {
traffic.failedLoopIn[*testCase.failedLoopIn] = testTime
}
builder := newLoopInBuilder(nil)
err := builder.inUse(traffic, peer1, []lnwire.ShortChannelID{
chan1,
})
require.Equal(t, testCase.expectedErr, err)
}
}
// TestLoopinBuildSwap tests construction of loop in swaps for autoloop,
// including the case where the client cannot get a quote because it is not
// reachable from the server.
func TestLoopinBuildSwap(t *testing.T) {
var (
peer1 = route.Vertex{1}
chan1 = lnwire.NewShortChanIDFromInt(1)
htlcConfTarget int32 = 6
swapAmt btcutil.Amount = 100000
quote = &loop.LoopInQuote{
SwapFee: 1,
MinerFee: 2,
}
expectedSwap = &loopInSwapSuggestion{
loop.LoopInRequest{
Amount: swapAmt,
MaxSwapFee: quote.SwapFee,
MaxMinerFee: quote.MinerFee,
HtlcConfTarget: htlcConfTarget,
LastHop: &peer1,
Initiator: autoloopSwapInitiator,
},
}
quoteRequest = &loop.LoopInQuoteRequest{
Amount: swapAmt,
LastHop: &peer1,
HtlcConfTarget: htlcConfTarget,
}
errPrecondition = status.Error(codes.FailedPrecondition, "failed")
errOtherCode = status.Error(codes.DeadlineExceeded, "timeout")
errNoCode = errors.New("failure")
)
tests := []struct {
name string
prepareMock func(*mockCfg)
expectedSwap swapSuggestion
expectedErr error
}{
{
name: "quote successful",
prepareMock: func(m *mockCfg) {
m.On(
"LoopInQuote", mock.Anything,
quoteRequest,
).Return(quote, nil)
},
expectedSwap: expectedSwap,
},
{
name: "client unreachable",
prepareMock: func(m *mockCfg) {
m.On(
"LoopInQuote", mock.Anything,
quoteRequest,
).Return(quote, errPrecondition)
},
expectedSwap: nil,
expectedErr: newReasonError(ReasonLoopInUnreachable),
},
{
name: "other error code",
prepareMock: func(m *mockCfg) {
m.On(
"LoopInQuote", mock.Anything,
quoteRequest,
).Return(quote, errOtherCode)
},
expectedSwap: nil,
expectedErr: errOtherCode,
},
{
name: "no error code",
prepareMock: func(m *mockCfg) {
m.On(
"LoopInQuote", mock.Anything,
quoteRequest,
).Return(quote, errNoCode)
},
expectedSwap: nil,
expectedErr: errNoCode,
},
}
for _, testCase := range tests {
mock, cfg := newMockConfig()
params := defaultParameters
params.HtlcConfTarget = htlcConfTarget
params.AutoFeeBudget = 100000
testCase.prepareMock(mock)
builder := newLoopInBuilder(cfg)
swap, err := builder.buildSwap(
context.Background(), peer1, []lnwire.ShortChannelID{
chan1,
}, swapAmt, false, params,
)
assert.Equal(t, testCase.expectedSwap, swap)
assert.Equal(t, testCase.expectedErr, err)
mock.AssertExpectations(t)
}
}

@ -0,0 +1,33 @@
package liquidity
import (
"context"
"github.com/lightninglabs/loop"
"github.com/stretchr/testify/mock"
)
// newMockConfig returns a liquidity config with mocked calls. Note that
// functions that are not implemented by the mock will panic if called.
func newMockConfig() (*mockCfg, *Config) {
mockCfg := &mockCfg{}
// Create a liquidity config which calls our mock.
config := &Config{
LoopInQuote: mockCfg.LoopInQuote,
}
return mockCfg, config
}
type mockCfg struct {
mock.Mock
}
// LoopInQuote mocks a call to get a loop in quote from the server.
func (m *mockCfg) LoopInQuote(ctx context.Context,
request *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error) {
args := m.Called(ctx, request)
return args.Get(0).(*loop.LoopInQuote), args.Error(1)
}

@ -65,6 +65,10 @@ const (
// ReasonFeePPMInsufficient indicates that the fees a swap would require
// are greater than the portion of swap amount allocated to fees.
ReasonFeePPMInsufficient
// ReasonLoopInUnreachable indicates that the server does not have a
// path to the client, so cannot perform a loop in swap at this time.
ReasonLoopInUnreachable
)
// String returns a string representation of a reason.
@ -112,6 +116,9 @@ func (r Reason) String() string {
case ReasonFeePPMInsufficient:
return "fee portion insufficient"
case ReasonLoopInUnreachable:
return "loop in unreachable"
default:
return "unknown"
}

@ -72,7 +72,7 @@ func (r *ThresholdRule) validate() error {
// swapAmount suggests a swap based on the liquidity thresholds configured,
// returning zero if no swap is recommended.
func (r *ThresholdRule) swapAmount(channel *balances,
restrictions *Restrictions) btcutil.Amount {
restrictions *Restrictions, swapType swap.Type) btcutil.Amount {
var (
// For loop out swaps, we want to adjust our incoming liquidity
@ -95,6 +95,14 @@ func (r *ThresholdRule) swapAmount(channel *balances,
reservePercentage = uint64(r.MinimumOutgoing)
)
// For loop in swaps, we reverse our target and reserve values.
if swapType == swap.TypeIn {
targetBalance = channel.outgoing
targetPercentage = uint64(r.MinimumOutgoing)
reserveBalance = channel.incoming
reservePercentage = uint64(r.MinimumIncoming)
}
// Examine our total balance and required ratios to decide whether we
// need to swap.
amount := calculateSwapAmount(

@ -4,6 +4,7 @@ import (
"testing"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/loop/swap"
"github.com/stretchr/testify/require"
)
@ -249,6 +250,7 @@ func TestSuggestSwap(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
swap := test.rule.swapAmount(
test.channel, test.outRestrictions,
swap.TypeOut,
)
require.Equal(t, test.swap, swap)
})

@ -719,8 +719,9 @@ func (s *swapClientServer) GetLiquidityParams(_ context.Context,
Rules: make(
[]*clientrpc.LiquidityRule, 0, totalRules,
),
MinSwapAmount: uint64(cfg.ClientRestrictions.Minimum),
MaxSwapAmount: uint64(cfg.ClientRestrictions.Maximum),
MinSwapAmount: uint64(cfg.ClientRestrictions.Minimum),
MaxSwapAmount: uint64(cfg.ClientRestrictions.Maximum),
HtlcConfTarget: cfg.HtlcConfTarget,
}
switch f := cfg.FeeLimit.(type) {
@ -812,6 +813,7 @@ func (s *swapClientServer) SetLiquidityParams(ctx context.Context,
Minimum: btcutil.Amount(in.Parameters.MinSwapAmount),
Maximum: btcutil.Amount(in.Parameters.MaxSwapAmount),
},
HtlcConfTarget: in.Parameters.HtlcConfTarget,
}
// Zero unix time is different to zero golang time.
@ -953,13 +955,17 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
return nil, err
}
var (
loopOut []*clientrpc.LoopOutRequest
disqualified []*clientrpc.Disqualified
)
resp := &clientrpc.SuggestSwapsResponse{
LoopOut: make(
[]*clientrpc.LoopOutRequest, len(suggestions.OutSwaps),
),
LoopIn: make(
[]*clientrpc.LoopInRequest, len(suggestions.InSwaps),
),
}
for _, swap := range suggestions.OutSwaps {
loopOut = append(loopOut, &clientrpc.LoopOutRequest{
for i, swap := range suggestions.OutSwaps {
resp.LoopOut[i] = &clientrpc.LoopOutRequest{
Amt: int64(swap.Amount),
OutgoingChanSet: swap.OutgoingChanSet,
MaxSwapFee: int64(swap.MaxSwapFee),
@ -968,7 +974,22 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
MaxSwapRoutingFee: int64(swap.MaxSwapRoutingFee),
MaxPrepayRoutingFee: int64(swap.MaxPrepayRoutingFee),
SweepConfTarget: swap.SweepConfTarget,
})
}
}
for i, swap := range suggestions.InSwaps {
loopIn := &clientrpc.LoopInRequest{
Amt: int64(swap.Amount),
MaxSwapFee: int64(swap.MaxSwapFee),
MaxMinerFee: int64(swap.MaxMinerFee),
HtlcConfTarget: swap.HtlcConfTarget,
}
if swap.LastHop != nil {
loopIn.LastHop = swap.LastHop[:]
}
resp.LoopIn[i] = loopIn
}
for id, reason := range suggestions.DisqualifiedChans {
@ -982,7 +1003,7 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
ChannelId: id.ToUint64(),
}
disqualified = append(disqualified, exclChan)
resp.Disqualified = append(resp.Disqualified, exclChan)
}
for pubkey, reason := range suggestions.DisqualifiedPeers {
@ -996,13 +1017,10 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
Pubkey: pubkey[:],
}
disqualified = append(disqualified, exclChan)
resp.Disqualified = append(resp.Disqualified, exclChan)
}
return &clientrpc.SuggestSwapsResponse{
LoopOut: loopOut,
Disqualified: disqualified,
}, nil
return resp, nil
}
func rpcAutoloopReason(reason liquidity.Reason) (clientrpc.AutoReason, error) {

@ -39,6 +39,7 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager {
mngrCfg := &liquidity.Config{
AutoloopTicker: ticker.NewForce(liquidity.DefaultAutoloopTicker),
LoopOut: client.LoopOut,
LoopIn: client.LoopIn,
Restrictions: func(ctx context.Context,
swapType swap.Type) (*liquidity.Restrictions, error) {
@ -65,6 +66,7 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager {
Lnd: client.LndServices,
Clock: clock.NewDefaultClock(),
LoopOutQuote: client.LoopOutQuote,
LoopInQuote: client.LoopInQuote,
ListLoopOut: client.Store.FetchLoopOutSwaps,
ListLoopIn: client.Store.FetchLoopInSwaps,
MinimumConfirmations: minConfTarget,

@ -2179,6 +2179,9 @@ type LiquidityParameters struct {
//dispatch a swap for. This value is subject to the server-side limits
//specified by the LoopOutTerms endpoint.
MaxSwapAmount uint64 `protobuf:"varint,15,opt,name=max_swap_amount,json=maxSwapAmount,proto3" json:"max_swap_amount,omitempty"`
//
//The confirmation target for loop in on-chain htlcs.
HtlcConfTarget int32 `protobuf:"varint,17,opt,name=htlc_conf_target,json=htlcConfTarget,proto3" json:"htlc_conf_target,omitempty"`
}
func (x *LiquidityParameters) Reset() {
@ -2325,6 +2328,13 @@ func (x *LiquidityParameters) GetMaxSwapAmount() uint64 {
return 0
}
func (x *LiquidityParameters) GetHtlcConfTarget() int32 {
if x != nil {
return x.HtlcConfTarget
}
return 0
}
type LiquidityRule struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -2636,6 +2646,9 @@ 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"`
//
//The set of recommended loop in swaps
LoopIn []*LoopInRequest `protobuf:"bytes,3,rep,name=loop_in,json=loopIn,proto3" json:"loop_in,omitempty"`
//
//Disqualified contains the set of channels that swaps are not recommended
//for.
Disqualified []*Disqualified `protobuf:"bytes,2,rep,name=disqualified,proto3" json:"disqualified,omitempty"`
@ -2680,6 +2693,13 @@ func (x *SuggestSwapsResponse) GetLoopOut() []*LoopOutRequest {
return nil
}
func (x *SuggestSwapsResponse) GetLoopIn() []*LoopInRequest {
if x != nil {
return x.LoopIn
}
return nil
}
func (x *SuggestSwapsResponse) GetDisqualified() []*Disqualified {
if x != nil {
return x.Disqualified
@ -2910,7 +2930,7 @@ var file_client_proto_rawDesc = []byte{
0x67, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1b, 0x0a, 0x19, 0x47, 0x65,
0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe0, 0x05, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8a, 0x06, 0x0a, 0x13, 0x4c, 0x69, 0x71, 0x75,
0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12,
0x2c, 0x0a, 0x05, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69,
@ -2956,166 +2976,172 @@ var file_client_proto_rawDesc = []byte{
0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75,
0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x61,
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78,
0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x4c,
0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x09, 0x73,
0x77, 0x61, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70,
0x65, 0x52, 0x08, 0x73, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70,
0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62,
0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75,
0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,
0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f,
0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f,
0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74,
0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11,
0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c,
0x64, 0x22, 0x59, 0x0a, 0x19, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74,
0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c,
0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71,
0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73,
0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, 0x1a,
0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x75,
0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65,
0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64,
0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x85, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73,
0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32,
0x0a, 0x08, 0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f,
0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x4f,
0x75, 0x74, 0x12, 0x39, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69,
0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x44, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x52,
0x0c, 0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2a, 0x25, 0x0a,
0x08, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f,
0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f,
0x49, 0x4e, 0x10, 0x01, 0x2a, 0x73, 0x0a, 0x09, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74,
0x65, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00,
0x12, 0x15, 0x0a, 0x11, 0x50, 0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56,
0x45, 0x41, 0x4c, 0x45, 0x44, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f,
0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53,
0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c,
0x45, 0x44, 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f,
0x53, 0x45, 0x54, 0x54, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x2a, 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x61,
0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46,
0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f,
0x4e, 0x45, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10,
0x01, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a,
0x1c, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12,
0x25, 0x0a, 0x21, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56,
0x41, 0x4c, 0x55, 0x45, 0x10, 0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41,
0x52, 0x59, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54,
0x5f, 0x41, 0x4d, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x06, 0x2a, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71,
0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54,
0x48, 0x52, 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x10, 0x01, 0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41,
0x75, 0x74, 0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54,
0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41,
0x52, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53,
0x10, 0x02, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x45, 0x4c, 0x41, 0x50, 0x53, 0x45, 0x44,
0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x49, 0x4e, 0x5f, 0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x04, 0x12, 0x18, 0x0a,
0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41,
0x50, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45,
0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x50, 0x52, 0x45, 0x50, 0x41, 0x59, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55,
0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x45, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x4f, 0x46, 0x46, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x41,
0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f,
0x4f, 0x55, 0x54, 0x10, 0x09, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c,
0x0a, 0x18, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49,
0x51, 0x55, 0x49, 0x44, 0x49, 0x54, 0x59, 0x5f, 0x4f, 0x4b, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47,
0x45, 0x54, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10,
0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x46, 0x45, 0x45, 0x5f, 0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e,
0x54, 0x10, 0x0d, 0x32, 0xc2, 0x07, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x12, 0x39, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a,
0x06, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f,
0x72, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69,
0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x30,
0x01, 0x12, 0x42, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x19,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61,
0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70,
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x2e, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49,
0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f,
0x6f, 0x70, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f,
0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72,
0x6f, 0x62, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72,
0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x40, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b,
0x65, 0x6e, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69,
0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74,
0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69,
0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x0a, 0x12,
0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74,
0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72,
0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53,
0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61,
0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70,
0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x53, 0x77, 0x61, 0x70, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x68, 0x74,
0x6c, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x11,
0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x68, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x54, 0x61,
0x72, 0x67, 0x65, 0x74, 0x22, 0x84, 0x02, 0x0a, 0x0d, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69,
0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e,
0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x09, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x73, 0x77, 0x61,
0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18,
0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a,
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x52,
0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a,
0x12, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68,
0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x69, 0x6e, 0x63, 0x6f, 0x6d,
0x69, 0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x12,
0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f,
0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69,
0x6e, 0x67, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x59, 0x0a, 0x19, 0x53,
0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61,
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79,
0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61,
0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71,
0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53,
0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x72, 0x0a, 0x0c, 0x44,
0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75,
0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b,
0x65, 0x79, 0x12, 0x2b, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x74,
0x6f, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22,
0xb6, 0x01, 0x0a, 0x14, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x6f, 0x70,
0x5f, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x52, 0x07, 0x6c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x2f, 0x0a, 0x07,
0x6c, 0x6f, 0x6f, 0x70, 0x5f, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x06, 0x6c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x12, 0x39, 0x0a,
0x0c, 0x64, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x69,
0x73, 0x71, 0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x71,
0x75, 0x61, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2a, 0x25, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70,
0x54, 0x79, 0x70, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54,
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x01, 0x2a,
0x73, 0x0a, 0x09, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09,
0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x50,
0x52, 0x45, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x45, 0x41, 0x4c, 0x45, 0x44,
0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x48, 0x54, 0x4c, 0x43, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49,
0x53, 0x48, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53,
0x53, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12,
0x13, 0x0a, 0x0f, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x4c,
0x45, 0x44, 0x10, 0x05, 0x2a, 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52,
0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12,
0x1b, 0x0a, 0x17, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x4f, 0x46, 0x46, 0x43, 0x48, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16,
0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x54,
0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x46, 0x41, 0x49, 0x4c,
0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x45, 0x45, 0x50,
0x5f, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x12, 0x25, 0x0a, 0x21, 0x46, 0x41,
0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x53,
0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10,
0x04, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x54, 0x45, 0x4d, 0x50, 0x4f, 0x52, 0x41, 0x52, 0x59, 0x10, 0x05, 0x12,
0x23, 0x0a, 0x1f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x49, 0x4e, 0x43, 0x4f, 0x52, 0x52, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x4d, 0x4f, 0x55,
0x4e, 0x54, 0x10, 0x06, 0x2a, 0x2f, 0x0a, 0x11, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74,
0x79, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b,
0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x53, 0x48,
0x4f, 0x4c, 0x44, 0x10, 0x01, 0x2a, 0xa6, 0x03, 0x0a, 0x0a, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65,
0x61, 0x73, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41,
0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x22, 0x0a,
0x1e, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44,
0x47, 0x45, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10,
0x01, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e,
0x5f, 0x53, 0x57, 0x45, 0x45, 0x50, 0x5f, 0x46, 0x45, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1e, 0x0a,
0x1a, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44,
0x47, 0x45, 0x54, 0x5f, 0x45, 0x4c, 0x41, 0x50, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x19, 0x0a,
0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x5f,
0x46, 0x4c, 0x49, 0x47, 0x48, 0x54, 0x10, 0x04, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f,
0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x53, 0x57, 0x41, 0x50, 0x5f, 0x46, 0x45, 0x45,
0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f,
0x4e, 0x5f, 0x4d, 0x49, 0x4e, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a,
0x12, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x45,
0x50, 0x41, 0x59, 0x10, 0x07, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45,
0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x5f, 0x42, 0x41, 0x43,
0x4b, 0x4f, 0x46, 0x46, 0x10, 0x08, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52,
0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x4f, 0x55, 0x54, 0x10, 0x09,
0x12, 0x17, 0x0a, 0x13, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
0x4c, 0x4f, 0x4f, 0x50, 0x5f, 0x49, 0x4e, 0x10, 0x0a, 0x12, 0x1c, 0x0a, 0x18, 0x41, 0x55, 0x54,
0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4c, 0x49, 0x51, 0x55, 0x49, 0x44, 0x49,
0x54, 0x59, 0x5f, 0x4f, 0x4b, 0x10, 0x0b, 0x12, 0x23, 0x0a, 0x1f, 0x41, 0x55, 0x54, 0x4f, 0x5f,
0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x5f, 0x49, 0x4e,
0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c,
0x41, 0x55, 0x54, 0x4f, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x45, 0x45, 0x5f,
0x49, 0x4e, 0x53, 0x55, 0x46, 0x46, 0x49, 0x43, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x0d, 0x32, 0xc2,
0x07, 0x0a, 0x0a, 0x53, 0x77, 0x61, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a,
0x07, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x70,
0x49, 0x6e, 0x12, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x6f, 0x6f,
0x70, 0x49, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6c, 0x6f, 0x6f,
0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x39, 0x0a, 0x07, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x09,
0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x39, 0x0a, 0x08, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x2e, 0x6c,
0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x77, 0x61, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x77, 0x61, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x40, 0x0a, 0x0c, 0x4c,
0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x75, 0x74,
0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a,
0x0c, 0x4c, 0x6f, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x4f,
0x75, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d,
0x73, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x65, 0x72, 0x6d,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72,
0x70, 0x63, 0x2e, 0x49, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x6f, 0x70, 0x49, 0x6e, 0x51,
0x75, 0x6f, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x51,
0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6c, 0x6f,
0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x49, 0x6e, 0x51, 0x75, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x15,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a,
0x0d, 0x47, 0x65, 0x74, 0x4c, 0x73, 0x61, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x16,
0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x56, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50,
0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e,
0x47, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70,
0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72,
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x69,
0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x2e,
0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x69, 0x71, 0x75, 0x69,
0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x23, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c,
0x69, 0x71, 0x75, 0x69, 0x64, 0x69, 0x74, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73,
0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x12, 0x1c, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63,
0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x2e, 0x53,
0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x53, 0x77, 0x61, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x42, 0x27, 0x5a, 0x25, 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,
0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x6c, 0x6f, 0x6f, 0x70, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -3182,40 +3208,41 @@ var file_client_proto_depIdxs = []int32{
25, // 11: looprpc.SetLiquidityParamsRequest.parameters:type_name -> looprpc.LiquidityParameters
4, // 12: looprpc.Disqualified.reason:type_name -> looprpc.AutoReason
5, // 13: looprpc.SuggestSwapsResponse.loop_out:type_name -> looprpc.LoopOutRequest
30, // 14: looprpc.SuggestSwapsResponse.disqualified:type_name -> looprpc.Disqualified
5, // 15: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest
6, // 16: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest
8, // 17: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest
10, // 18: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest
12, // 19: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest
13, // 20: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest
16, // 21: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest
13, // 22: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest
16, // 23: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest
19, // 24: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest
21, // 25: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest
24, // 26: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest
27, // 27: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest
29, // 28: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest
7, // 29: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse
7, // 30: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse
9, // 31: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus
11, // 32: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse
9, // 33: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus
15, // 34: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse
18, // 35: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse
14, // 36: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse
17, // 37: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse
20, // 38: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse
22, // 39: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse
25, // 40: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters
28, // 41: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse
31, // 42: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse
29, // [29:43] is the sub-list for method output_type
15, // [15:29] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
6, // 14: looprpc.SuggestSwapsResponse.loop_in:type_name -> looprpc.LoopInRequest
30, // 15: looprpc.SuggestSwapsResponse.disqualified:type_name -> looprpc.Disqualified
5, // 16: looprpc.SwapClient.LoopOut:input_type -> looprpc.LoopOutRequest
6, // 17: looprpc.SwapClient.LoopIn:input_type -> looprpc.LoopInRequest
8, // 18: looprpc.SwapClient.Monitor:input_type -> looprpc.MonitorRequest
10, // 19: looprpc.SwapClient.ListSwaps:input_type -> looprpc.ListSwapsRequest
12, // 20: looprpc.SwapClient.SwapInfo:input_type -> looprpc.SwapInfoRequest
13, // 21: looprpc.SwapClient.LoopOutTerms:input_type -> looprpc.TermsRequest
16, // 22: looprpc.SwapClient.LoopOutQuote:input_type -> looprpc.QuoteRequest
13, // 23: looprpc.SwapClient.GetLoopInTerms:input_type -> looprpc.TermsRequest
16, // 24: looprpc.SwapClient.GetLoopInQuote:input_type -> looprpc.QuoteRequest
19, // 25: looprpc.SwapClient.Probe:input_type -> looprpc.ProbeRequest
21, // 26: looprpc.SwapClient.GetLsatTokens:input_type -> looprpc.TokensRequest
24, // 27: looprpc.SwapClient.GetLiquidityParams:input_type -> looprpc.GetLiquidityParamsRequest
27, // 28: looprpc.SwapClient.SetLiquidityParams:input_type -> looprpc.SetLiquidityParamsRequest
29, // 29: looprpc.SwapClient.SuggestSwaps:input_type -> looprpc.SuggestSwapsRequest
7, // 30: looprpc.SwapClient.LoopOut:output_type -> looprpc.SwapResponse
7, // 31: looprpc.SwapClient.LoopIn:output_type -> looprpc.SwapResponse
9, // 32: looprpc.SwapClient.Monitor:output_type -> looprpc.SwapStatus
11, // 33: looprpc.SwapClient.ListSwaps:output_type -> looprpc.ListSwapsResponse
9, // 34: looprpc.SwapClient.SwapInfo:output_type -> looprpc.SwapStatus
15, // 35: looprpc.SwapClient.LoopOutTerms:output_type -> looprpc.OutTermsResponse
18, // 36: looprpc.SwapClient.LoopOutQuote:output_type -> looprpc.OutQuoteResponse
14, // 37: looprpc.SwapClient.GetLoopInTerms:output_type -> looprpc.InTermsResponse
17, // 38: looprpc.SwapClient.GetLoopInQuote:output_type -> looprpc.InQuoteResponse
20, // 39: looprpc.SwapClient.Probe:output_type -> looprpc.ProbeResponse
22, // 40: looprpc.SwapClient.GetLsatTokens:output_type -> looprpc.TokensResponse
25, // 41: looprpc.SwapClient.GetLiquidityParams:output_type -> looprpc.LiquidityParameters
28, // 42: looprpc.SwapClient.SetLiquidityParams:output_type -> looprpc.SetLiquidityParamsResponse
31, // 43: looprpc.SwapClient.SuggestSwaps:output_type -> looprpc.SuggestSwapsResponse
30, // [30:44] is the sub-list for method output_type
16, // [16:30] is the sub-list for method input_type
16, // [16:16] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension extendee
0, // [0:16] is the sub-list for field type_name
}
func init() { file_client_proto_init() }

@ -848,6 +848,11 @@ message LiquidityParameters {
specified by the LoopOutTerms endpoint.
*/
uint64 max_swap_amount = 15;
/*
The confirmation target for loop in on-chain htlcs.
*/
int32 htlc_conf_target = 17;
}
enum LiquidityRuleType {
@ -1012,6 +1017,11 @@ message SuggestSwapsResponse {
*/
repeated LoopOutRequest loop_out = 1;
/*
The set of recommended loop in swaps
*/
repeated LoopInRequest loop_in = 3;
/*
Disqualified contains the set of channels that swaps are not recommended
for.

@ -675,6 +675,11 @@
"type": "string",
"format": "uint64",
"description": "The maximum amount, expressed in satoshis, that the autoloop client will\ndispatch a swap for. This value is subject to the server-side limits\nspecified by the LoopOutTerms endpoint."
},
"htlc_conf_target": {
"type": "integer",
"format": "int32",
"description": "The confirmation target for loop in on-chain htlcs."
}
}
},
@ -999,6 +1004,13 @@
},
"description": "The set of recommended loop outs."
},
"loop_in": {
"type": "array",
"items": {
"$ref": "#/definitions/looprpcLoopInRequest"
},
"title": "The set of recommended loop in swaps"
},
"disqualified": {
"type": "array",
"items": {

@ -15,6 +15,10 @@ This file tracks release notes for the loop client.
## Next release
#### New Features
* Loop in functionality has been added to AutoLoop. This feature can be enabled
to acquire outgoing capacity on your node automatically, using
`loop setrule --type=in`. At present, autoloop can only be set to loop out
*or* loop in, and cannot manage liquidity in both directions.
#### Breaking Changes

Loading…
Cancel
Save