github.com/decred/dcrlnd@v0.7.6/chanfitness/chaneventstore_test.go (about)

     1  package chanfitness
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     9  	"github.com/decred/dcrd/wire"
    10  	"github.com/decred/dcrlnd/channeldb"
    11  	"github.com/decred/dcrlnd/clock"
    12  	"github.com/decred/dcrlnd/routing/route"
    13  	"github.com/decred/dcrlnd/subscribe"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  // testNow is the current time tests will use.
    18  var testNow = time.Unix(1592465134, 0)
    19  
    20  // TestStartStoreError tests the starting of the store in cases where the setup
    21  // functions fail. It does not test the mechanics of consuming events because
    22  // these are covered in a separate set of tests.
    23  func TestStartStoreError(t *testing.T) {
    24  	// Ok and erroring subscribe functions are defined here to de-clutter
    25  	// tests.
    26  	okSubscribeFunc := func() (subscribe.Subscription, error) {
    27  		return newMockSubscription(t), nil
    28  	}
    29  
    30  	errSubscribeFunc := func() (subscribe.Subscription, error) {
    31  		return nil, errors.New("intentional test err")
    32  	}
    33  
    34  	tests := []struct {
    35  		name          string
    36  		ChannelEvents func() (subscribe.Subscription, error)
    37  		PeerEvents    func() (subscribe.Subscription, error)
    38  		GetChannels   func() ([]*channeldb.OpenChannel, error)
    39  	}{
    40  		{
    41  			name:          "Channel events fail",
    42  			ChannelEvents: errSubscribeFunc,
    43  		},
    44  		{
    45  			name:          "Peer events fail",
    46  			ChannelEvents: okSubscribeFunc,
    47  			PeerEvents:    errSubscribeFunc,
    48  		},
    49  		{
    50  			name:          "Get open channels fails",
    51  			ChannelEvents: okSubscribeFunc,
    52  			PeerEvents:    okSubscribeFunc,
    53  			GetChannels: func() ([]*channeldb.OpenChannel, error) {
    54  				return nil, errors.New("intentional test err")
    55  			},
    56  		},
    57  	}
    58  
    59  	for _, test := range tests {
    60  		test := test
    61  
    62  		t.Run(test.name, func(t *testing.T) {
    63  			clock := clock.NewTestClock(testNow)
    64  
    65  			store := NewChannelEventStore(&Config{
    66  				SubscribeChannelEvents: test.ChannelEvents,
    67  				SubscribePeerEvents:    test.PeerEvents,
    68  				GetOpenChannels:        test.GetChannels,
    69  				Clock:                  clock,
    70  			})
    71  
    72  			err := store.Start()
    73  			// Check that we receive an error, because the test only
    74  			// checks for error cases.
    75  			if err == nil {
    76  				t.Fatalf("Expected error on startup, got: nil")
    77  			}
    78  		})
    79  	}
    80  }
    81  
    82  // TestMonitorChannelEvents tests the store's handling of channel and peer
    83  // events. It tests for the unexpected cases where we receive a channel open for
    84  // an already known channel and but does not test for closing an unknown channel
    85  // because it would require custom logic in the test to prevent iterating
    86  // through an eventLog which does not exist. This test does not test handling
    87  // of uptime and lifespan requests, as they are tested in their own tests.
    88  func TestMonitorChannelEvents(t *testing.T) {
    89  	privKey, _ := secp256k1.GeneratePrivateKey()
    90  	var (
    91  		pubKey = privKey.PubKey()
    92  		chan1  = wire.OutPoint{Index: 1}
    93  		chan2  = wire.OutPoint{Index: 2}
    94  	)
    95  
    96  	peer1, err := route.NewVertexFromBytes(pubKey.SerializeCompressed())
    97  	require.NoError(t, err)
    98  
    99  	t.Run("peer comes online after channel open", func(t *testing.T) {
   100  		gen := func(ctx *chanEventStoreTestCtx) {
   101  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   102  			ctx.peerEvent(peer1, true)
   103  		}
   104  
   105  		testEventStore(t, gen, peer1, 1)
   106  	})
   107  
   108  	t.Run("duplicate channel open events", func(t *testing.T) {
   109  		gen := func(ctx *chanEventStoreTestCtx) {
   110  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   111  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   112  			ctx.peerEvent(peer1, true)
   113  		}
   114  
   115  		testEventStore(t, gen, peer1, 1)
   116  	})
   117  
   118  	t.Run("peer online before channel created", func(t *testing.T) {
   119  		gen := func(ctx *chanEventStoreTestCtx) {
   120  			ctx.peerEvent(peer1, true)
   121  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   122  		}
   123  
   124  		testEventStore(t, gen, peer1, 1)
   125  	})
   126  
   127  	t.Run("multiple channels for peer", func(t *testing.T) {
   128  		gen := func(ctx *chanEventStoreTestCtx) {
   129  			ctx.peerEvent(peer1, true)
   130  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   131  
   132  			ctx.peerEvent(peer1, false)
   133  			ctx.sendChannelOpenedUpdate(pubKey, chan2)
   134  		}
   135  
   136  		testEventStore(t, gen, peer1, 2)
   137  	})
   138  
   139  	t.Run("multiple channels for peer, one closed", func(t *testing.T) {
   140  		gen := func(ctx *chanEventStoreTestCtx) {
   141  			ctx.peerEvent(peer1, true)
   142  			ctx.sendChannelOpenedUpdate(pubKey, chan1)
   143  
   144  			ctx.peerEvent(peer1, false)
   145  			ctx.sendChannelOpenedUpdate(pubKey, chan2)
   146  
   147  			ctx.closeChannel(chan1, pubKey)
   148  			ctx.peerEvent(peer1, true)
   149  		}
   150  
   151  		testEventStore(t, gen, peer1, 1)
   152  	})
   153  
   154  }
   155  
   156  // testEventStore creates a new test contexts, generates a set of events for it
   157  // and tests that it has the number of channels we expect.
   158  func testEventStore(t *testing.T, generateEvents func(*chanEventStoreTestCtx),
   159  	peer route.Vertex, expectedChannels int) {
   160  
   161  	testCtx := newChanEventStoreTestCtx(t)
   162  	testCtx.start()
   163  
   164  	generateEvents(testCtx)
   165  
   166  	// Shutdown the store so that we can safely access the maps in our event
   167  	// store.
   168  	testCtx.stop()
   169  
   170  	// Get our peer and check that it has the channels we expect.
   171  	monitor, ok := testCtx.store.peers[peer]
   172  	require.True(t, ok)
   173  
   174  	require.Equal(t, expectedChannels, monitor.channelCount())
   175  }
   176  
   177  // TestStoreFlapCount tests flushing of flap counts to disk on timer ticks and
   178  // on store shutdown.
   179  func TestStoreFlapCount(t *testing.T) {
   180  	testCtx := newChanEventStoreTestCtx(t)
   181  	testCtx.start()
   182  
   183  	pubkey, _, _ := testCtx.createChannel()
   184  	testCtx.peerEvent(pubkey, false)
   185  
   186  	// Now, we tick our flap count ticker. We expect our main goroutine to
   187  	// flush our tick count to disk.
   188  	testCtx.tickFlapCount()
   189  
   190  	// Since we just tracked a offline event, we expect a single flap for
   191  	// our peer.
   192  	expectedUpdate := peerFlapCountMap{
   193  		pubkey: {
   194  			Count:    1,
   195  			LastFlap: testCtx.clock.Now(),
   196  		},
   197  	}
   198  
   199  	testCtx.assertFlapCountUpdated()
   200  	testCtx.assertFlapCountUpdates(expectedUpdate)
   201  
   202  	// Create three events for out peer, online/offline/online.
   203  	testCtx.peerEvent(pubkey, true)
   204  	testCtx.peerEvent(pubkey, false)
   205  	testCtx.peerEvent(pubkey, true)
   206  
   207  	// Trigger another write.
   208  	testCtx.tickFlapCount()
   209  
   210  	// Since we have processed 3 more events for our peer, we update our
   211  	// expected online map to have a flap count of 4 for this peer.
   212  	expectedUpdate[pubkey] = &channeldb.FlapCount{
   213  		Count:    4,
   214  		LastFlap: testCtx.clock.Now(),
   215  	}
   216  	testCtx.assertFlapCountUpdated()
   217  	testCtx.assertFlapCountUpdates(expectedUpdate)
   218  
   219  	testCtx.stop()
   220  }
   221  
   222  // TestGetChanInfo tests the GetChanInfo function for the cases where a channel
   223  // is known and unknown to the store.
   224  func TestGetChanInfo(t *testing.T) {
   225  	ctx := newChanEventStoreTestCtx(t)
   226  	ctx.start()
   227  
   228  	// Make a note of the time that our mocked clock starts on.
   229  	now := ctx.clock.Now()
   230  
   231  	// Create mock vars for a channel but do not add them to our store yet.
   232  	peer, pk, channel := ctx.newChannel()
   233  
   234  	// Send an online event for our peer, although we do not yet have an
   235  	// open channel.
   236  	ctx.peerEvent(peer, true)
   237  
   238  	// Try to get info for a channel that has not been opened yet, we
   239  	// expect to get an error.
   240  	_, err := ctx.store.GetChanInfo(channel, peer)
   241  	require.Equal(t, ErrChannelNotFound, err)
   242  
   243  	// Now we send our store a notification that a channel has been opened.
   244  	ctx.sendChannelOpenedUpdate(pk, channel)
   245  
   246  	// Wait for our channel to be recognized by our store. We need to wait
   247  	// for the channel to be created so that we do not update our time
   248  	// before the channel open is processed.
   249  	require.Eventually(t, func() bool {
   250  		_, err = ctx.store.GetChanInfo(channel, peer)
   251  		return err == nil
   252  	}, timeout, time.Millisecond*20)
   253  
   254  	// Increment our test clock by an hour.
   255  	now = now.Add(time.Hour)
   256  	ctx.clock.SetTime(now)
   257  
   258  	// At this stage our channel has been open and online for an hour.
   259  	info, err := ctx.store.GetChanInfo(channel, peer)
   260  	require.NoError(t, err)
   261  	require.Equal(t, time.Hour, info.Lifetime)
   262  	require.Equal(t, time.Hour, info.Uptime)
   263  
   264  	// Now we send a peer offline event for our channel.
   265  	ctx.peerEvent(peer, false)
   266  
   267  	// Since we have not bumped our mocked time, our uptime calculations
   268  	// should be the same, even though we've just processed an offline
   269  	// event.
   270  	info, err = ctx.store.GetChanInfo(channel, peer)
   271  	require.NoError(t, err)
   272  	require.Equal(t, time.Hour, info.Lifetime)
   273  	require.Equal(t, time.Hour, info.Uptime)
   274  
   275  	// Progress our time again. This time, our peer is currently tracked as
   276  	// being offline, so we expect our channel info to reflect that the peer
   277  	// has been offline for this period.
   278  	now = now.Add(time.Hour)
   279  	ctx.clock.SetTime(now)
   280  
   281  	info, err = ctx.store.GetChanInfo(channel, peer)
   282  	require.NoError(t, err)
   283  	require.Equal(t, time.Hour*2, info.Lifetime)
   284  	require.Equal(t, time.Hour, info.Uptime)
   285  
   286  	ctx.stop()
   287  }
   288  
   289  // TestFlapCount tests querying the store for peer flap counts, covering the
   290  // case where the peer is tracked in memory, and the case where we need to
   291  // lookup the peer on disk.
   292  func TestFlapCount(t *testing.T) {
   293  	clock := clock.NewTestClock(testNow)
   294  
   295  	var (
   296  		peer          = route.Vertex{9, 9, 9}
   297  		peerFlapCount = 3
   298  		lastFlap      = clock.Now()
   299  	)
   300  
   301  	// Create a test context with one peer's flap count already recorded,
   302  	// which mocks it already having its flap count stored on disk.
   303  	ctx := newChanEventStoreTestCtx(t)
   304  	ctx.flapUpdates[peer] = &channeldb.FlapCount{
   305  		Count:    uint32(peerFlapCount),
   306  		LastFlap: lastFlap,
   307  	}
   308  
   309  	ctx.start()
   310  
   311  	// Create test variables for a peer and channel, but do not add it to
   312  	// our store yet.
   313  	peer1 := route.Vertex{1, 2, 3}
   314  
   315  	// First, query for a peer that we have no record of in memory or on
   316  	// disk and confirm that we indicate that the peer was not found.
   317  	_, ts, err := ctx.store.FlapCount(peer1)
   318  	require.NoError(t, err)
   319  	require.Nil(t, ts)
   320  
   321  	// Send an online event for our peer.
   322  	ctx.peerEvent(peer1, true)
   323  
   324  	// Assert that we now find a record of the peer with flap count = 1.
   325  	count, ts, err := ctx.store.FlapCount(peer1)
   326  	require.NoError(t, err)
   327  	require.Equal(t, lastFlap, *ts)
   328  	require.Equal(t, 1, count)
   329  
   330  	// Make a request for our peer that not tracked in memory, but does
   331  	// have its flap count stored on disk.
   332  	count, ts, err = ctx.store.FlapCount(peer)
   333  	require.NoError(t, err)
   334  	require.Equal(t, lastFlap, *ts)
   335  	require.Equal(t, peerFlapCount, count)
   336  
   337  	ctx.stop()
   338  }