/ htlcswitch / circuit_map_test.go
circuit_map_test.go
  1  package htlcswitch_test
  2  
  3  import (
  4  	"bytes"
  5  	"fmt"
  6  	"io"
  7  	"testing"
  8  
  9  	"github.com/btcsuite/btcd/btcutil"
 10  	"github.com/btcsuite/btcd/wire"
 11  	"github.com/lightningnetwork/lnd/channeldb"
 12  	"github.com/lightningnetwork/lnd/htlcswitch"
 13  	"github.com/lightningnetwork/lnd/kvdb"
 14  	"github.com/lightningnetwork/lnd/lnwire"
 15  	"github.com/stretchr/testify/require"
 16  )
 17  
 18  var (
 19  	// closedChannelBucket stores summarization information concerning
 20  	// previously open, but now closed channels.
 21  	closedChannelBucket = []byte("closed-chan-bucket")
 22  )
 23  
 24  // TestCircuitMapCleanClosedChannels checks that the circuits and keystones are
 25  // deleted for closed channels upon restart.
 26  func TestCircuitMapCleanClosedChannels(t *testing.T) {
 27  	t.Parallel()
 28  
 29  	var (
 30  		// chanID0 is a zero value channel ID indicating a locally
 31  		// initiated payment.
 32  		chanID0 = lnwire.NewShortChanIDFromInt(uint64(0))
 33  		chanID1 = lnwire.NewShortChanIDFromInt(uint64(1))
 34  		chanID2 = lnwire.NewShortChanIDFromInt(uint64(2))
 35  
 36  		inKey00  = htlcswitch.CircuitKey{ChanID: chanID0, HtlcID: 0}
 37  		inKey10  = htlcswitch.CircuitKey{ChanID: chanID1, HtlcID: 0}
 38  		inKey11  = htlcswitch.CircuitKey{ChanID: chanID1, HtlcID: 1}
 39  		inKey20  = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 0}
 40  		inKey21  = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 1}
 41  		inKey22  = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 2}
 42  		outKey00 = htlcswitch.CircuitKey{ChanID: chanID0, HtlcID: 0}
 43  		outKey10 = htlcswitch.CircuitKey{ChanID: chanID1, HtlcID: 0}
 44  		outKey11 = htlcswitch.CircuitKey{ChanID: chanID1, HtlcID: 1}
 45  		outKey20 = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 0}
 46  		outKey21 = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 1}
 47  		outKey22 = htlcswitch.CircuitKey{ChanID: chanID2, HtlcID: 2}
 48  	)
 49  
 50  	type closeChannelParams struct {
 51  		chanID    lnwire.ShortChannelID
 52  		isPending bool
 53  	}
 54  
 55  	testParams := []struct {
 56  		name string
 57  
 58  		// keystones is used to create and open circuits. A keystone is
 59  		// a pair of circuit keys, inKey and outKey, with the outKey
 60  		// optionally being empty. If a keystone with an outKey is used,
 61  		// a circuit will be created and opened, thus creating a circuit
 62  		// and a keystone in the DB. Otherwise, only the circuit is
 63  		// created.
 64  		keystones []htlcswitch.Keystone
 65  
 66  		chanParams []closeChannelParams
 67  		deleted    []htlcswitch.Keystone
 68  		untouched  []htlcswitch.Keystone
 69  
 70  		// If resMsg is true, then closed channels will not delete
 71  		// circuits if the channel was the keystone / outgoing key in
 72  		// the open circuit.
 73  		resMsg bool
 74  	}{
 75  		{
 76  			name: "no deletion if there are no closed channels",
 77  			keystones: []htlcswitch.Keystone{
 78  				// Creates a circuit and a keystone
 79  				{InKey: inKey10, OutKey: outKey10},
 80  			},
 81  			untouched: []htlcswitch.Keystone{
 82  				{InKey: inKey10, OutKey: outKey10},
 83  			},
 84  		},
 85  		{
 86  			name: "no deletion if channel is pending close",
 87  			chanParams: []closeChannelParams{
 88  				// Creates a pending close channel.
 89  				{chanID: chanID1, isPending: true},
 90  			},
 91  			keystones: []htlcswitch.Keystone{
 92  				// Creates a circuit and a keystone
 93  				{InKey: inKey10, OutKey: outKey10},
 94  			},
 95  			untouched: []htlcswitch.Keystone{
 96  				{InKey: inKey10, OutKey: outKey10},
 97  			},
 98  		},
 99  		{
100  			name: "no deletion if the chanID is zero value",
101  			chanParams: []closeChannelParams{
102  				// Creates a close channel with chanID0.
103  				{chanID: chanID0, isPending: false},
104  			},
105  			keystones: []htlcswitch.Keystone{
106  				// Creates a circuit and a keystone
107  				{InKey: inKey00, OutKey: outKey00},
108  			},
109  			untouched: []htlcswitch.Keystone{
110  				{InKey: inKey00, OutKey: outKey00},
111  			},
112  		},
113  		{
114  			name: "delete half circuits on inKey match",
115  			chanParams: []closeChannelParams{
116  				// Creates a close channel with chanID1.
117  				{chanID: chanID1, isPending: false},
118  			},
119  			keystones: []htlcswitch.Keystone{
120  				// Creates a circuit, no keystone created
121  				{InKey: inKey10},
122  				// Creates a circuit, no keystone created
123  				{InKey: inKey11},
124  				// Creates a circuit and a keystone
125  				{InKey: inKey20, OutKey: outKey20},
126  			},
127  			deleted: []htlcswitch.Keystone{
128  				{InKey: inKey10}, {InKey: inKey11},
129  			},
130  			untouched: []htlcswitch.Keystone{
131  				{InKey: inKey20, OutKey: outKey20},
132  			},
133  		},
134  		{
135  			name: "delete half circuits on outKey match",
136  			chanParams: []closeChannelParams{
137  				// Creates a close channel with chanID1.
138  				{chanID: chanID1, isPending: false},
139  			},
140  			keystones: []htlcswitch.Keystone{
141  				// Creates a circuit and a keystone
142  				{InKey: inKey20, OutKey: outKey10},
143  				// Creates a circuit and a keystone
144  				{InKey: inKey21, OutKey: outKey11},
145  				// Creates a circuit and a keystone
146  				{InKey: inKey22, OutKey: outKey21},
147  			},
148  			deleted: []htlcswitch.Keystone{
149  				{InKey: inKey20, OutKey: outKey10},
150  				{InKey: inKey21, OutKey: outKey11},
151  			},
152  			untouched: []htlcswitch.Keystone{
153  				{InKey: inKey22, OutKey: outKey21},
154  			},
155  		},
156  		{
157  			name: "delete full circuits on inKey match",
158  			chanParams: []closeChannelParams{
159  				// Creates a close channel with chanID1.
160  				{chanID: chanID1, isPending: false},
161  			},
162  			keystones: []htlcswitch.Keystone{
163  				// Creates a circuit and a keystone
164  				{InKey: inKey10, OutKey: outKey20},
165  				// Creates a circuit and a keystone
166  				{InKey: inKey11, OutKey: outKey21},
167  				// Creates a circuit and a keystone
168  				{InKey: inKey20, OutKey: outKey22},
169  			},
170  			deleted: []htlcswitch.Keystone{
171  				{InKey: inKey10, OutKey: outKey20},
172  				{InKey: inKey11, OutKey: outKey21},
173  			},
174  			untouched: []htlcswitch.Keystone{
175  				{InKey: inKey20, OutKey: outKey22},
176  			},
177  		},
178  		{
179  			name: "delete full circuits on outKey match",
180  			chanParams: []closeChannelParams{
181  				// Creates a close channel with chanID1.
182  				{chanID: chanID1, isPending: false},
183  			},
184  			keystones: []htlcswitch.Keystone{
185  				// Creates a circuit and a keystone
186  				{InKey: inKey20, OutKey: outKey10},
187  				// Creates a circuit and a keystone
188  				{InKey: inKey21, OutKey: outKey11},
189  				// Creates a circuit and a keystone
190  				{InKey: inKey22, OutKey: outKey20},
191  			},
192  			deleted: []htlcswitch.Keystone{
193  				{InKey: inKey20, OutKey: outKey10},
194  				{InKey: inKey21, OutKey: outKey11},
195  			},
196  			untouched: []htlcswitch.Keystone{
197  				{InKey: inKey22, OutKey: outKey20},
198  			},
199  		},
200  		{
201  			name: "delete all circuits",
202  			chanParams: []closeChannelParams{
203  				// Creates a close channel with chanID1.
204  				{chanID: chanID1, isPending: false},
205  				// Creates a close channel with chanID2.
206  				{chanID: chanID2, isPending: false},
207  			},
208  			keystones: []htlcswitch.Keystone{
209  				// Creates a circuit and a keystone
210  				{InKey: inKey20, OutKey: outKey10},
211  				// Creates a circuit and a keystone
212  				{InKey: inKey21, OutKey: outKey11},
213  				// Creates a circuit and a keystone
214  				{InKey: inKey22, OutKey: outKey20},
215  			},
216  			deleted: []htlcswitch.Keystone{
217  				{InKey: inKey20, OutKey: outKey10},
218  				{InKey: inKey21, OutKey: outKey11},
219  				{InKey: inKey22, OutKey: outKey20},
220  			},
221  		},
222  		{
223  			name: "don't delete circuits for outgoing",
224  			chanParams: []closeChannelParams{
225  				// Creates a close channel with chanID1.
226  				{chanID: chanID1, isPending: false},
227  			},
228  			keystones: []htlcswitch.Keystone{
229  				// Creates a circuit and a keystone
230  				{InKey: inKey10, OutKey: outKey10},
231  				// Creates a circuit and a keystone
232  				{InKey: inKey11, OutKey: outKey20},
233  				// Creates a circuit and a keystone
234  				{InKey: inKey00, OutKey: outKey11},
235  			},
236  			deleted: []htlcswitch.Keystone{
237  				{InKey: inKey10, OutKey: outKey10},
238  				{InKey: inKey11, OutKey: outKey20},
239  			},
240  			resMsg: true,
241  		},
242  	}
243  
244  	for _, tt := range testParams {
245  		test := tt
246  
247  		t.Run(test.name, func(t *testing.T) {
248  			cfg, circuitMap := newCircuitMap(t, test.resMsg)
249  
250  			// create test circuits
251  			for _, ks := range test.keystones {
252  				err := createTestCircuit(ks, circuitMap)
253  				require.NoError(
254  					t, err,
255  					"failed to create test circuit",
256  				)
257  			}
258  
259  			// create close channels
260  			err := kvdb.Update(cfg.DB, func(tx kvdb.RwTx) error {
261  				for _, channel := range test.chanParams {
262  					if err := createTestCloseChannelSummery(
263  						tx, channel.isPending,
264  						channel.chanID,
265  					); err != nil {
266  						return err
267  					}
268  				}
269  				return nil
270  			}, func() {})
271  
272  			require.NoError(
273  				t, err,
274  				"failed to create close channel summery",
275  			)
276  
277  			// Now, restart the circuit map, and check that the
278  			// circuits and keystones of closed channels are
279  			// deleted in DB.
280  			_, circuitMap = restartCircuitMap(t, cfg)
281  
282  			// Check that items are deleted. LookupCircuit and
283  			// LookupOpenCircuit will check the cached circuits,
284  			// which are loaded on restart from the DB.
285  			for _, ks := range test.deleted {
286  				assertKeystoneDeleted(t, circuitMap, ks)
287  			}
288  
289  			// We also check we are not deleting wanted circuits.
290  			for _, ks := range test.untouched {
291  				assertKeystoneNotDeleted(t, circuitMap, ks)
292  			}
293  		})
294  	}
295  }
296  
297  // createTestCircuit creates a circuit for testing with its incoming key being
298  // the keystone's InKey. If the keystone has an OutKey, the circuit will be
299  // opened, which causes a Keystone to be created in DB.
300  func createTestCircuit(ks htlcswitch.Keystone, cm htlcswitch.CircuitMap) error {
301  	circuit := &htlcswitch.PaymentCircuit{
302  		Incoming:       ks.InKey,
303  		ErrorEncrypter: testExtracter,
304  	}
305  
306  	// First we will try to add an new circuit to the circuit map, this
307  	// should succeed.
308  	_, err := cm.CommitCircuits(circuit)
309  	if err != nil {
310  		return fmt.Errorf("failed to commit circuits: %w", err)
311  	}
312  
313  	// If the keystone has no outgoing key, we won't open it.
314  	if ks.OutKey == htlcswitch.EmptyCircuitKey {
315  		return nil
316  	}
317  
318  	// Open the circuit, implicitly creates a keystone on disk.
319  	err = cm.OpenCircuits(ks)
320  	if err != nil {
321  		return fmt.Errorf("failed to open circuits: %w", err)
322  	}
323  
324  	return nil
325  }
326  
327  // assertKeystoneDeleted checks that a given keystone is deleted from the
328  // circuit map.
329  func assertKeystoneDeleted(t *testing.T,
330  	cm htlcswitch.CircuitLookup, ks htlcswitch.Keystone) {
331  
332  	c := cm.LookupCircuit(ks.InKey)
333  	require.Nil(t, c, "no circuit should be found using InKey")
334  
335  	if ks.OutKey != htlcswitch.EmptyCircuitKey {
336  		c = cm.LookupOpenCircuit(ks.OutKey)
337  		require.Nil(t, c, "no circuit should be found using OutKey")
338  	}
339  }
340  
341  // assertKeystoneDeleted checks that a given keystone is not deleted from the
342  // circuit map.
343  func assertKeystoneNotDeleted(t *testing.T,
344  	cm htlcswitch.CircuitLookup, ks htlcswitch.Keystone) {
345  
346  	c := cm.LookupCircuit(ks.InKey)
347  	require.NotNil(t, c, "expecting circuit found using InKey")
348  
349  	if ks.OutKey != htlcswitch.EmptyCircuitKey {
350  		c = cm.LookupOpenCircuit(ks.OutKey)
351  		require.NotNil(t, c, "expecting circuit found using OutKey")
352  	}
353  }
354  
355  // createTestCloseChannelSummery creates a CloseChannelSummery for testing.
356  func createTestCloseChannelSummery(tx kvdb.RwTx, isPending bool,
357  	chanID lnwire.ShortChannelID) error {
358  
359  	closedChanBucket, err := tx.CreateTopLevelBucket(closedChannelBucket)
360  	if err != nil {
361  		return err
362  	}
363  	outputPoint := wire.OutPoint{Hash: hash1, Index: 1}
364  
365  	ccs := &channeldb.ChannelCloseSummary{
366  		ChanPoint:      outputPoint,
367  		ShortChanID:    chanID,
368  		ChainHash:      hash1,
369  		ClosingTXID:    hash2,
370  		CloseHeight:    100,
371  		RemotePub:      testEphemeralKey,
372  		Capacity:       btcutil.Amount(10000),
373  		SettledBalance: btcutil.Amount(50000),
374  		CloseType:      channeldb.RemoteForceClose,
375  		IsPending:      isPending,
376  	}
377  	var b bytes.Buffer
378  	if err := serializeChannelCloseSummary(&b, ccs); err != nil {
379  		return err
380  	}
381  
382  	var chanPointBuf bytes.Buffer
383  	if err := lnwire.WriteOutPoint(&chanPointBuf, outputPoint); err != nil {
384  		return err
385  	}
386  
387  	return closedChanBucket.Put(chanPointBuf.Bytes(), b.Bytes())
388  }
389  
390  func serializeChannelCloseSummary(
391  	w io.Writer,
392  	cs *channeldb.ChannelCloseSummary) error {
393  
394  	err := channeldb.WriteElements(
395  		w,
396  		cs.ChanPoint, cs.ShortChanID, cs.ChainHash, cs.ClosingTXID,
397  		cs.CloseHeight, cs.RemotePub, cs.Capacity, cs.SettledBalance,
398  		cs.TimeLockedBalance, cs.CloseType, cs.IsPending,
399  	)
400  	if err != nil {
401  		return err
402  	}
403  
404  	// If this is a close channel summary created before the addition of
405  	// the new fields, then we can exit here.
406  	if cs.RemoteCurrentRevocation == nil {
407  		return channeldb.WriteElements(w, false)
408  	}
409  
410  	return nil
411  }