From 00c2d4e5f0a9d299d70e0ca12e8d22a74d1e33b6 Mon Sep 17 00:00:00 2001 From: carla Date: Tue, 30 Nov 2021 13:18:26 +0200 Subject: [PATCH] liquidity: generalize threshold rule variable names The current wording in this function is very specific to loop out. In this commit, we refactor to use `target` and `reserve` rather than inbound and outbound so that it can be used more generically. --- liquidity/threshold_rule.go | 92 ++++++++++++++++++++------------ liquidity/threshold_rule_test.go | 17 +++--- 2 files changed, 68 insertions(+), 41 deletions(-) diff --git a/liquidity/threshold_rule.go b/liquidity/threshold_rule.go index 47ef735..2db9f6c 100644 --- a/liquidity/threshold_rule.go +++ b/liquidity/threshold_rule.go @@ -65,73 +65,99 @@ func (r *ThresholdRule) validate() error { // swapAmount suggests a swap based on the liquidity thresholds configured, // returning zero if no swap is recommended. func (r *ThresholdRule) swapAmount(channel *balances, - outRestrictions *Restrictions) btcutil.Amount { + restrictions *Restrictions) btcutil.Amount { + + var ( + // For loop out swaps, we want to adjust our incoming liquidity + // so the channel's incoming balance is our target. + targetBalance = channel.incoming + + // For loop out swaps, we target a minimum amount of incoming + // liquidity, so the minimum incoming threshold is our target + // percentage. + targetPercentage = uint64(r.MinimumIncoming) + + // For loop out swaps, we may want to preserve some of our + // outgoing balance, so the channel's outgoing balance is our + // reserve. + reserveBalance = channel.outgoing + + // For loop out swaps, we may want to preserve some percentage + // of our outgoing balance, so the minimum outgoing threshold + // is our reserve percentage. + reservePercentage = uint64(r.MinimumOutgoing) + ) // Examine our total balance and required ratios to decide whether we // need to swap. - amount := loopOutSwapAmount( - channel, r.MinimumIncoming, r.MinimumOutgoing, + amount := calculateSwapAmount( + targetBalance, reserveBalance, channel.capacity, + targetPercentage, reservePercentage, ) // Limit our swap amount by the minimum/maximum thresholds set. switch { - case amount < outRestrictions.Minimum: + case amount < restrictions.Minimum: return 0 - case amount > outRestrictions.Maximum: - return outRestrictions.Maximum + case amount > restrictions.Maximum: + return restrictions.Maximum default: return amount } } -// loopOutSwapAmount determines whether we can perform a loop out swap, and -// returns the amount we need to swap to reach the desired liquidity balance -// specified by the incoming and outgoing thresholds. -func loopOutSwapAmount(balances *balances, incomingThresholdPercent, - outgoingThresholdPercent int) btcutil.Amount { - - minimumIncoming := btcutil.Amount(uint64( - balances.capacity) * - uint64(incomingThresholdPercent) / 100, +// calculateSwapAmount calculates amount for a swap based on thresholds. +// This function can be used for loop out or loop in, but the concept is the +// same - we want liquidity in one (target) direction, while preserving some +// minimum in the other (reserve) direction. +// * target: this is the side of the channel(s) where we want to acquire some +// liquidity. We aim for this liquidity to reach the threshold amount set. +// * reserve: this is the side of the channel(s) that we will move liquidity +// away from. This may not drop below a certain reserve threshold. +func calculateSwapAmount(targetAmount, reserveAmount, + capacity btcutil.Amount, targetThresholdPercentage, + reserveThresholdPercentage uint64) btcutil.Amount { + + targetGoal := btcutil.Amount( + uint64(capacity) * targetThresholdPercentage / 100, ) - minimumOutgoing := btcutil.Amount( - uint64(balances.capacity) * - uint64(outgoingThresholdPercent) / 100, + reserveMinimum := btcutil.Amount( + uint64(capacity) * reserveThresholdPercentage / 100, ) switch { - // If we have sufficient incoming capacity, we do not need to loop out. - case balances.incoming >= minimumIncoming: + // If we have sufficient target capacity, we do not need to swap. + case targetAmount >= targetGoal: return 0 - // If we are already below the threshold set for outgoing capacity, we + // If we are already below the threshold set for reserve capacity, we // cannot take any further action. - case balances.outgoing <= minimumOutgoing: + case reserveAmount <= reserveMinimum: return 0 } - // Express our minimum outgoing amount as a maximum incoming amount. + // Express our minimum reserve amount as a maximum target amount. // We will use this value to limit the amount that we swap, so that we - // do not dip below our outgoing threshold. - maximumIncoming := balances.capacity - minimumOutgoing + // do not dip below our reserve threshold. + maximumTarget := capacity - reserveMinimum - // Calculate the midpoint between our minimum and maximum incoming - // values. We will aim to swap this amount so that we do not tip our - // outgoing balance beneath the desired level. - midpoint := (minimumIncoming + maximumIncoming) / 2 + // Calculate the midpoint between our minimum and maximum target values. + // We will aim to swap this amount so that we do not tip our reserve + // balance beneath the desired level. + midpoint := (targetGoal + maximumTarget) / 2 - // Calculate the amount of incoming balance we need to shift to reach + // Calculate the amount of target balance we need to shift to reach // this desired midpoint. - required := midpoint - balances.incoming + required := midpoint - targetAmount // Since we can have pending htlcs on our channel, we check the amount - // of outbound capacity that we can shift before we fall below our + // of reserve capacity that we can shift before we fall below our // threshold. - available := balances.outgoing - minimumOutgoing + available := reserveAmount - reserveMinimum // If we do not have enough balance available to reach our midpoint, we // take no action. This is the case when we have a large portion of diff --git a/liquidity/threshold_rule_test.go b/liquidity/threshold_rule_test.go index b90c6c6..a460def 100644 --- a/liquidity/threshold_rule_test.go +++ b/liquidity/threshold_rule_test.go @@ -93,18 +93,18 @@ func TestValidateThreshold(t *testing.T) { } } -// TestLoopOutAmount tests assessing of a set of balances to determine whether -// we should perform a loop out. -func TestLoopOutAmount(t *testing.T) { +// TestCalculateAmount tests calculation of the amount we recommend for a given +// set of balances and threshold rule. +func TestCalculateAmount(t *testing.T) { tests := []struct { name string - minIncoming int - minOutgoing int + minIncoming uint64 + minOutgoing uint64 balances *balances amt btcutil.Amount }{ { - name: "insufficient surplus", + name: "insufficient outgoing", balances: &balances{ capacity: 100, incoming: 20, @@ -166,8 +166,9 @@ func TestLoopOutAmount(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() - amt := loopOutSwapAmount( - test.balances, test.minIncoming, + amt := calculateSwapAmount( + test.balances.incoming, test.balances.outgoing, + test.balances.capacity, test.minIncoming, test.minOutgoing, ) require.Equal(t, test.amt, amt)