contractcourt: test commitment deadline logic

This commit adds two tests to check that a) the correct deadline is used
given different HTLC sets and b) when sweeping anchors the correct
deadlines are used.
This commit is contained in:
yyforyongyu 2021-05-11 21:00:00 +08:00
parent 0dc3190fec
commit 614884dcb8
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
2 changed files with 252 additions and 3 deletions

@ -7,6 +7,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"sort"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -2095,6 +2096,254 @@ func TestRemoteCloseInitiator(t *testing.T) {
} }
} }
// TestFindCommitmentDeadline tests the logic used to determine confirmation
// deadline is implemented as expected.
func TestFindCommitmentDeadline(t *testing.T) {
// Create a testing channel arbitrator.
log := &mockArbitratorLog{
state: StateDefault,
newStates: make(chan ArbitratorState, 5),
}
chanArbCtx, err := createTestChannelArbitrator(t, log)
require.NoError(t, err, "unable to create ChannelArbitrator")
// Add a dummy payment hash to the preimage lookup.
rHash := [lntypes.PreimageSize]byte{1, 2, 3}
mockPreimageDB := newMockWitnessBeacon()
mockPreimageDB.lookupPreimage[rHash] = rHash
// Attack a mock PreimageDB and Registry to channel arbitrator.
chanArb := chanArbCtx.chanArb
chanArb.cfg.PreimageDB = mockPreimageDB
chanArb.cfg.Registry = &mockRegistry{}
htlcIndexBase := uint64(99)
heightHint := uint32(1000)
htlcExpiryBase := heightHint + uint32(10)
// Create four testing HTLCs.
htlcDust := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 1,
RefundTimeout: htlcExpiryBase + 1,
OutputIndex: -1,
}
htlcSmallExipry := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 2,
RefundTimeout: htlcExpiryBase + 2,
}
htlcPreimage := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 3,
RefundTimeout: htlcExpiryBase + 3,
RHash: rHash,
}
htlcLargeExpiry := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 4,
RefundTimeout: htlcExpiryBase + 100,
}
htlcExpired := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 5,
RefundTimeout: heightHint,
}
makeHTLCSet := func(incoming, outgoing channeldb.HTLC) htlcSet {
return htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
incoming.HtlcIndex: incoming,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
outgoing.HtlcIndex: outgoing,
},
}
}
testCases := []struct {
name string
htlcs htlcSet
err error
deadline uint32
}{
{
// When we have no HTLCs, the default value should be
// used.
name: "use default conf target",
htlcs: htlcSet{},
err: nil,
deadline: anchorSweepConfTarget,
},
{
// When we have a preimage available in the local HTLC
// set, its CLTV should be used.
name: "use htlc with preimage available",
htlcs: makeHTLCSet(htlcPreimage, htlcLargeExpiry),
err: nil,
deadline: htlcPreimage.RefundTimeout - heightHint,
},
{
// When the HTLC in the local set is not preimage
// available, we should not use its CLTV even its value
// is smaller.
name: "use htlc with no preimage available",
htlcs: makeHTLCSet(htlcSmallExipry, htlcLargeExpiry),
err: nil,
deadline: htlcLargeExpiry.RefundTimeout - heightHint,
},
{
// When we have dust HTLCs, their CLTVs should NOT be
// used even the values are smaller.
name: "ignore dust HTLCs",
htlcs: makeHTLCSet(htlcPreimage, htlcDust),
err: nil,
deadline: htlcPreimage.RefundTimeout - heightHint,
},
{
// When we've reached our deadline, use conf target of
// 1 as our deadline.
name: "use conf target 1",
htlcs: makeHTLCSet(htlcPreimage, htlcExpired),
err: nil,
deadline: 1,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
deadline, err := chanArb.findCommitmentDeadline(
heightHint, tc.htlcs,
)
require.Equal(t, tc.err, err)
require.Equal(t, tc.deadline, deadline)
})
}
}
// TestSweepAnchors checks the sweep transactions are created using the
// expected deadlines for different anchor resolutions.
func TestSweepAnchors(t *testing.T) {
// Create a testing channel arbitrator.
log := &mockArbitratorLog{
state: StateDefault,
newStates: make(chan ArbitratorState, 5),
}
chanArbCtx, err := createTestChannelArbitrator(t, log)
require.NoError(t, err, "unable to create ChannelArbitrator")
// Add a dummy payment hash to the preimage lookup.
rHash := [lntypes.PreimageSize]byte{1, 2, 3}
mockPreimageDB := newMockWitnessBeacon()
mockPreimageDB.lookupPreimage[rHash] = rHash
// Attack a mock PreimageDB and Registry to channel arbitrator.
chanArb := chanArbCtx.chanArb
chanArb.cfg.PreimageDB = mockPreimageDB
chanArb.cfg.Registry = &mockRegistry{}
// Set current block height.
heightHint := uint32(1000)
chanArbCtx.chanArb.blocks <- int32(heightHint)
htlcIndexBase := uint64(99)
htlcExpiryBase := heightHint + uint32(10)
// Create three testing HTLCs.
htlcDust := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 1,
RefundTimeout: htlcExpiryBase + 1,
OutputIndex: -1,
}
htlcWithPreimage := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 2,
RefundTimeout: htlcExpiryBase + 2,
RHash: rHash,
}
htlcSmallExipry := channeldb.HTLC{
HtlcIndex: htlcIndexBase + 3,
RefundTimeout: htlcExpiryBase + 3,
}
// Setup our local HTLC set such that we will use the HTLC's CLTV from
// the incoming HTLC set.
expectedLocalDeadline := htlcWithPreimage.RefundTimeout - heightHint
chanArb.activeHTLCs[LocalHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcWithPreimage.HtlcIndex: htlcWithPreimage,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
}
// Setup our remote HTLC set such that no valid HTLCs can be used, thus
// we default to anchorSweepConfTarget.
expectedRemoteDeadline := anchorSweepConfTarget
chanArb.activeHTLCs[RemoteHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
}
// Setup out pending remote HTLC set such that we will use the HTLC's
// CLTV from the outgoing HTLC set.
expectedPendingDeadline := htlcSmallExipry.RefundTimeout - heightHint
chanArb.activeHTLCs[RemotePendingHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
},
}
// Create AnchorResolutions.
anchors := &lnwallet.AnchorResolutions{
Local: &lnwallet.AnchorResolution{
AnchorSignDescriptor: input.SignDescriptor{
Output: &wire.TxOut{Value: 1},
},
},
Remote: &lnwallet.AnchorResolution{
AnchorSignDescriptor: input.SignDescriptor{
Output: &wire.TxOut{Value: 1},
},
},
RemotePending: &lnwallet.AnchorResolution{
AnchorSignDescriptor: input.SignDescriptor{
Output: &wire.TxOut{Value: 1},
},
},
}
// Sweep anchors and check there's no error.
err = chanArb.sweepAnchors(anchors, heightHint)
require.NoError(t, err)
// Verify deadlines are used as expected.
deadlines := chanArbCtx.sweeper.deadlines
// Since there's no guarantee of the deadline orders, we sort it here
// so they can be compared.
sort.Ints(deadlines) // [12, 13, 144]
require.EqualValues(
t, expectedLocalDeadline, deadlines[0],
"local deadline not matched",
)
require.EqualValues(
t, expectedPendingDeadline, deadlines[1],
"pending remote deadline not matched",
)
require.EqualValues(
t, expectedRemoteDeadline, deadlines[2],
"remote deadline not matched",
)
}
// TestChannelArbitratorAnchors asserts that the commitment tx anchor is swept. // TestChannelArbitratorAnchors asserts that the commitment tx anchor is swept.
func TestChannelArbitratorAnchors(t *testing.T) { func TestChannelArbitratorAnchors(t *testing.T) {
log := &mockArbitratorLog{ log := &mockArbitratorLog{

@ -109,7 +109,7 @@ type mockSweeper struct {
sweepErr error sweepErr error
createSweepTxChan chan *wire.MsgTx createSweepTxChan chan *wire.MsgTx
deadlines []uint32 deadlines []int
} }
func newMockSweeper() *mockSweeper { func newMockSweeper() *mockSweeper {
@ -118,7 +118,7 @@ func newMockSweeper() *mockSweeper {
updatedInputs: make(chan wire.OutPoint), updatedInputs: make(chan wire.OutPoint),
sweepTx: &wire.MsgTx{}, sweepTx: &wire.MsgTx{},
createSweepTxChan: make(chan *wire.MsgTx), createSweepTxChan: make(chan *wire.MsgTx),
deadlines: []uint32{}, deadlines: []int{},
} }
} }
@ -129,7 +129,7 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
// Update the deadlines used if it's set. // Update the deadlines used if it's set.
if params.Fee.ConfTarget != 0 { if params.Fee.ConfTarget != 0 {
s.deadlines = append(s.deadlines, params.Fee.ConfTarget) s.deadlines = append(s.deadlines, int(params.Fee.ConfTarget))
} }
result := make(chan sweep.Result, 1) result := make(chan sweep.Result, 1)