liquidity

package
v0.28.1-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2024 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

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).

Index

Constants

View Source
const (

	// FeeBase is the base that we use to express fees.
	FeeBase = 1e6

	// DefaultAutoloopTicker is the default amount of time between automated
	// swap checks.
	DefaultAutoloopTicker = time.Minute * 20
)
View Source
const InfiniteDuration = (24 * 31 * 12 * 100) * time.Hour
View Source
const Subsystem = "LQDY"

Subsystem defines the sub system name of this package.

Variables

View Source
var (
	// 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")

	// ErrInvalidPPM is returned is the parts per million for a fee rate
	// are invalid.
	ErrInvalidPPM = errors.New("invalid ppm")

	// 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))
)
View Source
var (

	// ErrZeroChannelID is returned if we get a rule for a 0 channel ID.
	ErrZeroChannelID = fmt.Errorf("zero channel ID not allowed")

	// ErrNegativeBudget is returned if a negative swap budget is set.
	ErrNegativeBudget = errors.New("swap budget must be >= 0")

	// ErrZeroInFlight is returned is a zero in flight swaps value is set.
	ErrZeroInFlight = errors.New("max in flight swaps must be >=0")

	// ErrMinimumExceedsMaximumAmt is returned when the minimum configured
	// swap amount is more than the maximum.
	ErrMinimumExceedsMaximumAmt = errors.New("minimum swap amount " +
		"exceeds maximum")

	// ErrMaxExceedsServer is returned if the maximum swap amount set is
	// more than the server offers.
	ErrMaxExceedsServer = errors.New("maximum swap amount is more than " +
		"server maximum")

	// ErrMinLessThanServer is returned if the minimum swap amount set is
	// less than the server minimum.
	ErrMinLessThanServer = errors.New("minimum swap amount is less than " +
		"server minimum")

	// ErrNoRules is returned when no rules are set for swap suggestions.
	ErrNoRules = errors.New("no rules set for autoloop")

	// ErrExclusiveRules is returned when a set of rules that may not be
	// set together are specified.
	ErrExclusiveRules = errors.New("channel and peer rules must be " +
		"exclusive")

	// ErrAmbiguousDestAddr is returned when a destination address and
	// a extended public key account is set.
	ErrAmbiguousDestAddr = errors.New("ambiguous destination address")

	// ErrAccountAndAddrType indicates if an account is set but the
	// account address type is not or vice versa.
	ErrAccountAndAddrType = errors.New("account and address type have " +
		"to be both either set or unset")
)

Functions

func ParametersToRpc

func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters,
	error)

ParametersToRpc takes a `Parameters` and creates a `LiquidityParameters` from it.

func UseLogger

func UseLogger(logger btclog.Logger)

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.

Types

type Config

type Config struct {
	// AutoloopTicker determines how often we should check whether we want
	// to dispatch an automated swap. We use a force ticker so that we can
	// trigger autoloop in itests.
	AutoloopTicker *ticker.Force

	// Restrictions returns the restrictions that the server applies to
	// swaps.
	Restrictions func(ctx context.Context, swapType swap.Type,
		initiator string) (*Restrictions, error)

	// 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(context.Context) ([]*loopdb.LoopOut, error)

	// GetLoopOut returns a single loop out swap based on the provided swap
	// hash.
	GetLoopOut func(ctx context.Context, hash lntypes.Hash) (*loopdb.LoopOut, error)

	// ListLoopIn returns all of the loop in swaps stored on disk.
	ListLoopIn func(ctx context.Context) ([]*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)

	// LoopInQuote provides a quote for a loop in swap.
	LoopInQuote func(ctx context.Context,
		request *loop.LoopInQuoteRequest) (*loop.LoopInQuote, error)

	// LoopOut dispatches a loop out.
	LoopOut func(ctx context.Context, request *loop.OutRequest) (
		*loop.LoopOutSwapInfo, error)

	// LoopIn dispatches a loop in swap.
	LoopIn func(ctx context.Context,
		request *loop.LoopInRequest) (*loop.LoopInSwapInfo, error)

	// LoopInTerms returns the terms for a loop in swap.
	LoopInTerms func(ctx context.Context,
		initiator string) (*loop.LoopInTerms, error)

	// LoopOutTerms returns the terms for a loop out swap.
	LoopOutTerms func(ctx context.Context,
		initiator string) (*loop.LoopOutTerms, 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

	// PutLiquidityParams writes the serialized `Parameters` into db.
	//
	// NOTE: the params are encoded using `proto.Marshal` over an RPC
	// request.
	PutLiquidityParams func(ctx context.Context, params []byte) error

	// FetchLiquidityParams reads the serialized `Parameters` from db.
	//
	// NOTE: the params are decoded using `proto.Unmarshal` over a
	// serialized RPC request.
	FetchLiquidityParams func(ctx context.Context) ([]byte, error)
}

Config contains the external functionality required to run the liquidity manager.

type FeeCategoryLimit

type FeeCategoryLimit struct {
	// 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 uint64

	// 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 uint64

	// 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 uint64

	// 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

	// 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
}

FeeCategoryLimit is an implementation of the fee limit interface which sets a specific fee limit per fee category.

func NewFeeCategoryLimit

func NewFeeCategoryLimit(swapFeePPM, routingFeePPM, prepayFeePPM uint64,
	minerFee, prepay btcutil.Amount,
	sweepLimit chainfee.SatPerKWeight) *FeeCategoryLimit

NewFeeCategoryLimit created a new fee limit struct which sets individual fee limits per category.

func (*FeeCategoryLimit) String

func (f *FeeCategoryLimit) String() string

String returns the string representation of our fee category limits.

type FeeLimit

type FeeLimit interface {
	// String returns the string representation of fee limits.
	String() string
	// contains filtered or unexported methods
}

FeeLimit is an interface implemented by different strategies for limiting the fees we pay for autoloops.

type FeePortion

type FeePortion struct {
	// PartsPerMillion is the total portion of the swap amount that the
	// swap may consume.
	PartsPerMillion uint64
}

FeePortion is a fee limitation which limits fees to a set portion of the swap amount.

func NewFeePortion

func NewFeePortion(ppm uint64) *FeePortion

NewFeePortion creates a fee limit based on a flat percentage of swap amount.

func (*FeePortion) String

func (f *FeePortion) String() string

String returns a string representation of the fee limit.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager contains a set of desired liquidity rules for our channel balances.

func NewManager

func NewManager(cfg *Config) *Manager

NewManager creates a liquidity manager which has no rules set.

func (*Manager) ForceAutoLoop

func (m *Manager) ForceAutoLoop(ctx context.Context) error

ForceAutoLoop force-ticks our auto-out ticker.

func (*Manager) GetParameters

func (m *Manager) GetParameters() Parameters

GetParameters returns a copy of our current parameters.

func (*Manager) Run

func (m *Manager) Run(ctx context.Context) error

Run periodically checks whether we should automatically dispatch a loop out. We run this loop even if automated swaps are not currently enabled rather than managing starting and stopping the ticker as our parameters are updated.

func (*Manager) SetParameters

func (m *Manager) SetParameters(ctx context.Context,
	req *clientrpc.LiquidityParameters) error

SetParameters takes an RPC request and calls the internal method to set parameters for the manager.

func (*Manager) SuggestSwaps

func (m *Manager) SuggestSwaps(ctx context.Context) (
	*Suggestions, error)

SuggestSwaps returns a set of swap suggestions based on our current liquidity balance for the set of rules configured for the manager, failing if there are no rules set. It takes an autoloop boolean that indicates whether the suggestions are being used for our internal autolooper. This boolean is used to determine the information we add to our swap suggestion and whether we return any suggestions.

type Parameters

type Parameters struct {
	// Autoloop enables automatic dispatch of swaps.
	Autoloop bool

	// DestAddr is the address to be used for sweeping the on-chain HTLC
	// that is related with a loop out.
	DestAddr btcutil.Address

	// An alternative destination address source for the swap. This field
	// represents the name of the account in the backing lnd instance.
	// Refer to lnd's wallet import functions for reference.
	Account string

	// The address type of the account specified in the account field.
	AccountAddrType walletrpc.AddressType

	// AutoFeeBudget is the total amount we allow to be spent on
	// automatically dispatched swaps. Once this budget has been used, we
	// will stop dispatching swaps until the budget is refreshed.
	AutoFeeBudget btcutil.Amount

	// AutoFeeRefreshPeriod is the amount of time that must pass before the
	// auto fee budget is refreshed.
	AutoFeeRefreshPeriod time.Duration

	// AutoloopBudgetLastRefresh is the last time at which we refreshed
	// our budget.
	AutoloopBudgetLastRefresh time.Time

	// MaxAutoInFlight is the maximum number of in-flight automatically
	// dispatched swaps we allow.
	MaxAutoInFlight int

	// 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

	// 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

	// HtlcConfTarget is the confirmation target that we use for publishing
	// loop in swap htlcs on chain.
	HtlcConfTarget int32

	// FeeLimit controls the fee limit we place on swaps.
	FeeLimit FeeLimit

	// ClientRestrictions are the restrictions placed on swap size by the
	// client.
	ClientRestrictions Restrictions

	// ChannelRules maps a short channel ID to a rule that describes how we
	// would like liquidity to be managed. These rules and PeerRules are
	// exclusively set to prevent overlap between peer and channel rules.
	ChannelRules map[lnwire.ShortChannelID]*SwapRule

	// PeerRules maps a peer's pubkey to a rule that applies to all the
	// channels that we have with the peer collectively. These rules and
	// ChannelRules are exclusively set to prevent overlap between peer
	// and channel rules map to avoid ambiguity.
	PeerRules map[route.Vertex]*SwapRule

	// CustomPaymentCheckInterval is an optional custom interval to use when
	// checking an autoloop loop out payments' payment status.
	CustomPaymentCheckInterval time.Duration

	// EasyAutoloop is a boolean that indicates whether we should use the
	// easy autoloop feature.
	EasyAutoloop bool

	// EasyAutoloopTarget is the target amount of liquidity that we want to
	// maintain in our channels.
	EasyAutoloopTarget btcutil.Amount
}

Parameters is a set of parameters provided by the user which guide how we assess liquidity.

func RpcToParameters

func RpcToParameters(req *clientrpc.LiquidityParameters) (*Parameters,
	error)

rpcToParameters takes a `LiquidityParameters` and creates a `Parameters` from it.

func (Parameters) String

func (p Parameters) String() string

String returns the string representation of our parameters.

type Reason

type Reason uint8

Reason is an enum which represents the various reasons we have for not executing a swap.

const (
	// ReasonNone is the zero value reason, added so that this enum can
	// align with the numeric values used in our protobufs and avoid
	// ambiguity around default zero values.
	ReasonNone Reason = iota

	// ReasonBudgetNotStarted indicates that we do not recommend any swaps
	// because the start time for our budget has not arrived yet.
	ReasonBudgetNotStarted

	// ReasonSweepFees indicates that the estimated fees to sweep swaps
	// are too high right now.
	ReasonSweepFees

	// ReasonBudgetElapsed indicates that the autoloop budget for the
	// period has been elapsed.
	ReasonBudgetElapsed

	// ReasonInFlight indicates that the limit on in-flight automatically
	// dispatched swaps has already been reached.
	ReasonInFlight

	// ReasonSwapFee indicates that the server fee for a specific swap is
	// too high.
	ReasonSwapFee

	// ReasonMinerFee indicates that the miner fee for a specific swap is
	// to high.
	ReasonMinerFee

	// ReasonPrepay indicates that the prepay fee for a specific swap is
	// too high.
	ReasonPrepay

	// ReasonFailureBackoff indicates that a swap has recently failed for
	// this target, and the backoff period has not yet passed.
	ReasonFailureBackoff

	// ReasonLoopOut indicates that a loop out swap is currently utilizing
	// the channel, so it is not eligible.
	ReasonLoopOut

	// ReasonLoopIn indicates that a loop in swap is currently in flight
	// for the peer, so it is not eligible.
	ReasonLoopIn

	// ReasonLiquidityOk indicates that a target meets the liquidity
	// balance expressed in its rule, so no swap is needed.
	ReasonLiquidityOk

	// ReasonBudgetInsufficient indicates that we cannot perform a swap
	// because we do not have enough pending budget available. This differs
	// from budget elapsed, because we still have some budget available,
	// but we have allocated it to other swaps.
	ReasonBudgetInsufficient

	// ReasonFeePPMInsufficient indicates that the fees a swap would require
	// are greater than the portion of swap amount allocated to fees.
	ReasonFeePPMInsufficient

	// ReasonLoopInUnreachable indicates that the server does not have a
	// path to the client, so cannot perform a loop in swap at this time.
	ReasonLoopInUnreachable
)

func (Reason) String

func (r Reason) String() string

String returns a string representation of a reason.

type Restrictions

type Restrictions struct {
	// Minimum is the minimum amount we can swap, inclusive.
	Minimum btcutil.Amount

	// Maximum is the maximum amount we can swap, inclusive.
	Maximum btcutil.Amount
}

Restrictions indicates the restrictions placed on a swap.

func NewRestrictions

func NewRestrictions(minimum, maximum btcutil.Amount) *Restrictions

NewRestrictions creates a set of restrictions.

func (*Restrictions) String

func (r *Restrictions) String() string

String returns a string representation of a set of restrictions.

type Suggestions

type Suggestions struct {
	// OutSwaps is the set of loop out swaps that we suggest executing.
	OutSwaps []loop.OutRequest

	// InSwaps is the set of loop in swaps that we suggest executing.
	InSwaps []loop.LoopInRequest

	// DisqualifiedChans maps the set of channels that we do not recommend
	// swaps on to the reason that we did not recommend a swap.
	DisqualifiedChans map[lnwire.ShortChannelID]Reason

	// Disqualified peers maps the set of peers that we do not recommend
	// swaps for to the reason that they were excluded.
	DisqualifiedPeers map[route.Vertex]Reason
}

Suggestions provides a set of suggested swaps, and the set of channels that were excluded from consideration.

type SwapRule

type SwapRule struct {
	*ThresholdRule
	swap.Type
}

SwapRule is a liquidity rule with a specific swap type.

type ThresholdRule

type ThresholdRule struct {
	// MinimumIncoming is the percentage of incoming liquidity that we do
	// not want to drop below.
	MinimumIncoming int

	// MinimumOutgoing is the percentage of outgoing liquidity that we do
	// not want to drop below.
	MinimumOutgoing int
}

ThresholdRule is a liquidity rule that implements minimum incoming and outgoing liquidity threshold.

func NewThresholdRule

func NewThresholdRule(minIncoming, minOutgoing int) *ThresholdRule

NewThresholdRule returns a new threshold rule.

func (*ThresholdRule) String

func (r *ThresholdRule) String() string

String returns a string representation of a rule.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL