mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #289 from carlaKC/205-restrictsuggestions
suggestions: add fee and ongoing swap restrictions
This commit is contained in:
commit
a6956a4e06
14 changed files with 1556 additions and 248 deletions
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/lightninglabs/loop/liquidity"
|
||||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
|
@ -36,8 +37,8 @@ func getParams(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var setLiquidityParamCommand = cli.Command{
|
||||
Name: "setparam",
|
||||
var setLiquidityRuleCommand = cli.Command{
|
||||
Name: "setrule",
|
||||
Usage: "set liquidity manager rule for a channel",
|
||||
Description: "Update or remove the liquidity rule for a channel.",
|
||||
ArgsUsage: "shortchanid",
|
||||
|
|
@ -58,10 +59,10 @@ var setLiquidityParamCommand = cli.Command{
|
|||
Usage: "remove the rule currently set for the channel.",
|
||||
},
|
||||
},
|
||||
Action: setParam,
|
||||
Action: setRule,
|
||||
}
|
||||
|
||||
func setParam(ctx *cli.Context) error {
|
||||
func setRule(ctx *cli.Context) error {
|
||||
// We require that a channel ID is set for this rule update.
|
||||
if ctx.NArg() != 1 {
|
||||
return fmt.Errorf("please set a channel id for the rule " +
|
||||
|
|
@ -122,12 +123,11 @@ func setParam(ctx *cli.Context) error {
|
|||
"flag")
|
||||
}
|
||||
|
||||
params.Rules = otherRules
|
||||
_, err = client.SetLiquidityParams(
|
||||
context.Background(),
|
||||
&looprpc.SetLiquidityParamsRequest{
|
||||
Parameters: &looprpc.LiquidityParameters{
|
||||
Rules: otherRules,
|
||||
},
|
||||
Parameters: params,
|
||||
},
|
||||
)
|
||||
return err
|
||||
|
|
@ -158,19 +158,176 @@ func setParam(ctx *cli.Context) error {
|
|||
)
|
||||
}
|
||||
|
||||
// Just set the rules on our current set of parameters and leave the
|
||||
// other values untouched.
|
||||
otherRules = append(otherRules, newRule)
|
||||
params.Rules = otherRules
|
||||
|
||||
// Update our parameters to the existing set, plus our new rule.
|
||||
_, err = client.SetLiquidityParams(
|
||||
context.Background(),
|
||||
&looprpc.SetLiquidityParamsRequest{
|
||||
Parameters: &looprpc.LiquidityParameters{
|
||||
Rules: append(otherRules, newRule),
|
||||
},
|
||||
Parameters: params,
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var setParamsCommand = cli.Command{
|
||||
Name: "setparams",
|
||||
Usage: "update the parameters set for the liquidity manager",
|
||||
Description: "Updates the parameters set for the liquidity manager.",
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Name: "sweeplimit",
|
||||
Usage: "the limit placed on our estimated sweep fee " +
|
||||
"in sat/vByte.",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "maxswapfee",
|
||||
Usage: "the maximum percentage of swap volume we are " +
|
||||
"willing to pay in server fees.",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "maxroutingfee",
|
||||
Usage: "the maximum percentage of off-chain payment " +
|
||||
"volume that are are willing to pay in " +
|
||||
"routing fees.",
|
||||
},
|
||||
cli.Float64Flag{
|
||||
Name: "maxprepayfee",
|
||||
Usage: "the maximum percentage of off-chain prepay " +
|
||||
"volume that are are willing to pay in " +
|
||||
"routing fees.",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "maxprepay",
|
||||
Usage: "the maximum no-show (prepay) in satoshis that " +
|
||||
"swap suggestions should be limited to.",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "maxminer",
|
||||
Usage: "the maximum miner fee in satoshis that swap " +
|
||||
"suggestions should be limited to.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sweepconf",
|
||||
Usage: "the number of blocks from htlc height that " +
|
||||
"swap suggestion sweeps should target, used " +
|
||||
"to estimate max miner fee.",
|
||||
},
|
||||
cli.Uint64Flag{
|
||||
Name: "failurebackoff",
|
||||
Usage: "the amount of time, in seconds, that " +
|
||||
"should pass before a channel that " +
|
||||
"previously had a failed swap will be " +
|
||||
"included in suggestions.",
|
||||
},
|
||||
},
|
||||
Action: setParams,
|
||||
}
|
||||
|
||||
func setParams(ctx *cli.Context) error {
|
||||
client, cleanup, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// We need to set the full set of current parameters every time we call
|
||||
// SetParameters. To allow users to set only individual fields on the
|
||||
// cli, we lookup our current params, then update individual values.
|
||||
params, err := client.GetLiquidityParams(
|
||||
context.Background(), &looprpc.GetLiquidityParamsRequest{},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var flagSet bool
|
||||
|
||||
if ctx.IsSet("maxswapfee") {
|
||||
feeRate := ctx.Float64("maxswapfee")
|
||||
params.MaxSwapFeePpm, err = ppmFromPercentage(feeRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("sweeplimit") {
|
||||
satPerVByte := ctx.Int("sweeplimit")
|
||||
params.SweepFeeRateSatPerVbyte = uint64(satPerVByte)
|
||||
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("maxroutingfee") {
|
||||
feeRate := ctx.Float64("maxroutingfee")
|
||||
params.MaxRoutingFeePpm, err = ppmFromPercentage(feeRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("maxprepayfee") {
|
||||
feeRate := ctx.Float64("maxprepayfee")
|
||||
params.MaxPrepayRoutingFeePpm, err = ppmFromPercentage(feeRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("maxprepay") {
|
||||
params.MaxPrepaySat = ctx.Uint64("maxprepay")
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("maxminer") {
|
||||
params.MaxMinerFeeSat = ctx.Uint64("maxminer")
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("sweepconf") {
|
||||
params.SweepConfTarget = int32(ctx.Int("sweepconf"))
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if ctx.IsSet("failurebackoff") {
|
||||
params.FailureBackoffSec = ctx.Uint64("failurebackoff")
|
||||
flagSet = true
|
||||
}
|
||||
|
||||
if !flagSet {
|
||||
return fmt.Errorf("at least one flag required to set params")
|
||||
}
|
||||
|
||||
// Update our parameters to our mutated values.
|
||||
_, err = client.SetLiquidityParams(
|
||||
context.Background(), &looprpc.SetLiquidityParamsRequest{
|
||||
Parameters: params,
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ppmFromPercentage converts a percentage, expressed as a float, to parts
|
||||
// per million.
|
||||
func ppmFromPercentage(percentage float64) (uint64, error) {
|
||||
if percentage <= 0 || percentage >= 100 {
|
||||
return 0, fmt.Errorf("fee percentage must be in (0;100)")
|
||||
}
|
||||
|
||||
return uint64(percentage / 100 * liquidity.FeeBase), nil
|
||||
}
|
||||
|
||||
var suggestSwapCommand = cli.Command{
|
||||
Name: "suggestswaps",
|
||||
Usage: "show a list of suggested swaps",
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ func main() {
|
|||
loopOutCommand, loopInCommand, termsCommand,
|
||||
monitorCommand, quoteCommand, listAuthCommand,
|
||||
listSwapsCommand, swapInfoCommand, getLiquidityParamsCommand,
|
||||
setLiquidityParamCommand, suggestSwapCommand,
|
||||
setLiquidityRuleCommand, suggestSwapCommand, setParamsCommand,
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -14,6 +14,7 @@ require (
|
|||
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d
|
||||
github.com/lightningnetwork/lnd v0.11.1-beta.rc3
|
||||
github.com/lightningnetwork/lnd/cert v1.0.3
|
||||
github.com/lightningnetwork/lnd/clock v1.0.1
|
||||
github.com/lightningnetwork/lnd/queue v1.0.4
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/urfave/cli v1.20.0
|
||||
|
|
|
|||
|
|
@ -1,21 +1,130 @@
|
|||
// Package liquidity is responsible for monitoring our node's liquidity. It
|
||||
// allows setting of a liquidity rule which describes the desired liquidity
|
||||
// balance on a per-channel basis.
|
||||
//
|
||||
// Swap suggestions are limited to channels that are not currently being used
|
||||
// for a pending swap. If we are currently processing an unrestricted swap (ie,
|
||||
// a loop out with no outgoing channel targets set or a loop in with no last
|
||||
// hop set), we will not suggest any swaps because these swaps will shift the
|
||||
// balances of our channels in ways we can't predict.
|
||||
//
|
||||
// Fee restrictions are placed on swap suggestions to ensure that we only
|
||||
// suggest swaps that fit the configured fee preferences.
|
||||
// - Sweep Fee Rate Limit: the maximum sat/vByte fee estimate for our sweep
|
||||
// transaction to confirm within our configured number of confirmations
|
||||
// that we will suggest swaps for.
|
||||
// - Maximum Swap Fee PPM: the maximum server fee, expressed as parts per
|
||||
// million of the full swap amount
|
||||
// - Maximum Routing Fee PPM: the maximum off-chain routing fees for the swap
|
||||
// invoice, expressed as parts per million of the swap amount.
|
||||
// - Maximum Prepay Routing Fee PPM: the maximum off-chain routing fees for the
|
||||
// swap prepayment, expressed as parts per million of the prepay amount.
|
||||
// - Maximum Prepay: the maximum now-show fee, expressed in satoshis. This
|
||||
// amount is only payable in the case where the swap server broadcasts a htlc
|
||||
// and the client fails to sweep the preimage.
|
||||
// - Maximum miner fee: the maximum miner fee we are willing to pay to sweep the
|
||||
// on chain htlc. Note that the client will use current fee estimates to
|
||||
// sweep, so this value acts more as a sanity check in the case of a large fee
|
||||
// spike.
|
||||
//
|
||||
// The maximum fee per-swap is calculated as follows:
|
||||
// (swap amount * serverPPM/1e6) + miner fee + (swap amount * routingPPM/1e6)
|
||||
// + (prepay amount * prepayPPM/1e6).
|
||||
package liquidity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultFailureBackoff is the default amount of time we backoff if
|
||||
// a channel is part of a temporarily failed swap.
|
||||
defaultFailureBackoff = time.Hour * 24
|
||||
|
||||
// FeeBase is the base that we use to express fees.
|
||||
FeeBase = 1e6
|
||||
|
||||
// defaultSwapFeePPM is the default limit we place on swap fees,
|
||||
// expressed as parts per million of swap volume, 0.5%.
|
||||
defaultSwapFeePPM = 5000
|
||||
|
||||
// defaultRoutingFeePPM is the default limit we place on routing fees
|
||||
// for the swap invoice, expressed as parts per million of swap volume,
|
||||
// 1%.
|
||||
defaultRoutingFeePPM = 10000
|
||||
|
||||
// defaultRoutingFeePPM is the default limit we place on routing fees
|
||||
// for the prepay invoice, expressed as parts per million of prepay
|
||||
// volume, 0.5%.
|
||||
defaultPrepayRoutingFeePPM = 5000
|
||||
|
||||
// defaultMaximumMinerFee is the default limit we place on miner fees
|
||||
// per swap. We apply a multiplier to this default fee to guard against
|
||||
// the case where we have broadcast the preimage, then fees spike and
|
||||
// we need to sweep the preimage.
|
||||
defaultMaximumMinerFee = 15000 * 100
|
||||
|
||||
// defaultMaximumPrepay is the default limit we place on prepay
|
||||
// invoices.
|
||||
defaultMaximumPrepay = 30000
|
||||
|
||||
// defaultSweepFeeRateLimit is the default limit we place on estimated
|
||||
// sweep fees, (750 * 4 /1000 = 3 sat/vByte).
|
||||
defaultSweepFeeRateLimit = chainfee.SatPerKWeight(750)
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultParameters contains the default parameters that we start our
|
||||
// liquidity manger with.
|
||||
defaultParameters = Parameters{
|
||||
ChannelRules: make(map[lnwire.ShortChannelID]*ThresholdRule),
|
||||
FailureBackOff: defaultFailureBackoff,
|
||||
SweepFeeRateLimit: defaultSweepFeeRateLimit,
|
||||
SweepConfTarget: loop.DefaultSweepConfTarget,
|
||||
MaximumSwapFeePPM: defaultSwapFeePPM,
|
||||
MaximumRoutingFeePPM: defaultRoutingFeePPM,
|
||||
MaximumPrepayRoutingFeePPM: defaultPrepayRoutingFeePPM,
|
||||
MaximumMinerFee: defaultMaximumMinerFee,
|
||||
MaximumPrepay: defaultMaximumPrepay,
|
||||
}
|
||||
|
||||
// ErrZeroChannelID is returned if we get a rule for a 0 channel ID.
|
||||
ErrZeroChannelID = fmt.Errorf("zero channel ID not allowed")
|
||||
|
||||
// ErrInvalidSweepFeeRateLimit is returned if an invalid sweep fee limit
|
||||
// is set.
|
||||
ErrInvalidSweepFeeRateLimit = fmt.Errorf("sweep fee rate limit must "+
|
||||
"be > %v sat/vByte",
|
||||
satPerKwToSatPerVByte(chainfee.AbsoluteFeePerKwFloor))
|
||||
|
||||
// ErrZeroMinerFee is returned if a zero maximum miner fee is set.
|
||||
ErrZeroMinerFee = errors.New("maximum miner fee must be non-zero")
|
||||
|
||||
// ErrZeroSwapFeePPM is returned if a zero server fee ppm is set.
|
||||
ErrZeroSwapFeePPM = errors.New("swap fee PPM must be non-zero")
|
||||
|
||||
// ErrZeroRoutingPPM is returned if a zero routing fee ppm is set.
|
||||
ErrZeroRoutingPPM = errors.New("routing fee PPM must be non-zero")
|
||||
|
||||
// ErrZeroPrepayPPM is returned if a zero prepay routing fee ppm is set.
|
||||
ErrZeroPrepayPPM = errors.New("prepay routing fee PPM must be non-zero")
|
||||
|
||||
// ErrZeroPrepay is returned if a zero maximum prepay is set.
|
||||
ErrZeroPrepay = errors.New("maximum prepay must be non-zero")
|
||||
)
|
||||
|
||||
// Config contains the external functionality required to run the
|
||||
|
|
@ -25,25 +134,78 @@ type Config struct {
|
|||
// to loop out swaps.
|
||||
LoopOutRestrictions func(ctx context.Context) (*Restrictions, error)
|
||||
|
||||
// Lnd provides us with access to lnd's main rpc.
|
||||
Lnd lndclient.LightningClient
|
||||
// Lnd provides us with access to lnd's rpc servers.
|
||||
Lnd *lndclient.LndServices
|
||||
|
||||
// ListLoopOut returns all of the loop our swaps stored on disk.
|
||||
ListLoopOut func() ([]*loopdb.LoopOut, error)
|
||||
|
||||
// ListLoopIn returns all of the loop in swaps stored on disk.
|
||||
ListLoopIn func() ([]*loopdb.LoopIn, error)
|
||||
|
||||
// LoopOutQuote gets swap fee, estimated miner fee and prepay amount for
|
||||
// a loop out swap.
|
||||
LoopOutQuote func(ctx context.Context,
|
||||
request *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote, error)
|
||||
|
||||
// Clock allows easy mocking of time in unit tests.
|
||||
Clock clock.Clock
|
||||
|
||||
// MinimumConfirmations is the minimum number of confirmations we allow
|
||||
// setting for sweep target.
|
||||
MinimumConfirmations int32
|
||||
}
|
||||
|
||||
// Parameters is a set of parameters provided by the user which guide
|
||||
// how we assess liquidity.
|
||||
type Parameters struct {
|
||||
// FailureBackOff is the amount of time that we require passes after a
|
||||
// channel has been part of a failed loop out swap before we suggest
|
||||
// using it again.
|
||||
// TODO(carla): add exponential backoff
|
||||
FailureBackOff time.Duration
|
||||
|
||||
// SweepFeeRateLimit is the limit that we place on our estimated sweep
|
||||
// fee. A swap will not be suggested if estimated fee rate is above this
|
||||
// value.
|
||||
SweepFeeRateLimit chainfee.SatPerKWeight
|
||||
|
||||
// SweepConfTarget is the number of blocks we aim to confirm our sweep
|
||||
// transaction in. This value affects the on chain fees we will pay.
|
||||
SweepConfTarget int32
|
||||
|
||||
// MaximumPrepay is the maximum prepay amount we are willing to pay per
|
||||
// swap.
|
||||
MaximumPrepay btcutil.Amount
|
||||
|
||||
// MaximumSwapFeePPM is the maximum server fee we are willing to pay per
|
||||
// swap expressed as parts per million of the swap volume.
|
||||
MaximumSwapFeePPM int
|
||||
|
||||
// MaximumRoutingFeePPM is the maximum off-chain routing fee we
|
||||
// are willing to pay for off chain invoice routing fees per swap,
|
||||
// expressed as parts per million of the swap amount.
|
||||
MaximumRoutingFeePPM int
|
||||
|
||||
// MaximumPrepayRoutingFeePPM is the maximum off-chain routing fee we
|
||||
// are willing to pay for off chain prepay routing fees per swap,
|
||||
// expressed as parts per million of the prepay amount.
|
||||
MaximumPrepayRoutingFeePPM int
|
||||
|
||||
// MaximumMinerFee is the maximum on chain fee that we cap our miner
|
||||
// fee at in case where we need to claim on chain because we have
|
||||
// revealed the preimage, but fees have spiked. We will not initiate a
|
||||
// swap if we estimate that the sweep cost will be above our sweep
|
||||
// fee limit, and we use fee estimates at time of sweep to set our fees,
|
||||
// so this is just a sane cap covering the special case where we need to
|
||||
// sweep during a fee spike.
|
||||
MaximumMinerFee btcutil.Amount
|
||||
|
||||
// ChannelRules maps a short channel ID to a rule that describes how we
|
||||
// would like liquidity to be managed.
|
||||
ChannelRules map[lnwire.ShortChannelID]*ThresholdRule
|
||||
}
|
||||
|
||||
// newParameters creates an empty set of parameters.
|
||||
func newParameters() Parameters {
|
||||
return Parameters{
|
||||
ChannelRules: make(map[lnwire.ShortChannelID]*ThresholdRule),
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of our parameters.
|
||||
func (p Parameters) String() string {
|
||||
channelRules := make([]string, 0, len(p.ChannelRules))
|
||||
|
|
@ -54,12 +216,20 @@ func (p Parameters) String() string {
|
|||
)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("channel rules: %v",
|
||||
strings.Join(channelRules, ","))
|
||||
return fmt.Sprintf("channel rules: %v, failure backoff: %v, sweep "+
|
||||
"fee rate limit: %v, sweep conf target: %v, maximum prepay: "+
|
||||
"%v, maximum miner fee: %v, maximum swap fee ppm: %v, maximum "+
|
||||
"routing fee ppm: %v, maximum prepay routing fee ppm: %v",
|
||||
strings.Join(channelRules, ","), p.FailureBackOff,
|
||||
p.SweepFeeRateLimit, p.SweepConfTarget, p.MaximumPrepay,
|
||||
p.MaximumMinerFee, p.MaximumSwapFeePPM,
|
||||
p.MaximumRoutingFeePPM, p.MaximumPrepayRoutingFeePPM,
|
||||
)
|
||||
}
|
||||
|
||||
// validate checks whether a set of parameters is valid.
|
||||
func (p Parameters) validate() error {
|
||||
// validate checks whether a set of parameters is valid. It takes the minimum
|
||||
// confirmations we allow for sweep confirmation target as a parameter.
|
||||
func (p Parameters) validate(minConfs int32) error {
|
||||
for channel, rule := range p.ChannelRules {
|
||||
if channel.ToUint64() == 0 {
|
||||
return ErrZeroChannelID
|
||||
|
|
@ -71,6 +241,40 @@ func (p Parameters) validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Check that our sweep limit is above our minimum fee rate. We use
|
||||
// absolute fee floor rather than kw floor because we will allow users
|
||||
// to specify fee rate is sat/vByte and want to allow 1 sat/vByte.
|
||||
if p.SweepFeeRateLimit < chainfee.AbsoluteFeePerKwFloor {
|
||||
return ErrInvalidSweepFeeRateLimit
|
||||
}
|
||||
|
||||
// Check that our confirmation target is above our required minimum.
|
||||
if p.SweepConfTarget < minConfs {
|
||||
return fmt.Errorf("confirmation target must be at least: %v",
|
||||
minConfs)
|
||||
}
|
||||
|
||||
// Check that we have non-zero fee limits.
|
||||
if p.MaximumSwapFeePPM == 0 {
|
||||
return ErrZeroSwapFeePPM
|
||||
}
|
||||
|
||||
if p.MaximumRoutingFeePPM == 0 {
|
||||
return ErrZeroRoutingPPM
|
||||
}
|
||||
|
||||
if p.MaximumPrepayRoutingFeePPM == 0 {
|
||||
return ErrZeroPrepayPPM
|
||||
}
|
||||
|
||||
if p.MaximumPrepay == 0 {
|
||||
return ErrZeroPrepay
|
||||
}
|
||||
|
||||
if p.MaximumMinerFee == 0 {
|
||||
return ErrZeroMinerFee
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +297,7 @@ type Manager struct {
|
|||
func NewManager(cfg *Config) *Manager {
|
||||
return &Manager{
|
||||
cfg: cfg,
|
||||
params: newParameters(),
|
||||
params: defaultParameters,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +312,7 @@ func (m *Manager) GetParameters() Parameters {
|
|||
// SetParameters updates our current set of parameters if the new parameters
|
||||
// provided are valid.
|
||||
func (m *Manager) SetParameters(params Parameters) error {
|
||||
if err := params.validate(); err != nil {
|
||||
if err := params.validate(m.cfg.MinimumConfirmations); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -123,10 +327,11 @@ func (m *Manager) SetParameters(params Parameters) error {
|
|||
// cannot mutate our parameters. Although our parameters struct itself is not
|
||||
// a reference, we still need to clone the contents of maps.
|
||||
func cloneParameters(params Parameters) Parameters {
|
||||
paramCopy := Parameters{
|
||||
ChannelRules: make(map[lnwire.ShortChannelID]*ThresholdRule,
|
||||
len(params.ChannelRules)),
|
||||
}
|
||||
paramCopy := params
|
||||
paramCopy.ChannelRules = make(
|
||||
map[lnwire.ShortChannelID]*ThresholdRule,
|
||||
len(params.ChannelRules),
|
||||
)
|
||||
|
||||
for channel, rule := range params.ChannelRules {
|
||||
ruleCopy := *rule
|
||||
|
|
@ -140,7 +345,7 @@ func cloneParameters(params Parameters) Parameters {
|
|||
// 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) {
|
||||
[]loop.OutRequest, error) {
|
||||
|
||||
m.paramsLock.Lock()
|
||||
defer m.paramsLock.Unlock()
|
||||
|
|
@ -151,19 +356,53 @@ func (m *Manager) SuggestSwaps(ctx context.Context) (
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
channels, err := m.cfg.Lnd.ListChannels(ctx)
|
||||
// Before we get any swap suggestions, we check what the current fee
|
||||
// estimate is to sweep within our target number of confirmations. If
|
||||
// This fee exceeds the fee limit we have set, we will not suggest any
|
||||
// swaps at present.
|
||||
estimate, err := m.cfg.Lnd.WalletKit.EstimateFee(
|
||||
ctx, m.params.SweepConfTarget,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if estimate > m.params.SweepFeeRateLimit {
|
||||
log.Debugf("Current fee estimate to sweep within: %v blocks "+
|
||||
"%v sat/vByte exceeds limit of: %v sat/vByte",
|
||||
m.params.SweepConfTarget,
|
||||
satPerKwToSatPerVByte(estimate),
|
||||
satPerKwToSatPerVByte(m.params.SweepFeeRateLimit))
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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.
|
||||
loopOut, err := m.cfg.ListLoopOut()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loopIn, err := m.cfg.ListLoopIn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eligible, err := m.getEligibleChannels(ctx, loopOut, loopIn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var suggestions []loop.OutRequest
|
||||
for _, channel := range eligible {
|
||||
channelID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
|
||||
rule, ok := m.params.ChannelRules[channelID]
|
||||
if !ok {
|
||||
|
|
@ -175,11 +414,242 @@ func (m *Manager) SuggestSwaps(ctx context.Context) (
|
|||
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)
|
||||
// required, so we skip over them.
|
||||
if suggestion == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get a quote for a swap of this amount.
|
||||
quote, err := m.cfg.LoopOutQuote(
|
||||
ctx, &loop.LoopOutQuoteRequest{
|
||||
Amount: suggestion.Amount,
|
||||
SweepConfTarget: m.params.SweepConfTarget,
|
||||
SwapPublicationDeadline: m.cfg.Clock.Now(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("quote for suggestion: %v, swap fee: %v, "+
|
||||
"miner fee: %v, prepay: %v", suggestion, quote.SwapFee,
|
||||
quote.MinerFee, quote.PrepayAmount)
|
||||
|
||||
// Check that the estimated fees for the suggested swap are
|
||||
// below the fee limits configured by the manager.
|
||||
err = m.checkFeeLimits(quote, suggestion.Amount)
|
||||
if err != nil {
|
||||
log.Infof("suggestion: %v expected fees too high: %v",
|
||||
suggestion, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
outRequest := m.makeLoopOutRequest(suggestion, quote)
|
||||
suggestions = append(suggestions, outRequest)
|
||||
}
|
||||
|
||||
return suggestions, nil
|
||||
}
|
||||
|
||||
// makeLoopOutRequest creates a loop out request from a suggestion. Since we
|
||||
// do not get any information about our off-chain routing fees when we request
|
||||
// a quote, we just set our prepay and route maximum fees directly from the
|
||||
// amounts we expect to route. The estimation we use elsewhere is the repo is
|
||||
// route-independent, which is a very poor estimation so we don't bother with
|
||||
// checking against this inaccurate constant. We use the exact prepay amount
|
||||
// and swap fee given to us by the server, but use our maximum miner fee anyway
|
||||
// to give us some leeway when performing the swap.
|
||||
func (m *Manager) makeLoopOutRequest(suggestion *LoopOutRecommendation,
|
||||
quote *loop.LoopOutQuote) loop.OutRequest {
|
||||
|
||||
prepayMaxFee := ppmToSat(
|
||||
quote.PrepayAmount, m.params.MaximumPrepayRoutingFeePPM,
|
||||
)
|
||||
|
||||
routeMaxFee := ppmToSat(
|
||||
suggestion.Amount, m.params.MaximumRoutingFeePPM,
|
||||
)
|
||||
|
||||
return loop.OutRequest{
|
||||
Amount: suggestion.Amount,
|
||||
OutgoingChanSet: loopdb.ChannelSet{
|
||||
suggestion.Channel.ToUint64(),
|
||||
},
|
||||
MaxPrepayRoutingFee: prepayMaxFee,
|
||||
MaxSwapRoutingFee: routeMaxFee,
|
||||
MaxMinerFee: m.params.MaximumMinerFee,
|
||||
MaxSwapFee: quote.SwapFee,
|
||||
MaxPrepayAmount: quote.PrepayAmount,
|
||||
SweepConfTarget: m.params.SweepConfTarget,
|
||||
}
|
||||
}
|
||||
|
||||
// getEligibleChannels takes lists of our existing loop out and in swaps, and
|
||||
// gets a list of channels that are not currently being utilized for a swap.
|
||||
// If an unrestricted swap is ongoing, we return an empty set of channels
|
||||
// because we don't know which channels balances it will affect.
|
||||
func (m *Manager) getEligibleChannels(ctx context.Context,
|
||||
loopOut []*loopdb.LoopOut, loopIn []*loopdb.LoopIn) (
|
||||
[]lndclient.ChannelInfo, error) {
|
||||
|
||||
var (
|
||||
existingOut = make(map[lnwire.ShortChannelID]bool)
|
||||
existingIn = make(map[route.Vertex]bool)
|
||||
failedOut = make(map[lnwire.ShortChannelID]time.Time)
|
||||
)
|
||||
|
||||
// Failure cutoff is the most recent failure timestamp we will still
|
||||
// consider a channel eligible. Any channels involved in swaps that have
|
||||
// failed since this point will not be considered.
|
||||
failureCutoff := m.cfg.Clock.Now().Add(m.params.FailureBackOff * -1)
|
||||
|
||||
for _, out := range loopOut {
|
||||
var (
|
||||
state = out.State().State
|
||||
chanSet = out.Contract.OutgoingChanSet
|
||||
)
|
||||
|
||||
// If a loop out swap failed due to off chain payment after our
|
||||
// failure cutoff, we add all of its channels to a set of
|
||||
// recently failed channels. It is possible that not all of
|
||||
// these channels were used for the swap, but we play it safe
|
||||
// and back off for all of them.
|
||||
//
|
||||
// We only backoff for off temporary failures. In the case of
|
||||
// chain payment failures, our swap failed to route and we do
|
||||
// not want to repeatedly try to route through bad channels
|
||||
// which remain unbalanced because they cannot route a swap, so
|
||||
// we backoff.
|
||||
if state == loopdb.StateFailOffchainPayments {
|
||||
failedAt := out.LastUpdate().Time
|
||||
|
||||
if failedAt.After(failureCutoff) {
|
||||
for _, id := range chanSet {
|
||||
chanID := lnwire.NewShortChanIDFromInt(
|
||||
id,
|
||||
)
|
||||
|
||||
failedOut[chanID] = failedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip completed swaps, they can't affect our channel balances.
|
||||
// Swaps that fail temporarily are considered to be in a pending
|
||||
// state, so we will also check that channels being used by
|
||||
// these swaps. This is important, because a temporarily failed
|
||||
// swap could be re-dispatched on restart, affecting our
|
||||
// balances.
|
||||
if state.Type() != loopdb.StateTypePending {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(chanSet) == 0 {
|
||||
log.Debugf("Ongoing unrestricted loop out: "+
|
||||
"%v, no suggestions at present", out.Hash)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, id := range chanSet {
|
||||
chanID := lnwire.NewShortChanIDFromInt(id)
|
||||
existingOut[chanID] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, in := range loopIn {
|
||||
// Skip completed swaps, they can't affect our channel balances.
|
||||
if in.State().State.Type() != loopdb.StateTypePending {
|
||||
continue
|
||||
}
|
||||
|
||||
if in.Contract.LastHop == nil {
|
||||
log.Debugf("Ongoing unrestricted loop in: "+
|
||||
"%v, no suggestions at present", in.Hash)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
existingIn[*in.Contract.LastHop] = true
|
||||
}
|
||||
|
||||
channels, err := m.cfg.Lnd.Client.ListChannels(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Run through our set of channels and skip over any channels that
|
||||
// are currently being utilized by a restricted swap (where restricted
|
||||
// means that a loop out limited channels, or a loop in limited last
|
||||
// hop).
|
||||
var eligible []lndclient.ChannelInfo
|
||||
for _, channel := range channels {
|
||||
shortID := lnwire.NewShortChanIDFromInt(channel.ChannelID)
|
||||
|
||||
lastFail, recentFail := failedOut[shortID]
|
||||
if recentFail {
|
||||
log.Debugf("Channel: %v not eligible for "+
|
||||
"suggestions, was part of a failed swap at: %v",
|
||||
channel.ChannelID, lastFail)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if existingOut[shortID] {
|
||||
log.Debugf("Channel: %v not eligible for "+
|
||||
"suggestions, ongoing loop out utilizing "+
|
||||
"channel", channel.ChannelID)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if existingIn[channel.PubKeyBytes] {
|
||||
log.Debugf("Channel: %v not eligible for "+
|
||||
"suggestions, ongoing loop in utilizing "+
|
||||
"peer", channel.ChannelID)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
eligible = append(eligible, channel)
|
||||
}
|
||||
|
||||
return eligible, nil
|
||||
}
|
||||
|
||||
// checkFeeLimits takes a set of fees for a swap and checks whether they exceed
|
||||
// our swap limits.
|
||||
func (m *Manager) checkFeeLimits(quote *loop.LoopOutQuote,
|
||||
swapAmt btcutil.Amount) error {
|
||||
|
||||
maxFee := ppmToSat(swapAmt, m.params.MaximumSwapFeePPM)
|
||||
|
||||
if quote.SwapFee > maxFee {
|
||||
return fmt.Errorf("quoted swap fee: %v > maximum swap fee: %v",
|
||||
quote.SwapFee, maxFee)
|
||||
}
|
||||
|
||||
if quote.MinerFee > m.params.MaximumMinerFee {
|
||||
return fmt.Errorf("quoted miner fee: %v > maximum miner "+
|
||||
"fee: %v", quote.MinerFee, m.params.MaximumMinerFee)
|
||||
}
|
||||
|
||||
if quote.PrepayAmount > m.params.MaximumPrepay {
|
||||
return fmt.Errorf("quoted prepay: %v > maximum prepay: %v",
|
||||
quote.PrepayAmount, m.params.MaximumPrepay)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// satPerKwToSatPerVByte converts sat per kWeight to sat per vByte.
|
||||
func satPerKwToSatPerVByte(satPerKw chainfee.SatPerKWeight) int64 {
|
||||
return int64(satPerKw.FeePerKVByte() / 1000)
|
||||
}
|
||||
|
||||
// ppmToSat takes an amount and a measure of parts per million for the amount
|
||||
// and returns the amount that the ppm represents.
|
||||
func ppmToSat(amount btcutil.Amount, ppm int) btcutil.Amount {
|
||||
return btcutil.Amount(uint64(amount) * uint64(ppm) / FeeBase)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,34 +3,138 @@ package liquidity
|
|||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/test"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
testTime = time.Date(2020, 02, 13, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
chanID1 = lnwire.NewShortChanIDFromInt(1)
|
||||
chanID2 = lnwire.NewShortChanIDFromInt(2)
|
||||
|
||||
peer1 = route.Vertex{1}
|
||||
peer2 = route.Vertex{2}
|
||||
|
||||
channel1 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID1.ToUint64(),
|
||||
PubKeyBytes: peer1,
|
||||
LocalBalance: 10000,
|
||||
RemoteBalance: 0,
|
||||
Capacity: 10000,
|
||||
}
|
||||
|
||||
channel2 = lndclient.ChannelInfo{
|
||||
ChannelID: chanID2.ToUint64(),
|
||||
PubKeyBytes: peer2,
|
||||
LocalBalance: 10000,
|
||||
RemoteBalance: 0,
|
||||
Capacity: 10000,
|
||||
}
|
||||
|
||||
// chanRule is a rule that produces chan1Rec.
|
||||
chanRule = NewThresholdRule(50, 0)
|
||||
|
||||
testQuote = &loop.LoopOutQuote{
|
||||
SwapFee: btcutil.Amount(1),
|
||||
PrepayAmount: btcutil.Amount(500),
|
||||
MinerFee: btcutil.Amount(50),
|
||||
}
|
||||
|
||||
prepayFee = ppmToSat(
|
||||
testQuote.PrepayAmount, defaultPrepayRoutingFeePPM,
|
||||
)
|
||||
routingFee = ppmToSat(7500, defaultRoutingFeePPM)
|
||||
|
||||
// chan1Rec is the suggested swap for channel 1 when we use chanRule.
|
||||
chan1Rec = loop.OutRequest{
|
||||
Amount: 7500,
|
||||
OutgoingChanSet: loopdb.ChannelSet{chanID1.ToUint64()},
|
||||
MaxPrepayRoutingFee: prepayFee,
|
||||
MaxSwapRoutingFee: routingFee,
|
||||
MaxMinerFee: defaultMaximumMinerFee,
|
||||
MaxSwapFee: testQuote.SwapFee,
|
||||
MaxPrepayAmount: testQuote.PrepayAmount,
|
||||
SweepConfTarget: loop.DefaultSweepConfTarget,
|
||||
}
|
||||
|
||||
// chan2Rec is the suggested swap for channel 2 when we use chanRule.
|
||||
chan2Rec = loop.OutRequest{
|
||||
Amount: 7500,
|
||||
OutgoingChanSet: loopdb.ChannelSet{chanID2.ToUint64()},
|
||||
MaxPrepayRoutingFee: prepayFee,
|
||||
MaxSwapRoutingFee: routingFee,
|
||||
MaxMinerFee: defaultMaximumMinerFee,
|
||||
MaxPrepayAmount: testQuote.PrepayAmount,
|
||||
MaxSwapFee: testQuote.SwapFee,
|
||||
SweepConfTarget: loop.DefaultSweepConfTarget,
|
||||
}
|
||||
|
||||
// chan1Out is a contract that uses channel 1, used to represent on
|
||||
// disk swap using chan 1.
|
||||
chan1Out = &loopdb.LoopOutContract{
|
||||
OutgoingChanSet: loopdb.ChannelSet(
|
||||
[]uint64{
|
||||
chanID1.ToUint64(),
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
// newTestConfig creates a default test config.
|
||||
func newTestConfig() *Config {
|
||||
func newTestConfig() (*Config, *test.LndMockServices) {
|
||||
lnd := test.NewMockLnd()
|
||||
|
||||
// Set our fee estimate for the default number of confirmations to our
|
||||
// limit so that our fees will be ok by default.
|
||||
lnd.SetFeeEstimate(
|
||||
defaultParameters.SweepConfTarget,
|
||||
defaultParameters.SweepFeeRateLimit,
|
||||
)
|
||||
|
||||
return &Config{
|
||||
LoopOutRestrictions: func(_ context.Context) (*Restrictions,
|
||||
error) {
|
||||
|
||||
return NewRestrictions(1, 10000), nil
|
||||
},
|
||||
Lnd: test.NewMockLnd().Client,
|
||||
}
|
||||
Lnd: &lnd.LndServices,
|
||||
Clock: clock.NewTestClock(testTime),
|
||||
ListLoopOut: func() ([]*loopdb.LoopOut, error) {
|
||||
return nil, nil
|
||||
},
|
||||
ListLoopIn: func() ([]*loopdb.LoopIn, error) {
|
||||
return nil, nil
|
||||
},
|
||||
LoopOutQuote: func(_ context.Context,
|
||||
_ *loop.LoopOutQuoteRequest) (*loop.LoopOutQuote,
|
||||
error) {
|
||||
|
||||
return testQuote, nil
|
||||
},
|
||||
}, lnd
|
||||
}
|
||||
|
||||
// TestParameters tests getting and setting of parameters for our manager.
|
||||
func TestParameters(t *testing.T) {
|
||||
manager := NewManager(newTestConfig())
|
||||
cfg, _ := newTestConfig()
|
||||
manager := NewManager(cfg)
|
||||
|
||||
chanID := lnwire.NewShortChanIDFromInt(1)
|
||||
|
||||
// Start with the case where we have no rules set.
|
||||
startParams := manager.GetParameters()
|
||||
require.Equal(t, newParameters(), startParams)
|
||||
require.Equal(t, defaultParameters, startParams)
|
||||
|
||||
// Mutate the parameters returned by our get function.
|
||||
startParams.ChannelRules[chanID] = NewThresholdRule(1, 1)
|
||||
|
|
@ -38,15 +142,14 @@ func TestParameters(t *testing.T) {
|
|||
// Make sure that we have not mutated the liquidity manager's params
|
||||
// by making this change.
|
||||
params := manager.GetParameters()
|
||||
require.Equal(t, newParameters(), params)
|
||||
require.Equal(t, defaultParameters, params)
|
||||
|
||||
// Provide a valid set of parameters and validate assert that they are
|
||||
// set.
|
||||
originalRule := NewThresholdRule(10, 10)
|
||||
expected := Parameters{
|
||||
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID: originalRule,
|
||||
},
|
||||
expected := defaultParameters
|
||||
expected.ChannelRules = map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID: originalRule,
|
||||
}
|
||||
|
||||
err := manager.SetParameters(expected)
|
||||
|
|
@ -68,62 +171,272 @@ func TestParameters(t *testing.T) {
|
|||
require.Equal(t, ErrZeroChannelID, err)
|
||||
}
|
||||
|
||||
// TestSuggestSwaps tests getting of swap suggestions.
|
||||
func TestSuggestSwaps(t *testing.T) {
|
||||
// TestRestrictedSuggestions tests getting of swap suggestions when we have
|
||||
// other in-flight swaps. We setup our manager with a set of channels and rules
|
||||
// that require a loop out swap, focusing on the filtering our of channels that
|
||||
// are in use for in-flight swaps, or those which have recently failed.
|
||||
func TestRestrictedSuggestions(t *testing.T) {
|
||||
var (
|
||||
chanID1 = lnwire.NewShortChanIDFromInt(1)
|
||||
chanID2 = lnwire.NewShortChanIDFromInt(2)
|
||||
failedWithinTimeout = &loopdb.LoopEvent{
|
||||
SwapStateData: loopdb.SwapStateData{
|
||||
State: loopdb.StateFailOffchainPayments,
|
||||
},
|
||||
Time: testTime,
|
||||
}
|
||||
|
||||
failedBeforeBackoff = &loopdb.LoopEvent{
|
||||
SwapStateData: loopdb.SwapStateData{
|
||||
State: loopdb.StateFailOffchainPayments,
|
||||
},
|
||||
Time: testTime.Add(
|
||||
defaultFailureBackoff * -1,
|
||||
),
|
||||
}
|
||||
|
||||
// failedTemporary is a swap that failed outside of our backoff
|
||||
// period, but we still want to back off because the swap is
|
||||
// considered pending.
|
||||
failedTemporary = &loopdb.LoopEvent{
|
||||
SwapStateData: loopdb.SwapStateData{
|
||||
State: loopdb.StateFailTemporary,
|
||||
},
|
||||
Time: testTime.Add(
|
||||
defaultFailureBackoff * -3,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
channels []lndclient.ChannelInfo
|
||||
parameters Parameters
|
||||
swaps []*LoopOutRecommendation
|
||||
name string
|
||||
channels []lndclient.ChannelInfo
|
||||
loopOut []*loopdb.LoopOut
|
||||
loopIn []*loopdb.LoopIn
|
||||
expected []loop.OutRequest
|
||||
}{
|
||||
{
|
||||
name: "no rules",
|
||||
channels: nil,
|
||||
parameters: newParameters(),
|
||||
name: "no existing swaps",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
},
|
||||
loopOut: nil,
|
||||
loopIn: nil,
|
||||
expected: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unrestricted loop out",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1, channel2,
|
||||
},
|
||||
loopOut: []*loopdb.LoopOut{
|
||||
{
|
||||
Contract: &loopdb.LoopOutContract{
|
||||
OutgoingChanSet: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "unrestricted loop in",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1, channel2,
|
||||
},
|
||||
loopIn: []*loopdb.LoopIn{
|
||||
{
|
||||
Contract: &loopdb.LoopInContract{
|
||||
LastHop: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "restricted loop out",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1, channel2,
|
||||
},
|
||||
loopOut: []*loopdb.LoopOut{
|
||||
{
|
||||
Contract: chan1Out,
|
||||
},
|
||||
},
|
||||
expected: []loop.OutRequest{
|
||||
chan2Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "restricted loop in",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1, channel2,
|
||||
},
|
||||
loopIn: []*loopdb.LoopIn{
|
||||
{
|
||||
Contract: &loopdb.LoopInContract{
|
||||
LastHop: &peer2,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "swap failed recently",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
},
|
||||
loopOut: []*loopdb.LoopOut{
|
||||
{
|
||||
Contract: chan1Out,
|
||||
Loop: loopdb.Loop{
|
||||
Events: []*loopdb.LoopEvent{
|
||||
failedWithinTimeout,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "swap failed before cutoff",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
},
|
||||
loopOut: []*loopdb.LoopOut{
|
||||
{
|
||||
Contract: chan1Out,
|
||||
Loop: loopdb.Loop{
|
||||
Events: []*loopdb.LoopEvent{
|
||||
failedBeforeBackoff,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "temporary failure",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
},
|
||||
loopOut: []*loopdb.LoopOut{
|
||||
{
|
||||
Contract: chan1Out,
|
||||
Loop: loopdb.Loop{
|
||||
Events: []*loopdb.LoopEvent{
|
||||
failedTemporary,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
// Create a manager config which will return the test
|
||||
// case's set of existing swaps.
|
||||
cfg, lnd := newTestConfig()
|
||||
cfg.ListLoopOut = func() ([]*loopdb.LoopOut, error) {
|
||||
return testCase.loopOut, nil
|
||||
}
|
||||
cfg.ListLoopIn = func() ([]*loopdb.LoopIn, error) {
|
||||
return testCase.loopIn, nil
|
||||
}
|
||||
|
||||
rules := map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID1: chanRule,
|
||||
chanID2: chanRule,
|
||||
}
|
||||
|
||||
testSuggestSwaps(
|
||||
t, cfg, lnd, testCase.channels, rules,
|
||||
testCase.expected,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSweepFeeLimit tests getting of swap suggestions when our estimated sweep
|
||||
// fee is above and below the configured limit.
|
||||
func TestSweepFeeLimit(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
feeRate chainfee.SatPerKWeight
|
||||
swaps []loop.OutRequest
|
||||
}{
|
||||
{
|
||||
name: "fee estimate ok",
|
||||
feeRate: defaultSweepFeeRateLimit,
|
||||
swaps: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "fee estimate above limit",
|
||||
feeRate: defaultSweepFeeRateLimit + 1,
|
||||
swaps: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
cfg, lnd := newTestConfig()
|
||||
|
||||
// Set our test case's fee rate for our mock lnd.
|
||||
lnd.SetFeeEstimate(
|
||||
loop.DefaultSweepConfTarget, testCase.feeRate,
|
||||
)
|
||||
|
||||
channels := []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
}
|
||||
|
||||
rules := map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID1: chanRule,
|
||||
}
|
||||
|
||||
testSuggestSwaps(
|
||||
t, cfg, lnd, channels, rules, testCase.swaps,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSuggestSwaps tests getting of swap suggestions based on the rules set for
|
||||
// the liquidity manager and the current set of channel balances.
|
||||
func TestSuggestSwaps(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rules map[lnwire.ShortChannelID]*ThresholdRule
|
||||
swaps []loop.OutRequest
|
||||
}{
|
||||
{
|
||||
name: "no rules",
|
||||
rules: map[lnwire.ShortChannelID]*ThresholdRule{},
|
||||
},
|
||||
{
|
||||
name: "loop out",
|
||||
channels: []lndclient.ChannelInfo{
|
||||
{
|
||||
ChannelID: 1,
|
||||
Capacity: 1000,
|
||||
LocalBalance: 1000,
|
||||
RemoteBalance: 0,
|
||||
},
|
||||
rules: map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID1: chanRule,
|
||||
},
|
||||
parameters: Parameters{
|
||||
ChannelRules: map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID1: NewThresholdRule(
|
||||
10, 10,
|
||||
),
|
||||
},
|
||||
},
|
||||
swaps: []*LoopOutRecommendation{
|
||||
{
|
||||
Channel: chanID1,
|
||||
Amount: 500,
|
||||
},
|
||||
swaps: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
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),
|
||||
},
|
||||
rules: map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID2: NewThresholdRule(10, 10),
|
||||
},
|
||||
swaps: nil,
|
||||
},
|
||||
|
|
@ -133,23 +446,110 @@ func TestSuggestSwaps(t *testing.T) {
|
|||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
cfg := newTestConfig()
|
||||
cfg, lnd := 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
|
||||
channels := []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
}
|
||||
|
||||
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)
|
||||
testSuggestSwaps(
|
||||
t, cfg, lnd, channels, testCase.rules,
|
||||
testCase.swaps,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFeeLimits tests limiting of swap suggestions by fees.
|
||||
func TestFeeLimits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
quote *loop.LoopOutQuote
|
||||
expected []loop.OutRequest
|
||||
}{
|
||||
{
|
||||
name: "fees ok",
|
||||
quote: testQuote,
|
||||
expected: []loop.OutRequest{
|
||||
chan1Rec,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "insufficient prepay",
|
||||
quote: &loop.LoopOutQuote{
|
||||
SwapFee: 1,
|
||||
PrepayAmount: defaultMaximumPrepay + 1,
|
||||
MinerFee: 50,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "insufficient miner fee",
|
||||
quote: &loop.LoopOutQuote{
|
||||
SwapFee: 1,
|
||||
PrepayAmount: 100,
|
||||
MinerFee: defaultMaximumMinerFee + 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Swap fee limited to 0.5% of 7500 = 37,5.
|
||||
name: "insufficient swap fee",
|
||||
quote: &loop.LoopOutQuote{
|
||||
SwapFee: 38,
|
||||
PrepayAmount: 100,
|
||||
MinerFee: 500,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range tests {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
cfg, lnd := newTestConfig()
|
||||
cfg.LoopOutQuote = func(context.Context,
|
||||
*loop.LoopOutQuoteRequest) (*loop.LoopOutQuote,
|
||||
error) {
|
||||
|
||||
return testCase.quote, nil
|
||||
}
|
||||
|
||||
channels := []lndclient.ChannelInfo{
|
||||
channel1,
|
||||
}
|
||||
rules := map[lnwire.ShortChannelID]*ThresholdRule{
|
||||
chanID1: chanRule,
|
||||
}
|
||||
|
||||
testSuggestSwaps(
|
||||
t, cfg, lnd, channels, rules, testCase.expected,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// testSuggestSwaps tests getting swap suggestions.
|
||||
func testSuggestSwaps(t *testing.T, cfg *Config, lnd *test.LndMockServices,
|
||||
channels []lndclient.ChannelInfo,
|
||||
rules map[lnwire.ShortChannelID]*ThresholdRule,
|
||||
expected []loop.OutRequest) {
|
||||
|
||||
t.Parallel()
|
||||
|
||||
// Create a mock lnd with the set of channels set in our test case and
|
||||
// update our test case lnd to use these channels.
|
||||
lnd.Channels = channels
|
||||
|
||||
// Create a new manager, get our current set of parameters and update
|
||||
// them to use the rules set by the test.
|
||||
manager := NewManager(cfg)
|
||||
|
||||
currentParams := manager.GetParameters()
|
||||
currentParams.ChannelRules = rules
|
||||
|
||||
err := manager.SetParameters(currentParams)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := manager.SuggestSwaps(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, actual)
|
||||
}
|
||||
|
|
|
|||
26
liquidity/log.go
Normal file
26
liquidity/log.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package liquidity
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
)
|
||||
|
||||
// Subsystem defines the sub system name of this package.
|
||||
const Subsystem = "LQDY"
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
UseLogger(build.NewSubLogger(Subsystem, nil))
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package liquidity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
|
@ -15,6 +17,12 @@ type LoopOutRecommendation struct {
|
|||
Channel lnwire.ShortChannelID
|
||||
}
|
||||
|
||||
// String returns a string representation of a loop out recommendation.
|
||||
func (l *LoopOutRecommendation) String() string {
|
||||
return fmt.Sprintf("loop out: %v over %v", l.Amount,
|
||||
l.Channel.ToUint64())
|
||||
}
|
||||
|
||||
// newLoopOutRecommendation creates a new loop out swap.
|
||||
func newLoopOutRecommendation(amount btcutil.Amount,
|
||||
channelID lnwire.ShortChannelID) *LoopOutRecommendation {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/btcsuite/btclog"
|
||||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/liquidity"
|
||||
"github.com/lightninglabs/loop/loopdb"
|
||||
"github.com/lightninglabs/loop/lsat"
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
|
|
@ -21,6 +22,7 @@ func init() {
|
|||
addSubLogger("LNDC", lndclient.UseLogger)
|
||||
addSubLogger("STORE", loopdb.UseLogger)
|
||||
addSubLogger(lsat.Subsystem, lsat.UseLogger)
|
||||
addSubLogger(liquidity.Subsystem, liquidity.UseLogger)
|
||||
}
|
||||
|
||||
// addSubLogger is a helper method to conveniently create and register the
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/lightninglabs/loop/looprpc"
|
||||
"github.com/lightninglabs/loop/swap"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/queue"
|
||||
"github.com/lightningnetwork/lnd/routing/route"
|
||||
|
|
@ -555,7 +556,17 @@ func (s *swapClientServer) GetLiquidityParams(_ context.Context,
|
|||
|
||||
cfg := s.liquidityMgr.GetParameters()
|
||||
|
||||
satPerByte := cfg.SweepFeeRateLimit.FeePerKVByte() / 1000
|
||||
|
||||
rpcCfg := &looprpc.LiquidityParameters{
|
||||
MaxMinerFeeSat: uint64(cfg.MaximumMinerFee),
|
||||
MaxSwapFeePpm: uint64(cfg.MaximumSwapFeePPM),
|
||||
MaxRoutingFeePpm: uint64(cfg.MaximumRoutingFeePPM),
|
||||
MaxPrepayRoutingFeePpm: uint64(cfg.MaximumPrepayRoutingFeePPM),
|
||||
MaxPrepaySat: uint64(cfg.MaximumPrepay),
|
||||
SweepFeeRateSatPerVbyte: uint64(satPerByte),
|
||||
SweepConfTarget: cfg.SweepConfTarget,
|
||||
FailureBackoffSec: uint64(cfg.FailureBackOff.Seconds()),
|
||||
Rules: make(
|
||||
[]*looprpc.LiquidityRule, 0, len(cfg.ChannelRules),
|
||||
),
|
||||
|
|
@ -581,7 +592,20 @@ func (s *swapClientServer) SetLiquidityParams(_ context.Context,
|
|||
in *looprpc.SetLiquidityParamsRequest) (*looprpc.SetLiquidityParamsResponse,
|
||||
error) {
|
||||
|
||||
satPerVbyte := chainfee.SatPerKVByte(
|
||||
in.Parameters.SweepFeeRateSatPerVbyte * 1000,
|
||||
)
|
||||
|
||||
params := liquidity.Parameters{
|
||||
MaximumMinerFee: btcutil.Amount(in.Parameters.MaxMinerFeeSat),
|
||||
MaximumSwapFeePPM: int(in.Parameters.MaxSwapFeePpm),
|
||||
MaximumRoutingFeePPM: int(in.Parameters.MaxRoutingFeePpm),
|
||||
MaximumPrepayRoutingFeePPM: int(in.Parameters.MaxPrepayRoutingFeePpm),
|
||||
MaximumPrepay: btcutil.Amount(in.Parameters.MaxPrepaySat),
|
||||
SweepFeeRateLimit: satPerVbyte.FeePerKWeight(),
|
||||
SweepConfTarget: in.Parameters.SweepConfTarget,
|
||||
FailureBackOff: time.Duration(in.Parameters.FailureBackoffSec) *
|
||||
time.Second,
|
||||
ChannelRules: make(
|
||||
map[lnwire.ShortChannelID]*liquidity.ThresholdRule,
|
||||
len(in.Parameters.Rules),
|
||||
|
|
@ -646,10 +670,14 @@ func (s *swapClientServer) SuggestSwaps(ctx context.Context,
|
|||
|
||||
for _, swap := range swaps {
|
||||
loopOut = append(loopOut, &looprpc.LoopOutRequest{
|
||||
Amt: int64(swap.Amount),
|
||||
OutgoingChanSet: []uint64{
|
||||
swap.Channel.ToUint64(),
|
||||
},
|
||||
Amt: int64(swap.Amount),
|
||||
OutgoingChanSet: swap.OutgoingChanSet,
|
||||
MaxSwapFee: int64(swap.MaxSwapFee),
|
||||
MaxMinerFee: int64(swap.MaxMinerFee),
|
||||
MaxPrepayAmt: int64(swap.MaxPrepayAmount),
|
||||
MaxSwapRoutingFee: int64(swap.MaxSwapRoutingFee),
|
||||
MaxPrepayRoutingFee: int64(swap.MaxPrepayRoutingFee),
|
||||
SweepConfTarget: swap.SweepConfTarget,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/lightninglabs/lndclient"
|
||||
"github.com/lightninglabs/loop"
|
||||
"github.com/lightninglabs/loop/liquidity"
|
||||
"github.com/lightningnetwork/lnd/clock"
|
||||
)
|
||||
|
||||
// getClient returns an instance of the swap client.
|
||||
|
|
@ -46,7 +47,12 @@ func getLiquidityManager(client *loop.Client) *liquidity.Manager {
|
|||
outTerms.MinSwapAmount, outTerms.MaxSwapAmount,
|
||||
), nil
|
||||
},
|
||||
Lnd: client.LndServices.Client,
|
||||
Lnd: client.LndServices,
|
||||
Clock: clock.NewDefaultClock(),
|
||||
LoopOutQuote: client.LoopOutQuote,
|
||||
ListLoopOut: client.Store.FetchLoopOutSwaps,
|
||||
ListLoopIn: client.Store.FetchLoopInSwaps,
|
||||
MinimumConfirmations: minConfTarget,
|
||||
}
|
||||
|
||||
return liquidity.NewManager(mngrCfg)
|
||||
|
|
|
|||
|
|
@ -1543,10 +1543,46 @@ var xxx_messageInfo_GetLiquidityParamsRequest proto.InternalMessageInfo
|
|||
type LiquidityParameters struct {
|
||||
//
|
||||
//A set of liquidity rules that describe the desired liquidity balance.
|
||||
Rules []*LiquidityRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
Rules []*LiquidityRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
|
||||
//
|
||||
//The limit we place on our estimated sweep cost for a swap in sat/vByte. If
|
||||
//the estimated fee for our sweep transaction within the specified
|
||||
//confirmation target is above this value, we will not suggest any swaps.
|
||||
SweepFeeRateSatPerVbyte uint64 `protobuf:"varint,2,opt,name=sweep_fee_rate_sat_per_vbyte,json=sweepFeeRateSatPerVbyte,proto3" json:"sweep_fee_rate_sat_per_vbyte,omitempty"`
|
||||
//
|
||||
//The maximum fee paid to the server for facilitating the swap, expressed
|
||||
//as parts per million of the swap volume.
|
||||
MaxSwapFeePpm uint64 `protobuf:"varint,3,opt,name=max_swap_fee_ppm,json=maxSwapFeePpm,proto3" json:"max_swap_fee_ppm,omitempty"`
|
||||
//
|
||||
//The maximum fee paid to route the swap invoice off chain, expressed as
|
||||
//parts per million of the volume being routed.
|
||||
MaxRoutingFeePpm uint64 `protobuf:"varint,4,opt,name=max_routing_fee_ppm,json=maxRoutingFeePpm,proto3" json:"max_routing_fee_ppm,omitempty"`
|
||||
//
|
||||
//The maximum fee paid to route the prepay invoice off chain, expressed as
|
||||
//parts per million of the volume being routed.
|
||||
MaxPrepayRoutingFeePpm uint64 `protobuf:"varint,5,opt,name=max_prepay_routing_fee_ppm,json=maxPrepayRoutingFeePpm,proto3" json:"max_prepay_routing_fee_ppm,omitempty"`
|
||||
//
|
||||
//The maximum no-show penalty in satoshis paid for a swap.
|
||||
MaxPrepaySat uint64 `protobuf:"varint,6,opt,name=max_prepay_sat,json=maxPrepaySat,proto3" json:"max_prepay_sat,omitempty"`
|
||||
//
|
||||
//The maximum miner fee we will pay to sweep the swap on chain. Note that we
|
||||
//will not suggest a swap if the estimate is above the sweep limit set by
|
||||
//these parameters, and we use the current fee estimate to sweep on chain so
|
||||
//this value is only a cap placed on the amount we spend on fees in the case
|
||||
//where the swap needs to be claimed on chain, but fees have suddenly spiked.
|
||||
MaxMinerFeeSat uint64 `protobuf:"varint,7,opt,name=max_miner_fee_sat,json=maxMinerFeeSat,proto3" json:"max_miner_fee_sat,omitempty"`
|
||||
//
|
||||
//The number of blocks from the on-chain HTLC's confirmation height that it
|
||||
//should be swept within.
|
||||
SweepConfTarget int32 `protobuf:"varint,8,opt,name=sweep_conf_target,json=sweepConfTarget,proto3" json:"sweep_conf_target,omitempty"`
|
||||
//
|
||||
//The amount of time we require pass since a channel was part of a failed
|
||||
//swap due to off chain payment failure until it will be considered for swap
|
||||
//suggestions again, expressed in seconds.
|
||||
FailureBackoffSec uint64 `protobuf:"varint,9,opt,name=failure_backoff_sec,json=failureBackoffSec,proto3" json:"failure_backoff_sec,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) Reset() { *m = LiquidityParameters{} }
|
||||
|
|
@ -1581,6 +1617,62 @@ func (m *LiquidityParameters) GetRules() []*LiquidityRule {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetSweepFeeRateSatPerVbyte() uint64 {
|
||||
if m != nil {
|
||||
return m.SweepFeeRateSatPerVbyte
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetMaxSwapFeePpm() uint64 {
|
||||
if m != nil {
|
||||
return m.MaxSwapFeePpm
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetMaxRoutingFeePpm() uint64 {
|
||||
if m != nil {
|
||||
return m.MaxRoutingFeePpm
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetMaxPrepayRoutingFeePpm() uint64 {
|
||||
if m != nil {
|
||||
return m.MaxPrepayRoutingFeePpm
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetMaxPrepaySat() uint64 {
|
||||
if m != nil {
|
||||
return m.MaxPrepaySat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetMaxMinerFeeSat() uint64 {
|
||||
if m != nil {
|
||||
return m.MaxMinerFeeSat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetSweepConfTarget() int32 {
|
||||
if m != nil {
|
||||
return m.SweepConfTarget
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *LiquidityParameters) GetFailureBackoffSec() uint64 {
|
||||
if m != nil {
|
||||
return m.FailureBackoffSec
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type LiquidityRule struct {
|
||||
//
|
||||
//The short channel ID of the channel that this rule should be applied to.
|
||||
|
|
@ -1838,141 +1930,150 @@ func init() {
|
|||
func init() { proto.RegisterFile("client.proto", fileDescriptor_014de31d7ac8c57c) }
|
||||
|
||||
var fileDescriptor_014de31d7ac8c57c = []byte{
|
||||
// 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,
|
||||
// 2280 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0xcf, 0x6f, 0x22, 0xc9,
|
||||
0xf5, 0x1f, 0xa0, 0x31, 0xf0, 0x68, 0xa0, 0x5d, 0x9e, 0xb1, 0x31, 0xeb, 0xd5, 0x7a, 0x7a, 0x76,
|
||||
0xbe, 0xeb, 0xf1, 0x77, 0xc7, 0x64, 0xbd, 0xa7, 0x8c, 0x76, 0x23, 0x31, 0x18, 0xaf, 0x71, 0x6c,
|
||||
0x20, 0x0d, 0x9e, 0xd5, 0x44, 0x91, 0x5a, 0x65, 0x28, 0xdb, 0xad, 0xa5, 0x7f, 0x4c, 0x77, 0x31,
|
||||
0x63, 0x6b, 0x95, 0x44, 0xca, 0x3f, 0xb0, 0x87, 0xfc, 0x07, 0xf9, 0x1b, 0x72, 0x4b, 0x6e, 0xb9,
|
||||
0xe6, 0x94, 0x1c, 0x73, 0x8d, 0x14, 0xe5, 0x90, 0xff, 0x21, 0xaa, 0x57, 0xdd, 0x4d, 0x37, 0x06,
|
||||
0x47, 0x39, 0xe4, 0x46, 0xbf, 0xf7, 0xa9, 0x57, 0xf5, 0x7e, 0xd6, 0xa7, 0x00, 0x75, 0x3c, 0xb5,
|
||||
0x98, 0xc3, 0x0f, 0x3c, 0xdf, 0xe5, 0x2e, 0x29, 0x4c, 0x5d, 0xd7, 0xf3, 0xbd, 0x71, 0x63, 0xe7,
|
||||
0xda, 0x75, 0xaf, 0xa7, 0xac, 0x49, 0x3d, 0xab, 0x49, 0x1d, 0xc7, 0xe5, 0x94, 0x5b, 0xae, 0x13,
|
||||
0x48, 0x98, 0xfe, 0x83, 0x02, 0xd5, 0x33, 0xd7, 0xf5, 0xfa, 0x33, 0x6e, 0xb0, 0x77, 0x33, 0x16,
|
||||
0x70, 0xa2, 0x41, 0x8e, 0xda, 0xbc, 0x9e, 0xd9, 0xcd, 0xec, 0xe5, 0x0c, 0xf1, 0x93, 0x10, 0x50,
|
||||
0x26, 0x2c, 0xe0, 0xf5, 0xec, 0x6e, 0x66, 0xaf, 0x64, 0xe0, 0x6f, 0xd2, 0x84, 0xc7, 0x36, 0xbd,
|
||||
0x35, 0x83, 0x0f, 0xd4, 0x33, 0x7d, 0x77, 0xc6, 0x2d, 0xe7, 0xda, 0xbc, 0x62, 0xac, 0x9e, 0xc3,
|
||||
0x65, 0xeb, 0x36, 0xbd, 0x1d, 0x7e, 0xa0, 0x9e, 0x21, 0x35, 0xc7, 0x8c, 0x91, 0x2f, 0x61, 0x53,
|
||||
0x2c, 0xf0, 0x7c, 0xe6, 0xd1, 0xbb, 0xd4, 0x12, 0x05, 0x97, 0x6c, 0xd8, 0xf4, 0x76, 0x80, 0xca,
|
||||
0xc4, 0xa2, 0x5d, 0x50, 0xe3, 0x5d, 0x04, 0x34, 0x8f, 0x50, 0x08, 0xad, 0x0b, 0xc4, 0xa7, 0x50,
|
||||
0x4d, 0x98, 0x15, 0x07, 0x5f, 0x43, 0x8c, 0x1a, 0x9b, 0x6b, 0xd9, 0x9c, 0xe8, 0x50, 0x11, 0x28,
|
||||
0xdb, 0x72, 0x98, 0x8f, 0x86, 0x0a, 0x08, 0x2a, 0xdb, 0xf4, 0xf6, 0x5c, 0xc8, 0x84, 0xa5, 0xcf,
|
||||
0x41, 0x13, 0x31, 0x33, 0xdd, 0x19, 0x37, 0xc7, 0x37, 0xd4, 0x71, 0xd8, 0xb4, 0x5e, 0xdc, 0xcd,
|
||||
0xec, 0x29, 0xaf, 0xb3, 0xf5, 0x8c, 0x51, 0x9d, 0xca, 0x28, 0xb5, 0xa5, 0x86, 0xec, 0xc3, 0xba,
|
||||
0x3b, 0xe3, 0xd7, 0xae, 0x70, 0x42, 0xa0, 0xcd, 0x80, 0xf1, 0x7a, 0x79, 0x37, 0xb7, 0xa7, 0x18,
|
||||
0xb5, 0x48, 0x21, 0xb0, 0x43, 0xc6, 0x05, 0x36, 0xf8, 0xc0, 0x98, 0x67, 0x8e, 0x5d, 0xe7, 0xca,
|
||||
0xe4, 0xd4, 0xbf, 0x66, 0xbc, 0x5e, 0xda, 0xcd, 0xec, 0xe5, 0x8d, 0x1a, 0x2a, 0xda, 0xae, 0x73,
|
||||
0x35, 0x42, 0x31, 0x79, 0x09, 0xe4, 0x86, 0x4f, 0xc7, 0x08, 0xb5, 0x7c, 0x5b, 0x26, 0xab, 0x5e,
|
||||
0x41, 0xf0, 0xba, 0xd0, 0xb4, 0x93, 0x0a, 0xf2, 0x0a, 0xb6, 0x31, 0x38, 0xde, 0xec, 0x72, 0x6a,
|
||||
0x8d, 0x51, 0x68, 0x4e, 0x18, 0x9d, 0x4c, 0x2d, 0x87, 0xd5, 0x41, 0x9c, 0xde, 0xd8, 0x12, 0x80,
|
||||
0xc1, 0x5c, 0x7f, 0x14, 0xaa, 0xc9, 0x63, 0xc8, 0x4f, 0xe9, 0x25, 0x9b, 0xd6, 0x55, 0xcc, 0xab,
|
||||
0xfc, 0xd0, 0xff, 0x91, 0x81, 0x8a, 0xa8, 0x88, 0xae, 0xb3, 0xba, 0x20, 0x16, 0xd3, 0x92, 0xbd,
|
||||
0x97, 0x96, 0x7b, 0x01, 0xcf, 0xdd, 0x0f, 0xf8, 0x36, 0x14, 0xa7, 0x34, 0xe0, 0xe6, 0x8d, 0xeb,
|
||||
0x61, 0x0d, 0xa8, 0x46, 0x41, 0x7c, 0x9f, 0xb8, 0x1e, 0x79, 0x06, 0x15, 0x76, 0xcb, 0x99, 0xef,
|
||||
0xd0, 0xa9, 0x29, 0x9c, 0xc6, 0xc4, 0x17, 0x0d, 0x35, 0x12, 0x9e, 0xf0, 0xe9, 0x98, 0xec, 0x81,
|
||||
0x16, 0x87, 0x2a, 0x8a, 0xea, 0x1a, 0x06, 0xaa, 0x1a, 0x05, 0x2a, 0x0c, 0x6a, 0xec, 0x69, 0x21,
|
||||
0xe9, 0xe9, 0x3f, 0x33, 0xa0, 0x62, 0x91, 0xb2, 0xc0, 0x73, 0x9d, 0x80, 0x11, 0x02, 0x59, 0x6b,
|
||||
0x82, 0x7e, 0x96, 0x30, 0xe7, 0x59, 0x6b, 0x22, 0x0e, 0x69, 0x4d, 0xcc, 0xcb, 0x3b, 0xce, 0x02,
|
||||
0xf4, 0x41, 0x35, 0x0a, 0xd6, 0xe4, 0xb5, 0xf8, 0x24, 0xcf, 0x41, 0xc5, 0xfd, 0xe9, 0x64, 0xe2,
|
||||
0xb3, 0x20, 0x90, 0xed, 0x81, 0x0b, 0xcb, 0x42, 0xde, 0x92, 0x62, 0x72, 0x00, 0x1b, 0x49, 0x98,
|
||||
0xe9, 0x78, 0x87, 0x1f, 0x82, 0x1b, 0xf4, 0xb8, 0x24, 0x53, 0x1a, 0x22, 0x7b, 0xa8, 0x20, 0x9f,
|
||||
0x87, 0x15, 0x10, 0xe1, 0x25, 0x3c, 0x8f, 0x70, 0x2d, 0x01, 0x1f, 0x20, 0xfa, 0x39, 0x54, 0x03,
|
||||
0xe6, 0xbf, 0x67, 0xbe, 0x69, 0xb3, 0x20, 0xa0, 0xd7, 0x0c, 0x43, 0x50, 0x32, 0x2a, 0x52, 0x7a,
|
||||
0x2e, 0x85, 0xba, 0x06, 0xd5, 0x73, 0xd7, 0xb1, 0xb8, 0xeb, 0x87, 0x59, 0xd5, 0x7f, 0xaf, 0x00,
|
||||
0x08, 0xef, 0x87, 0x9c, 0xf2, 0x59, 0xb0, 0xb4, 0xeb, 0x45, 0x34, 0xb2, 0x2b, 0xa3, 0x51, 0x5e,
|
||||
0x8c, 0x86, 0xc2, 0xef, 0x3c, 0x99, 0xe8, 0xea, 0xe1, 0xfa, 0x41, 0x38, 0x7f, 0x0e, 0xc4, 0x1e,
|
||||
0xa3, 0x3b, 0x8f, 0x19, 0xa8, 0x26, 0x7b, 0x90, 0x0f, 0x38, 0xe5, 0xb2, 0xeb, 0xab, 0x87, 0x24,
|
||||
0x85, 0x13, 0x67, 0x61, 0x86, 0x04, 0x90, 0xaf, 0xa1, 0x7a, 0x45, 0xad, 0xe9, 0xcc, 0x67, 0xa6,
|
||||
0xcf, 0x68, 0xe0, 0x3a, 0xf5, 0x2a, 0x2e, 0xd9, 0x8c, 0x97, 0x1c, 0x4b, 0xb5, 0x81, 0x5a, 0xa3,
|
||||
0x72, 0x95, 0xfc, 0x24, 0x9f, 0x41, 0xcd, 0x72, 0x2c, 0x6e, 0xc9, 0x9e, 0xe0, 0x96, 0x1d, 0x4d,
|
||||
0x8f, 0xea, 0x5c, 0x3c, 0xb2, 0x6c, 0x71, 0x22, 0x0d, 0xcb, 0x70, 0xe6, 0x4d, 0x28, 0x67, 0x12,
|
||||
0x29, 0x67, 0x48, 0x55, 0xc8, 0x2f, 0x50, 0x8c, 0xc8, 0xc5, 0x84, 0x17, 0x96, 0x27, 0x7c, 0x79,
|
||||
0x02, 0xd5, 0x15, 0x09, 0x5c, 0x51, 0x1e, 0x95, 0x55, 0xe5, 0xf1, 0x09, 0x94, 0xc7, 0x6e, 0xc0,
|
||||
0x4d, 0x99, 0x5f, 0x9c, 0x50, 0x39, 0x03, 0x84, 0x68, 0x88, 0x12, 0xf2, 0x14, 0x54, 0x04, 0xb8,
|
||||
0xce, 0xf8, 0x86, 0x5a, 0x0e, 0x0e, 0x9a, 0x9c, 0x81, 0x8b, 0xfa, 0x52, 0x24, 0xda, 0x4b, 0x42,
|
||||
0xae, 0xae, 0x24, 0x06, 0xe4, 0xcc, 0x44, 0x4c, 0x28, 0x9b, 0x37, 0x4d, 0x2d, 0xd9, 0x34, 0x04,
|
||||
0xb4, 0x33, 0x2b, 0xe0, 0x22, 0x5b, 0x41, 0x54, 0x4a, 0x3f, 0x81, 0xf5, 0x84, 0x2c, 0x6c, 0xa6,
|
||||
0x17, 0x90, 0x17, 0xf3, 0x21, 0xa8, 0x67, 0x76, 0x73, 0x7b, 0xe5, 0xc3, 0x8d, 0x7b, 0x89, 0x9e,
|
||||
0x05, 0x86, 0x44, 0xe8, 0x4f, 0xa1, 0x26, 0x84, 0x5d, 0xe7, 0xca, 0x8d, 0x66, 0x4e, 0x35, 0x6e,
|
||||
0x45, 0x55, 0x14, 0x9e, 0x5e, 0x05, 0x75, 0xc4, 0x7c, 0x3b, 0xde, 0xf2, 0xd7, 0x50, 0xeb, 0x3a,
|
||||
0xa1, 0x24, 0xdc, 0xf0, 0xff, 0xa0, 0x66, 0x5b, 0x8e, 0x1c, 0x4a, 0xd4, 0x76, 0x67, 0x0e, 0x0f,
|
||||
0x13, 0x5e, 0xb1, 0x2d, 0x47, 0xd8, 0x6f, 0xa1, 0x10, 0x71, 0xd1, 0xf0, 0x0a, 0x71, 0x6b, 0x21,
|
||||
0x4e, 0xce, 0x2f, 0x89, 0x3b, 0x55, 0x8a, 0x19, 0x2d, 0x7b, 0xaa, 0x14, 0xb3, 0x5a, 0xee, 0x54,
|
||||
0x29, 0xe6, 0x34, 0xe5, 0x54, 0x29, 0x2a, 0x5a, 0xfe, 0x54, 0x29, 0x16, 0xb4, 0xa2, 0xfe, 0xe7,
|
||||
0x0c, 0x68, 0xfd, 0x19, 0xff, 0x9f, 0x1e, 0x01, 0x2f, 0x37, 0xcb, 0x31, 0xc7, 0x53, 0xfe, 0xde,
|
||||
0x9c, 0xb0, 0x29, 0xa7, 0x98, 0xee, 0xbc, 0xa1, 0xda, 0x96, 0xd3, 0x9e, 0xf2, 0xf7, 0x47, 0x42,
|
||||
0x16, 0x5d, 0x81, 0x09, 0x54, 0x29, 0x44, 0xd1, 0xdb, 0x18, 0xf5, 0x1f, 0xdc, 0xf9, 0x5d, 0x06,
|
||||
0xd4, 0x9f, 0xcd, 0x5c, 0xce, 0x56, 0x0f, 0x7d, 0x2c, 0xbc, 0xf9, 0xa4, 0xcd, 0xe2, 0x1e, 0x30,
|
||||
0x9e, 0x4f, 0xd9, 0x7b, 0x43, 0x3b, 0xb7, 0x64, 0x68, 0x3f, 0x78, 0x61, 0x29, 0x0f, 0x5e, 0x58,
|
||||
0xfa, 0x0f, 0x19, 0x91, 0xf5, 0xf0, 0x98, 0x61, 0xc8, 0x77, 0x41, 0x8d, 0xae, 0x21, 0x33, 0xa0,
|
||||
0xd1, 0x81, 0x21, 0x90, 0xf7, 0xd0, 0x90, 0x22, 0x53, 0xc1, 0x06, 0xc3, 0x1d, 0x83, 0x9b, 0x18,
|
||||
0x19, 0x32, 0x15, 0xa1, 0x1b, 0x48, 0x55, 0xb8, 0xe0, 0x63, 0x80, 0x44, 0x2c, 0xf3, 0xe8, 0x67,
|
||||
0x69, 0x9c, 0x08, 0xa4, 0x0c, 0xa1, 0xa2, 0xe5, 0xf5, 0xbf, 0xc8, 0x2a, 0xf8, 0x6f, 0x8f, 0xf4,
|
||||
0x29, 0x54, 0xe7, 0x84, 0x05, 0x31, 0xf2, 0x06, 0x55, 0xbd, 0x88, 0xb1, 0x08, 0xd4, 0xff, 0x87,
|
||||
0x73, 0x44, 0x72, 0x87, 0xf4, 0xb1, 0x6b, 0x42, 0x33, 0x14, 0x8a, 0xd0, 0x24, 0x72, 0x0c, 0x11,
|
||||
0x57, 0x7a, 0x67, 0x33, 0x87, 0x9b, 0x48, 0xd8, 0xe4, 0xad, 0x5a, 0xc3, 0x78, 0x4a, 0xf9, 0x91,
|
||||
0xc8, 0xed, 0xc3, 0x0e, 0xea, 0x35, 0xa8, 0x8c, 0xdc, 0xef, 0x98, 0x13, 0x37, 0xdb, 0x57, 0x50,
|
||||
0x8d, 0x04, 0xa1, 0x8b, 0xfb, 0xb0, 0xc6, 0x51, 0x12, 0x76, 0xf7, 0x7c, 0x8c, 0x9f, 0x05, 0x94,
|
||||
0x23, 0xd8, 0x08, 0x11, 0xfa, 0x1f, 0xb2, 0x50, 0x8a, 0xa5, 0xa2, 0x48, 0x2e, 0x69, 0xc0, 0x4c,
|
||||
0x9b, 0x8e, 0xa9, 0xef, 0xba, 0x4e, 0xd8, 0xe3, 0xaa, 0x10, 0x9e, 0x87, 0x32, 0x31, 0xc2, 0x22,
|
||||
0x3f, 0x6e, 0x68, 0x70, 0x83, 0xd1, 0x51, 0x8d, 0x72, 0x28, 0x3b, 0xa1, 0xc1, 0x0d, 0x79, 0x01,
|
||||
0x5a, 0x04, 0xf1, 0x7c, 0x66, 0xd9, 0xe2, 0xe6, 0x93, 0xf7, 0x73, 0x2d, 0x94, 0x0f, 0x42, 0xb1,
|
||||
0x18, 0xf0, 0xb2, 0xc9, 0x4c, 0x8f, 0x5a, 0x13, 0xd3, 0x16, 0x51, 0x94, 0x9c, 0xb3, 0x2a, 0xe5,
|
||||
0x03, 0x6a, 0x4d, 0xce, 0x03, 0xca, 0xc9, 0x17, 0xf0, 0x24, 0x41, 0x4c, 0x13, 0x70, 0xd9, 0xc5,
|
||||
0xc4, 0x8f, 0x99, 0x69, 0xbc, 0xe4, 0x29, 0xa8, 0xe2, 0xc6, 0x30, 0xc7, 0x3e, 0xa3, 0x9c, 0x4d,
|
||||
0xc2, 0x3e, 0x2e, 0x0b, 0x59, 0x5b, 0x8a, 0x48, 0x1d, 0x0a, 0xec, 0xd6, 0xb3, 0x7c, 0x36, 0xc1,
|
||||
0x1b, 0xa3, 0x68, 0x44, 0x9f, 0x62, 0x71, 0xc0, 0x5d, 0x9f, 0x5e, 0x33, 0xd3, 0xa1, 0x36, 0xc3,
|
||||
0xee, 0x2e, 0x19, 0xe5, 0x50, 0xd6, 0xa3, 0x36, 0xd3, 0x3f, 0x82, 0xed, 0x6f, 0x18, 0x3f, 0xb3,
|
||||
0xde, 0xcd, 0xac, 0x89, 0xc5, 0xef, 0x06, 0xd4, 0xa7, 0xf3, 0x29, 0xf8, 0xa7, 0x1c, 0x6c, 0xa4,
|
||||
0x55, 0x8c, 0x33, 0x5f, 0xdc, 0x40, 0x79, 0x7f, 0x36, 0x65, 0x51, 0x76, 0xe6, 0x37, 0x66, 0x0c,
|
||||
0x36, 0x66, 0x53, 0x66, 0x48, 0x10, 0xf9, 0x1a, 0x76, 0xe6, 0x25, 0xe6, 0x8b, 0x3b, 0x30, 0xa0,
|
||||
0xdc, 0xf4, 0x98, 0x6f, 0xbe, 0x17, 0x37, 0x3d, 0x46, 0x1f, 0xbb, 0x52, 0x56, 0x9b, 0x41, 0xb9,
|
||||
0xa8, 0xb8, 0x01, 0xf3, 0xdf, 0x08, 0x35, 0xf9, 0x0c, 0xb4, 0x24, 0x19, 0x34, 0x3d, 0xcf, 0xc6,
|
||||
0x4c, 0x28, 0xf1, 0x34, 0x13, 0xf1, 0xf2, 0x6c, 0xf2, 0x12, 0x04, 0xc7, 0x37, 0x53, 0x11, 0xf6,
|
||||
0xec, 0xb0, 0xe9, 0x85, 0x8d, 0x39, 0xf1, 0x17, 0xf0, 0x57, 0xd0, 0x58, 0xfe, 0x60, 0xc0, 0x55,
|
||||
0x79, 0x5c, 0xb5, 0xb9, 0xe4, 0xd1, 0x20, 0xd6, 0xa6, 0x5f, 0x05, 0x22, 0x83, 0x6b, 0x88, 0x9f,
|
||||
0xbf, 0x0a, 0x44, 0xcf, 0xbc, 0x80, 0xf5, 0x14, 0x49, 0x45, 0x60, 0x01, 0x81, 0xd5, 0x04, 0x51,
|
||||
0x8d, 0xdb, 0x6b, 0x91, 0xc2, 0x17, 0x97, 0x53, 0xf8, 0x03, 0xd8, 0x88, 0x88, 0xcb, 0x25, 0x1d,
|
||||
0x7f, 0xe7, 0x5e, 0x5d, 0x99, 0x01, 0x1b, 0xe3, 0x50, 0x56, 0x8c, 0xf5, 0x50, 0xf5, 0x5a, 0x6a,
|
||||
0x86, 0x6c, 0xac, 0xff, 0x51, 0x30, 0xee, 0x64, 0x62, 0xb0, 0x41, 0xe5, 0x3b, 0xc3, 0x0c, 0x6f,
|
||||
0x41, 0xc5, 0x28, 0x85, 0x92, 0xee, 0x84, 0x1c, 0x84, 0x54, 0x2b, 0x8b, 0x7c, 0xa8, 0xb1, 0x3c,
|
||||
0xbb, 0x09, 0xce, 0xf5, 0x12, 0x88, 0xe5, 0x8c, 0x5d, 0x5b, 0xc4, 0x8f, 0xdf, 0xf8, 0x2c, 0xb8,
|
||||
0x71, 0xa7, 0x13, 0xcc, 0x51, 0xc5, 0x58, 0x8f, 0x34, 0xa3, 0x48, 0x21, 0xe0, 0xf1, 0xd3, 0x66,
|
||||
0x0e, 0x57, 0x24, 0x3c, 0xd2, 0xc4, 0x70, 0xfd, 0x2d, 0x6c, 0x0f, 0x57, 0x55, 0x28, 0xf9, 0x0a,
|
||||
0xc0, 0x8b, 0xeb, 0x12, 0x3d, 0x29, 0x1f, 0xee, 0xdc, 0x3f, 0xf0, 0xbc, 0x76, 0x8d, 0x04, 0x5e,
|
||||
0xdf, 0x81, 0xc6, 0x32, 0xd3, 0x72, 0x08, 0xe9, 0x4f, 0x60, 0x63, 0x38, 0xbb, 0xbe, 0x66, 0x0b,
|
||||
0x6c, 0xe4, 0x14, 0x1e, 0xa7, 0xc5, 0xe1, 0xcc, 0x3a, 0x84, 0x62, 0xf4, 0xbe, 0x0b, 0xfb, 0x62,
|
||||
0x6b, 0x7e, 0x90, 0xd4, 0x13, 0xd8, 0x28, 0x84, 0x8f, 0xbd, 0xfd, 0xe7, 0x50, 0x8c, 0xf8, 0x2b,
|
||||
0x51, 0xa1, 0x78, 0xd6, 0xef, 0x0f, 0xcc, 0xfe, 0xc5, 0x48, 0x7b, 0x44, 0xca, 0x50, 0xc0, 0xaf,
|
||||
0x6e, 0x4f, 0xcb, 0xec, 0x07, 0x50, 0x8a, 0xe9, 0x2b, 0xa9, 0x40, 0xa9, 0xdb, 0xeb, 0x8e, 0xba,
|
||||
0xad, 0x51, 0xe7, 0x48, 0x7b, 0x44, 0x9e, 0xc0, 0xfa, 0xc0, 0xe8, 0x74, 0xcf, 0x5b, 0xdf, 0x74,
|
||||
0x4c, 0xa3, 0xf3, 0xa6, 0xd3, 0x3a, 0xeb, 0x1c, 0x69, 0x19, 0x42, 0xa0, 0x7a, 0x32, 0x3a, 0x6b,
|
||||
0x9b, 0x83, 0x8b, 0xd7, 0x67, 0xdd, 0xe1, 0x49, 0xe7, 0x48, 0xcb, 0x0a, 0x9b, 0xc3, 0x8b, 0x76,
|
||||
0xbb, 0x33, 0x1c, 0x6a, 0x39, 0x02, 0xb0, 0x76, 0xdc, 0xea, 0x0a, 0xb0, 0x42, 0x36, 0xa0, 0xd6,
|
||||
0xed, 0xbd, 0xe9, 0x77, 0xdb, 0x1d, 0x73, 0xd8, 0x19, 0x8d, 0x84, 0x30, 0xbf, 0xff, 0xaf, 0x0c,
|
||||
0x54, 0x52, 0x0c, 0x98, 0x6c, 0xc1, 0x86, 0x58, 0x72, 0x61, 0x88, 0x9d, 0x5a, 0xc3, 0x7e, 0xcf,
|
||||
0xec, 0xf5, 0x7b, 0x1d, 0xed, 0x11, 0xf9, 0x08, 0xb6, 0x16, 0x14, 0xfd, 0xe3, 0xe3, 0xf6, 0x49,
|
||||
0x4b, 0x1c, 0x9e, 0x34, 0x60, 0x73, 0x41, 0x39, 0xea, 0x9e, 0x77, 0x84, 0x97, 0x59, 0xb2, 0x0b,
|
||||
0x3b, 0x0b, 0xba, 0xe1, 0xb7, 0x9d, 0xce, 0x20, 0x46, 0xe4, 0xc8, 0x73, 0x78, 0xba, 0x80, 0xe8,
|
||||
0xf6, 0x86, 0x17, 0xc7, 0xc7, 0xdd, 0x76, 0xb7, 0xd3, 0x1b, 0x99, 0x6f, 0x5a, 0x67, 0x17, 0x1d,
|
||||
0x4d, 0x21, 0x3b, 0x50, 0x5f, 0xdc, 0xa4, 0x73, 0x3e, 0xe8, 0x1b, 0x2d, 0xe3, 0xad, 0x96, 0x27,
|
||||
0xcf, 0xe0, 0x93, 0x7b, 0x46, 0xda, 0x7d, 0xc3, 0xe8, 0xb4, 0x47, 0x66, 0xeb, 0xbc, 0x7f, 0xd1,
|
||||
0x1b, 0x69, 0x6b, 0xfb, 0x4d, 0xc1, 0x32, 0x17, 0x0a, 0x5c, 0x84, 0xec, 0xa2, 0xf7, 0xd3, 0x5e,
|
||||
0xff, 0xdb, 0x9e, 0xf6, 0x48, 0x44, 0x7e, 0x74, 0x62, 0x74, 0x86, 0x27, 0xfd, 0xb3, 0x23, 0x2d,
|
||||
0x73, 0xf8, 0xb7, 0x92, 0x7c, 0xe1, 0xb4, 0xf1, 0x7f, 0x11, 0x62, 0x40, 0x21, 0x4c, 0x33, 0x59,
|
||||
0x95, 0xf8, 0xc6, 0x93, 0x14, 0x4b, 0x8d, 0x2b, 0x6d, 0xeb, 0x37, 0x7f, 0xfd, 0xfb, 0x6f, 0xb3,
|
||||
0xeb, 0xba, 0xda, 0x7c, 0xff, 0x45, 0x53, 0x20, 0x9a, 0xee, 0x8c, 0xbf, 0xca, 0xec, 0x93, 0x3e,
|
||||
0xac, 0xc9, 0xb7, 0x32, 0xd9, 0x4c, 0x99, 0x8c, 0x1f, 0xcf, 0xab, 0x2c, 0x6e, 0xa2, 0x45, 0x4d,
|
||||
0x2f, 0xc7, 0x16, 0x2d, 0x47, 0x18, 0xfc, 0x31, 0x14, 0xc2, 0x77, 0x5a, 0xe2, 0x90, 0xe9, 0x97,
|
||||
0x5b, 0x63, 0x19, 0x95, 0xfe, 0x51, 0x86, 0xfc, 0x1c, 0x4a, 0x31, 0x0b, 0x27, 0xdb, 0x89, 0x1e,
|
||||
0x4b, 0xf7, 0x47, 0xa3, 0xb1, 0x4c, 0x95, 0x3e, 0x16, 0xa9, 0xc6, 0xc7, 0x42, 0x86, 0x4e, 0x2e,
|
||||
0x64, 0x1f, 0x08, 0x86, 0x4e, 0xea, 0xa9, 0xed, 0x13, 0xa4, 0x7d, 0xe9, 0xc1, 0xf4, 0x06, 0x9a,
|
||||
0x7c, 0x4c, 0x48, 0xca, 0x64, 0xf3, 0x7b, 0x6b, 0xf2, 0x4b, 0xf2, 0x0b, 0x50, 0xc3, 0x04, 0x20,
|
||||
0x8f, 0x26, 0xf3, 0x60, 0x25, 0xc9, 0x7e, 0x63, 0xee, 0xcc, 0x22, 0xe3, 0x5e, 0x62, 0xdd, 0x9d,
|
||||
0xf1, 0x26, 0x47, 0x6b, 0x97, 0xb1, 0x75, 0xe4, 0x67, 0x09, 0xeb, 0x49, 0xa6, 0x9b, 0xb6, 0x9e,
|
||||
0x62, 0x72, 0xfa, 0x2e, 0x5a, 0x6f, 0x90, 0x7a, 0xca, 0xfa, 0x3b, 0x81, 0x69, 0x7e, 0x4f, 0x6d,
|
||||
0x2e, 0x3c, 0xa8, 0x8a, 0xeb, 0x19, 0x53, 0xfe, 0xa0, 0x0f, 0xf3, 0xa8, 0x2d, 0xbc, 0x5b, 0xf4,
|
||||
0x6d, 0xdc, 0x64, 0x83, 0xac, 0x27, 0x4a, 0x21, 0xf6, 0x60, 0x6e, 0xfd, 0x41, 0x1f, 0x92, 0xd6,
|
||||
0xd3, 0x2e, 0x7c, 0x82, 0xd6, 0xb7, 0xc9, 0x56, 0xd2, 0x7a, 0xd2, 0x83, 0xb7, 0x50, 0x11, 0x7b,
|
||||
0x44, 0x04, 0x2d, 0x48, 0x54, 0x72, 0x8a, 0x05, 0x36, 0xb6, 0xee, 0xc9, 0xd3, 0xdd, 0x41, 0x6a,
|
||||
0xb8, 0x45, 0x40, 0x79, 0x53, 0x32, 0x3f, 0xc2, 0x81, 0xdc, 0xe7, 0x2e, 0x44, 0x8f, 0xed, 0xac,
|
||||
0x24, 0x36, 0x8d, 0x07, 0xaf, 0x08, 0x7d, 0x07, 0x37, 0xdc, 0x24, 0x8f, 0x71, 0xc3, 0x08, 0xd0,
|
||||
0xf4, 0xa4, 0xfd, 0x5f, 0x01, 0x19, 0x3e, 0xb4, 0xeb, 0xca, 0xcb, 0xaa, 0xf1, 0xec, 0x41, 0x4c,
|
||||
0x3a, 0xa0, 0xfa, 0xd2, 0xcd, 0x45, 0x0b, 0x33, 0x50, 0x93, 0xf7, 0x0f, 0x99, 0xfb, 0xb2, 0xe4,
|
||||
0xb6, 0x6a, 0x7c, 0xbc, 0x42, 0x1b, 0xee, 0x56, 0xc7, 0xdd, 0x08, 0xd1, 0xc4, 0x6e, 0x74, 0xc6,
|
||||
0xdd, 0x66, 0x20, 0x61, 0x97, 0x6b, 0xf8, 0x07, 0xee, 0x97, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff,
|
||||
0xff, 0xa6, 0xdb, 0xca, 0xf7, 0x15, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
|
|
|||
|
|
@ -710,6 +710,58 @@ message LiquidityParameters{
|
|||
A set of liquidity rules that describe the desired liquidity balance.
|
||||
*/
|
||||
repeated LiquidityRule rules = 1;
|
||||
|
||||
/*
|
||||
The limit we place on our estimated sweep cost for a swap in sat/vByte. If
|
||||
the estimated fee for our sweep transaction within the specified
|
||||
confirmation target is above this value, we will not suggest any swaps.
|
||||
*/
|
||||
uint64 sweep_fee_rate_sat_per_vbyte = 2;
|
||||
|
||||
/*
|
||||
The maximum fee paid to the server for facilitating the swap, expressed
|
||||
as parts per million of the swap volume.
|
||||
*/
|
||||
uint64 max_swap_fee_ppm = 3;
|
||||
|
||||
/*
|
||||
The maximum fee paid to route the swap invoice off chain, expressed as
|
||||
parts per million of the volume being routed.
|
||||
*/
|
||||
uint64 max_routing_fee_ppm = 4;
|
||||
|
||||
/*
|
||||
The maximum fee paid to route the prepay invoice off chain, expressed as
|
||||
parts per million of the volume being routed.
|
||||
*/
|
||||
uint64 max_prepay_routing_fee_ppm = 5;
|
||||
|
||||
/*
|
||||
The maximum no-show penalty in satoshis paid for a swap.
|
||||
*/
|
||||
uint64 max_prepay_sat = 6;
|
||||
|
||||
/*
|
||||
The maximum miner fee we will pay to sweep the swap on chain. Note that we
|
||||
will not suggest a swap if the estimate is above the sweep limit set by
|
||||
these parameters, and we use the current fee estimate to sweep on chain so
|
||||
this value is only a cap placed on the amount we spend on fees in the case
|
||||
where the swap needs to be claimed on chain, but fees have suddenly spiked.
|
||||
*/
|
||||
uint64 max_miner_fee_sat = 7;
|
||||
|
||||
/*
|
||||
The number of blocks from the on-chain HTLC's confirmation height that it
|
||||
should be swept within.
|
||||
*/
|
||||
int32 sweep_conf_target = 8;
|
||||
|
||||
/*
|
||||
The amount of time we require pass since a channel was part of a failed
|
||||
swap due to off chain payment failure until it will be considered for swap
|
||||
suggestions again, expressed in seconds.
|
||||
*/
|
||||
uint64 failure_backoff_sec = 9;
|
||||
}
|
||||
|
||||
enum LiquidityRuleType{
|
||||
|
|
|
|||
|
|
@ -453,6 +453,46 @@
|
|||
"$ref": "#/definitions/looprpcLiquidityRule"
|
||||
},
|
||||
"description": "A set of liquidity rules that describe the desired liquidity balance."
|
||||
},
|
||||
"sweep_fee_rate_sat_per_vbyte": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The limit we place on our estimated sweep cost for a swap in sat/vByte. If\nthe estimated fee for our sweep transaction within the specified\nconfirmation target is above this value, we will not suggest any swaps."
|
||||
},
|
||||
"max_swap_fee_ppm": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The maximum fee paid to the server for facilitating the swap, expressed\nas parts per million of the swap volume."
|
||||
},
|
||||
"max_routing_fee_ppm": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The maximum fee paid to route the swap invoice off chain, expressed as\nparts per million of the volume being routed."
|
||||
},
|
||||
"max_prepay_routing_fee_ppm": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The maximum fee paid to route the prepay invoice off chain, expressed as\nparts per million of the volume being routed."
|
||||
},
|
||||
"max_prepay_sat": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The maximum no-show penalty in satoshis paid for a swap."
|
||||
},
|
||||
"max_miner_fee_sat": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The maximum miner fee we will pay to sweep the swap on chain. Note that we\nwill not suggest a swap if the estimate is above the sweep limit set by\nthese parameters, and we use the current fee estimate to sweep on chain so\nthis value is only a cap placed on the amount we spend on fees in the case\nwhere the swap needs to be claimed on chain, but fees have suddenly spiked."
|
||||
},
|
||||
"sweep_conf_target": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"description": "The number of blocks from the on-chain HTLC's confirmation height that it\nshould be swept within."
|
||||
},
|
||||
"failure_backoff_sec": {
|
||||
"type": "string",
|
||||
"format": "uint64",
|
||||
"description": "The amount of time we require pass since a channel was part of a failed\nswap due to off chain payment failure until it will be considered for swap\nsuggestions again, expressed in seconds."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,6 +18,19 @@ This file tracks release notes for the loop client.
|
|||
This has to potential to greatly reduce chain fee costs. Note that it is not yet possible
|
||||
to select specific peers to loop in through.
|
||||
|
||||
##### Updated Swap Suggestions
|
||||
* The swap suggestions endpoint has been updated to be fee-aware. Swaps that
|
||||
exceed the fee limits set by the liquidity manager will no longer be
|
||||
suggested (see `getParams` for the current limits, and use `setParams` to
|
||||
update these values).
|
||||
* Swap suggestions are now aware of ongoing and previously failed swaps. They
|
||||
will not suggest swaps for channels that are currently being utilized for
|
||||
swaps, and will not suggest any swaps if a swap that is not limited to a
|
||||
specific peer or channel is ongoing. If a channel was part of a failed swap
|
||||
within the last 24H, it will be excluded from our swap suggestions (this
|
||||
value is configurable).
|
||||
* The `debug` logging level is recommended if using this feature.
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
* Macaroon authentication has been enabled for the `loopd` gRPC and REST
|
||||
|
|
@ -29,4 +42,8 @@ This file tracks release notes for the loop client.
|
|||
testnet you need to specify the `--network=testnet` flag.
|
||||
[More information about TLS and macaroons.](README.md#authentication-and-transport-security)
|
||||
|
||||
* The `setparm` loopcli endpoint is renamed to `setrule` because this endpoint
|
||||
is only used for setting liqudity rules (parameters can be set using the new
|
||||
`setparams` endpoint).
|
||||
|
||||
#### Bug Fixes
|
||||
Loading…
Add table
Add a link
Reference in a new issue