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
- Variables
- func ParametersToRpc(cfg Parameters) (*clientrpc.LiquidityParameters, error)
- func UseLogger(logger btclog.Logger)
- type Config
- type FeeCategoryLimit
- type FeeLimit
- type FeePortion
- type Manager
- func (m *Manager) ForceAutoLoop(ctx context.Context) error
- func (m *Manager) GetParameters() Parameters
- func (m *Manager) Run(ctx context.Context) error
- func (m *Manager) SetParameters(ctx context.Context, req *clientrpc.LiquidityParameters) error
- func (m *Manager) SuggestSwaps(ctx context.Context) (*Suggestions, error)
- type Parameters
- type Reason
- type Restrictions
- type Suggestions
- type SwapRule
- type ThresholdRule
Constants ¶
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 )
const InfiniteDuration = (24 * 31 * 12 * 100) * time.Hour
const Subsystem = "LQDY"
Subsystem defines the sub system name of this package.
Variables ¶
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)) )
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.
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 ¶
NewManager creates a liquidity manager which has no rules set.
func (*Manager) ForceAutoLoop ¶
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 ¶
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 ¶
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 )
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 // DisqualifiedPeers 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.