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)