liquidity: add calculations for threshold rule

pull/278/head
carla 4 years ago
parent 7a3dcc3a89
commit cd8a7704af
No known key found for this signature in database
GPG Key ID: 4CA7FE54A6213C91

@ -0,0 +1,18 @@
package liquidity
import (
"github.com/btcsuite/btcutil"
)
// balances summarizes the state of the balances of a channel. Channel reserve,
// fees and pending htlc balances are not included in these balances.
type balances struct {
// capacity is the total capacity of the channel.
capacity btcutil.Amount
// incoming is the remote balance of the channel.
incoming btcutil.Amount
// outgoing is the local balance of the channel.
outgoing btcutil.Amount
}

@ -3,6 +3,8 @@ package liquidity
import (
"errors"
"fmt"
"github.com/btcsuite/btcutil"
)
var (
@ -59,3 +61,60 @@ func (r *ThresholdRule) validate() error {
return nil
}
// 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,
)
minimumOutgoing := btcutil.Amount(
uint64(balances.capacity) *
uint64(outgoingThresholdPercent) / 100,
)
switch {
// If we have sufficient incoming capacity, we do not need to loop out.
case balances.incoming >= minimumIncoming:
return 0
// If we are already below the threshold set for outgoing capacity, we
// cannot take any further action.
case balances.outgoing <= minimumOutgoing:
return 0
}
// Express our minimum outgoing amount as a maximum incoming 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
// 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 amount of incoming balance we need to shift to reach
// this desired midpoint.
required := midpoint - balances.incoming
// 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
// threshold.
available := balances.outgoing - minimumOutgoing
// 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
// pending htlcs.
if available < required {
return 0
}
return required
}

@ -3,6 +3,7 @@ package liquidity
import (
"testing"
"github.com/btcsuite/btcutil"
"github.com/stretchr/testify/require"
)
@ -91,3 +92,85 @@ 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) {
tests := []struct {
name string
minIncoming int
minOutgoing int
balances *balances
amt btcutil.Amount
}{
{
name: "insufficient surplus",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 20,
},
minOutgoing: 40,
minIncoming: 40,
amt: 0,
},
{
name: "loop out",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 80,
},
minOutgoing: 20,
minIncoming: 60,
amt: 50,
},
{
name: "pending htlcs",
balances: &balances{
capacity: 100,
incoming: 20,
outgoing: 30,
},
minOutgoing: 20,
minIncoming: 60,
amt: 0,
},
{
name: "loop in",
balances: &balances{
capacity: 100,
incoming: 50,
outgoing: 50,
},
minOutgoing: 60,
minIncoming: 30,
amt: 0,
},
{
name: "liquidity ok",
balances: &balances{
capacity: 100,
incoming: 50,
outgoing: 50,
},
minOutgoing: 40,
minIncoming: 40,
amt: 0,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
amt := loopOutSwapAmount(
test.balances, test.minIncoming,
test.minOutgoing,
)
require.Equal(t, test.amt, amt)
})
}
}

Loading…
Cancel
Save