lnwallet: add tests for new conf scaling helper funcs
This commit is contained in:
1 parent
09c813a61c
commit
4f43717d4f
1 file changed
+340
@@ -0,0 +1,340 @@
|
||||
package lnwallet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/stretchr/testify/require"
|
||||
"pgregory.net/rapid"
|
||||
)
|
||||
|
||||
// TestScaleNumConfsProperties tests various properties that ScaleNumConfs
|
||||
// should satisfy using property-based testing.
|
||||
func TestScaleNumConfsProperties(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// The result should always be bounded between the minimum and maximum
|
||||
// number of confirmations regardless of input values.
|
||||
t.Run("bounded_result", func(t *testing.T) {
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
// Generate random channel amount and push amount.
|
||||
chanAmt := rapid.Uint64Range(
|
||||
0, maxChannelSize*10,
|
||||
).Draw(t, "chanAmt")
|
||||
pushAmtSats := rapid.Uint64Range(
|
||||
0, chanAmt,
|
||||
).Draw(t, "pushAmtSats")
|
||||
pushAmt := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmtSats),
|
||||
)
|
||||
|
||||
result := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt,
|
||||
)
|
||||
|
||||
// Check bounds
|
||||
require.GreaterOrEqual(
|
||||
t, result, uint16(minRequiredConfs),
|
||||
"result should be >= minRequiredConfs",
|
||||
)
|
||||
require.LessOrEqual(
|
||||
t, result, uint16(maxRequiredConfs),
|
||||
"result should be <= maxRequiredConfs",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Larger channel amounts and push amounts should require equal or more
|
||||
// confirmations, ensuring the function is monotonically increasing.
|
||||
t.Run("monotonicity", func(t *testing.T) {
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
// Generate two channel amounts where amt1 <= amt2.
|
||||
amt1 := rapid.Uint64Range(
|
||||
0, maxChannelSize,
|
||||
).Draw(t, "amt1")
|
||||
amt2 := rapid.Uint64Range(
|
||||
amt1, maxChannelSize,
|
||||
).Draw(t, "amt2")
|
||||
|
||||
// Generate push amounts proportional to channel size.
|
||||
pushAmt1Sats := rapid.Uint64Range(
|
||||
0, amt1,
|
||||
).Draw(t, "pushAmt1")
|
||||
pushAmt2Sats := rapid.Uint64Range(
|
||||
pushAmt1Sats, amt2,
|
||||
).Draw(t, "pushAmt2")
|
||||
|
||||
pushAmt1 := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmt1Sats),
|
||||
)
|
||||
pushAmt2 := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmt2Sats),
|
||||
)
|
||||
|
||||
confs1 := ScaleNumConfs(btcutil.Amount(amt1), pushAmt1)
|
||||
confs2 := ScaleNumConfs(btcutil.Amount(amt2), pushAmt2)
|
||||
|
||||
// Larger or equal stake should require equal or more
|
||||
// confirmations.
|
||||
require.GreaterOrEqual(
|
||||
t, confs2, confs1,
|
||||
"larger amount should require equal or "+
|
||||
"more confirmations",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Wumbo channels (those exceeding the max standard channel size) should
|
||||
// always require the maximum number of confirmations for safety.
|
||||
t.Run("wumbo_max_confs", func(t *testing.T) {
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
// Generate wumbo channel amount (above maxChannelSize).
|
||||
wumboAmt := rapid.Uint64Range(
|
||||
maxChannelSize+1, maxChannelSize*100,
|
||||
).Draw(t, "wumboAmt")
|
||||
pushAmtSats := rapid.Uint64Range(
|
||||
0, wumboAmt,
|
||||
).Draw(t, "pushAmtSats")
|
||||
pushAmt := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmtSats),
|
||||
)
|
||||
|
||||
result := ScaleNumConfs(
|
||||
btcutil.Amount(wumboAmt), pushAmt,
|
||||
)
|
||||
|
||||
require.Equal(
|
||||
t, uint16(maxRequiredConfs), result,
|
||||
"wumbo channels should always get "+
|
||||
"max confirmations",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Zero channel amounts should always result in the minimum number of
|
||||
// confirmations since there's no value at risk.
|
||||
t.Run("zero_gets_min", func(t *testing.T) {
|
||||
result := ScaleNumConfs(0, 0)
|
||||
require.Equal(
|
||||
t, uint16(minRequiredConfs), result,
|
||||
"zero amount should get minimum confirmations",
|
||||
)
|
||||
})
|
||||
|
||||
// The function should be deterministic, always returning the same
|
||||
// output for the same input values.
|
||||
t.Run("determinism", func(t *testing.T) {
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
chanAmt := rapid.Uint64Range(
|
||||
0, maxChannelSize*2,
|
||||
).Draw(t, "chanAmt")
|
||||
pushAmtSats := rapid.Uint64Range(
|
||||
0, chanAmt,
|
||||
).Draw(t, "pushAmtSats")
|
||||
pushAmt := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmtSats),
|
||||
)
|
||||
|
||||
// Call multiple times with same inputs.
|
||||
result1 := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt,
|
||||
)
|
||||
result2 := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt,
|
||||
)
|
||||
result3 := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt,
|
||||
)
|
||||
|
||||
require.Equal(
|
||||
t, result1, result2,
|
||||
"function should be deterministic",
|
||||
)
|
||||
require.Equal(
|
||||
t, result2, result3,
|
||||
"function should be deterministic",
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
// Adding a push amount to a channel should require equal or more
|
||||
// confirmations compared to the same channel without a push amount.
|
||||
t.Run("push_amount_effect", func(t *testing.T) {
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
// Fix channel amount, vary push amount
|
||||
chanAmt := rapid.Uint64Range(
|
||||
1, maxChannelSize,
|
||||
).Draw(t, "chanAmt")
|
||||
pushAmt1Sats := rapid.Uint64Range(
|
||||
0, chanAmt/2,
|
||||
).Draw(t, "pushAmt1")
|
||||
pushAmt2Sats := rapid.Uint64Range(
|
||||
pushAmt1Sats, chanAmt,
|
||||
).Draw(t, "pushAmt2")
|
||||
|
||||
pushAmt1 := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmt1Sats),
|
||||
)
|
||||
pushAmt2 := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmt2Sats),
|
||||
)
|
||||
|
||||
confs1 := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt1,
|
||||
)
|
||||
confs2 := ScaleNumConfs(
|
||||
btcutil.Amount(chanAmt), pushAmt2,
|
||||
)
|
||||
|
||||
// More push amount should require equal or more
|
||||
// confirmations.
|
||||
require.GreaterOrEqual(
|
||||
t, confs2, confs1,
|
||||
"larger push amount should "+
|
||||
"require equal or more confirmations",
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// TestScaleNumConfsKnownValues tests ScaleNumConfs with specific known values
|
||||
// to ensure the scaling formula works as expected.
|
||||
func TestScaleNumConfsKnownValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
chanAmt btcutil.Amount
|
||||
pushAmt lnwire.MilliSatoshi
|
||||
expected uint16
|
||||
}{
|
||||
{
|
||||
name: "zero amounts",
|
||||
chanAmt: 0,
|
||||
pushAmt: 0,
|
||||
expected: minRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "tiny channel",
|
||||
chanAmt: 1000,
|
||||
pushAmt: 0,
|
||||
expected: minRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "small channel no push",
|
||||
chanAmt: 100_000,
|
||||
pushAmt: 0,
|
||||
expected: minRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "half max channel no push",
|
||||
chanAmt: maxChannelSize / 2,
|
||||
pushAmt: 0,
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
name: "max channel no push",
|
||||
chanAmt: maxChannelSize,
|
||||
pushAmt: 0,
|
||||
expected: maxRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "wumbo channel",
|
||||
chanAmt: maxChannelSize * 2,
|
||||
pushAmt: 0,
|
||||
expected: maxRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "small channel with push",
|
||||
chanAmt: 100_000,
|
||||
pushAmt: lnwire.NewMSatFromSatoshis(50_000),
|
||||
expected: minRequiredConfs,
|
||||
},
|
||||
{
|
||||
name: "medium channel with significant push",
|
||||
chanAmt: maxChannelSize / 4,
|
||||
pushAmt: lnwire.NewMSatFromSatoshis(
|
||||
maxChannelSize / 4,
|
||||
),
|
||||
expected: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := ScaleNumConfs(tc.chanAmt, tc.pushAmt)
|
||||
|
||||
require.Equal(
|
||||
t, tc.expected, result,
|
||||
"chanAmt=%d, pushAmt=%d", tc.chanAmt,
|
||||
tc.pushAmt,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFundingConfsForAmounts verifies that FundingConfsForAmounts is a simple
|
||||
// wrapper around ScaleNumConfs.
|
||||
func TestFundingConfsForAmounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
chanAmt := rapid.Uint64Range(
|
||||
0, maxChannelSize*2,
|
||||
).Draw(t, "chanAmt")
|
||||
pushAmtSats := rapid.Uint64Range(
|
||||
0, chanAmt,
|
||||
).Draw(t, "pushAmtSats")
|
||||
pushAmt := lnwire.NewMSatFromSatoshis(
|
||||
btcutil.Amount(pushAmtSats),
|
||||
)
|
||||
|
||||
// Both functions should return the same result.
|
||||
scaleResult := ScaleNumConfs(btcutil.Amount(chanAmt), pushAmt)
|
||||
fundingResult := FundingConfsForAmounts(
|
||||
btcutil.Amount(chanAmt), pushAmt,
|
||||
)
|
||||
|
||||
require.Equal(
|
||||
t, scaleResult, fundingResult,
|
||||
"FundingConfsForAmounts should return "+
|
||||
"same result as ScaleNumConfs",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// TestCloseConfsForCapacity verifies that CloseConfsForCapacity correctly
|
||||
// wraps ScaleNumConfs with zero push amount and enforces a minimum of 3
|
||||
// confirmations for reorg safety.
|
||||
func TestCloseConfsForCapacity(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rapid.Check(t, func(t *rapid.T) {
|
||||
capacity := rapid.Uint64Range(
|
||||
0, maxChannelSize*2,
|
||||
).Draw(t, "capacity")
|
||||
|
||||
// CloseConfsForCapacity should be equivalent to ScaleNumConfs
|
||||
// with 0 push, but with a minimum of 3 confirmations enforced
|
||||
// for reorg safety.
|
||||
closeConfs := CloseConfsForCapacity(btcutil.Amount(capacity))
|
||||
scaleConfs := ScaleNumConfs(btcutil.Amount(capacity), 0)
|
||||
|
||||
// The result should be at least the scaled value, but with a
|
||||
// minimum of 3 confirmations.
|
||||
const minCloseConfs = 3
|
||||
expectedConfs := uint32(scaleConfs)
|
||||
if expectedConfs < minCloseConfs {
|
||||
expectedConfs = minCloseConfs
|
||||
}
|
||||
|
||||
require.Equal(
|
||||
t, expectedConfs, closeConfs,
|
||||
"CloseConfsForCapacity should match "+
|
||||
"ScaleNumConfs with 0 push amount, "+
|
||||
"but with minimum of 3 confs",
|
||||
)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user