From 614884dcb880d1da51e4945715336c04af27de2c Mon Sep 17 00:00:00 2001 From: yyforyongyu Date: Tue, 11 May 2021 21:00:00 +0800 Subject: [PATCH] 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. --- contractcourt/channel_arbitrator_test.go | 249 ++++++++++++++++++++ contractcourt/commit_sweep_resolver_test.go | 6 +- 2 files changed, 252 insertions(+), 3 deletions(-) diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index 5cf30d8f..34eab1e3 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "sort" "sync" "testing" "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. func TestChannelArbitratorAnchors(t *testing.T) { log := &mockArbitratorLog{ diff --git a/contractcourt/commit_sweep_resolver_test.go b/contractcourt/commit_sweep_resolver_test.go index cec78893..1321c032 100644 --- a/contractcourt/commit_sweep_resolver_test.go +++ b/contractcourt/commit_sweep_resolver_test.go @@ -109,7 +109,7 @@ type mockSweeper struct { sweepErr error createSweepTxChan chan *wire.MsgTx - deadlines []uint32 + deadlines []int } func newMockSweeper() *mockSweeper { @@ -118,7 +118,7 @@ func newMockSweeper() *mockSweeper { updatedInputs: make(chan wire.OutPoint), sweepTx: &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. 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)