github.com/decred/dcrlnd@v0.7.6/chanbackup/pubsub_test.go (about)

     1  package chanbackup
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/decred/dcrd/wire"
     9  	"github.com/decred/dcrlnd/keychain"
    10  )
    11  
    12  type mockSwapper struct {
    13  	fail bool
    14  
    15  	swaps chan PackedMulti
    16  
    17  	swapState *Multi
    18  
    19  	keyChain keychain.KeyRing
    20  }
    21  
    22  func newMockSwapper(keychain keychain.KeyRing) *mockSwapper {
    23  	return &mockSwapper{
    24  		swaps:     make(chan PackedMulti, 1),
    25  		keyChain:  keychain,
    26  		swapState: &Multi{},
    27  	}
    28  }
    29  
    30  func (m *mockSwapper) UpdateAndSwap(newBackup PackedMulti) error {
    31  	if m.fail {
    32  		return fmt.Errorf("fail")
    33  	}
    34  
    35  	swapState, err := newBackup.Unpack(m.keyChain)
    36  	if err != nil {
    37  		return fmt.Errorf("unable to decode on disk swaps: %v", err)
    38  	}
    39  
    40  	m.swapState = swapState
    41  
    42  	m.swaps <- newBackup
    43  
    44  	return nil
    45  }
    46  
    47  func (m *mockSwapper) ExtractMulti(keychain keychain.KeyRing) (*Multi, error) {
    48  	return m.swapState, nil
    49  }
    50  
    51  type mockChannelNotifier struct {
    52  	fail bool
    53  
    54  	chanEvents chan ChannelEvent
    55  }
    56  
    57  func newMockChannelNotifier() *mockChannelNotifier {
    58  	return &mockChannelNotifier{
    59  		chanEvents: make(chan ChannelEvent),
    60  	}
    61  }
    62  
    63  func (m *mockChannelNotifier) SubscribeChans(chans map[wire.OutPoint]struct{}) (
    64  	*ChannelSubscription, error) {
    65  
    66  	if m.fail {
    67  		return nil, fmt.Errorf("fail")
    68  	}
    69  
    70  	return &ChannelSubscription{
    71  		ChanUpdates: m.chanEvents,
    72  		Cancel: func() {
    73  		},
    74  	}, nil
    75  }
    76  
    77  // TestNewSubSwapperSubscribeFail tests that if we're unable to obtain a
    78  // channel subscription, then the entire sub-swapper will fail to start.
    79  func TestNewSubSwapperSubscribeFail(t *testing.T) {
    80  	t.Parallel()
    81  
    82  	keyRing := &mockKeyRing{}
    83  
    84  	var swapper mockSwapper
    85  	chanNotifier := mockChannelNotifier{
    86  		fail: true,
    87  	}
    88  
    89  	_, err := NewSubSwapper(nil, &chanNotifier, keyRing, &swapper)
    90  	if err == nil {
    91  		t.Fatalf("expected fail due to lack of subscription")
    92  	}
    93  }
    94  
    95  func assertExpectedBackupSwap(t *testing.T, swapper *mockSwapper,
    96  	subSwapper *SubSwapper, keyRing keychain.KeyRing,
    97  	expectedChanSet map[wire.OutPoint]Single) {
    98  
    99  	t.Helper()
   100  
   101  	select {
   102  	case newPackedMulti := <-swapper.swaps:
   103  		// If we unpack the new multi, then we should find all the old
   104  		// channels, and also the new channel included and any deleted
   105  		// channel omitted.
   106  		newMulti, err := newPackedMulti.Unpack(keyRing)
   107  		if err != nil {
   108  			t.Fatalf("unable to unpack multi: %v", err)
   109  		}
   110  
   111  		// Ensure that once unpacked, the current backup has the
   112  		// expected number of Singles.
   113  		if len(newMulti.StaticBackups) != len(expectedChanSet) {
   114  			t.Fatalf("new backup wasn't included: expected %v "+
   115  				"backups have %v", len(expectedChanSet),
   116  				len(newMulti.StaticBackups))
   117  		}
   118  
   119  		// We should also find all the old and new channels in this new
   120  		// backup.
   121  		for _, backup := range newMulti.StaticBackups {
   122  			_, ok := expectedChanSet[backup.FundingOutpoint]
   123  			if !ok {
   124  				t.Fatalf("didn't find backup in original set: %v",
   125  					backup.FundingOutpoint)
   126  			}
   127  		}
   128  
   129  		// The same applies for our in-memory state, but it's also
   130  		// possible for there to be items in the on-disk state that we
   131  		// don't know of explicit.
   132  		newChans := make(map[wire.OutPoint]Single)
   133  		for _, newChan := range newMulti.StaticBackups {
   134  			newChans[newChan.FundingOutpoint] = newChan
   135  		}
   136  		for _, backup := range subSwapper.backupState {
   137  			_, ok := newChans[backup.FundingOutpoint]
   138  			if !ok {
   139  				t.Fatalf("didn't find backup in original set: %v",
   140  					backup.FundingOutpoint)
   141  			}
   142  		}
   143  
   144  	case <-time.After(time.Second * 5):
   145  		t.Fatalf("update swapper didn't swap out multi")
   146  	}
   147  }
   148  
   149  // TestSubSwapperIdempotentStartStop tests that calling the Start/Stop methods
   150  // multiple time is permitted.
   151  func TestSubSwapperIdempotentStartStop(t *testing.T) {
   152  	t.Parallel()
   153  
   154  	keyRing := &mockKeyRing{}
   155  
   156  	var chanNotifier mockChannelNotifier
   157  
   158  	swapper := newMockSwapper(keyRing)
   159  	subSwapper, err := NewSubSwapper(nil, &chanNotifier, keyRing, swapper)
   160  	if err != nil {
   161  		t.Fatalf("unable to init subSwapper: %v", err)
   162  	}
   163  
   164  	if err := subSwapper.Start(); err != nil {
   165  		t.Fatalf("unable to start swapper: %v", err)
   166  	}
   167  
   168  	// The swapper should write the initial channel state as soon as it's
   169  	// active.
   170  	backupSet := make(map[wire.OutPoint]Single)
   171  	assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
   172  
   173  	subSwapper.Start()
   174  
   175  	subSwapper.Stop()
   176  	subSwapper.Stop()
   177  }
   178  
   179  // TestSubSwapperUpdater tests that the SubSwapper will properly swap out
   180  // new/old channels within the channel set, and notify the swapper to update
   181  // the master multi file backup.
   182  func TestSubSwapperUpdater(t *testing.T) {
   183  	t.Parallel()
   184  
   185  	keyRing := &mockKeyRing{}
   186  	chanNotifier := newMockChannelNotifier()
   187  	swapper := newMockSwapper(keyRing)
   188  
   189  	// First, we'll start out by creating a channels set for the initial
   190  	// set of channels known to the sub-swapper.
   191  	const numStartingChans = 3
   192  	initialChanSet := make([]Single, 0, numStartingChans)
   193  	backupSet := make(map[wire.OutPoint]Single)
   194  	for i := 0; i < numStartingChans; i++ {
   195  		channel, err := genRandomOpenChannelShell()
   196  		if err != nil {
   197  			t.Fatalf("unable to make test chan: %v", err)
   198  		}
   199  
   200  		single := NewSingle(channel, nil)
   201  
   202  		backupSet[channel.FundingOutpoint] = single
   203  		initialChanSet = append(initialChanSet, single)
   204  	}
   205  
   206  	// We'll also generate two additional channels which will already be
   207  	// present on disk. However, these will at first only be known by the
   208  	// on disk backup (the backup set).
   209  	const numDiskChans = 2
   210  	for i := 0; i < numDiskChans; i++ {
   211  		channel, err := genRandomOpenChannelShell()
   212  		if err != nil {
   213  			t.Fatalf("unable to make test chan: %v", err)
   214  		}
   215  
   216  		single := NewSingle(channel, nil)
   217  
   218  		backupSet[channel.FundingOutpoint] = single
   219  		swapper.swapState.StaticBackups = append(
   220  			swapper.swapState.StaticBackups, single,
   221  		)
   222  	}
   223  
   224  	// With our channel set created, we'll make a fresh sub swapper
   225  	// instance to begin our test.
   226  	subSwapper, err := NewSubSwapper(
   227  		initialChanSet, chanNotifier, keyRing, swapper,
   228  	)
   229  	if err != nil {
   230  		t.Fatalf("unable to make swapper: %v", err)
   231  	}
   232  	if err := subSwapper.Start(); err != nil {
   233  		t.Fatalf("unable to start sub swapper: %v", err)
   234  	}
   235  	defer subSwapper.Stop()
   236  
   237  	// The swapper should write the initial channel state as soon as it's
   238  	// active.
   239  	assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
   240  
   241  	// Now that the sub-swapper is active, we'll notify to add a brand new
   242  	// channel to the channel state.
   243  	newChannel, err := genRandomOpenChannelShell()
   244  	if err != nil {
   245  		t.Fatalf("unable to create new chan: %v", err)
   246  	}
   247  
   248  	// With the new channel created, we'll send a new update to the main
   249  	// goroutine telling it about this new channel.
   250  	select {
   251  	case chanNotifier.chanEvents <- ChannelEvent{
   252  		NewChans: []ChannelWithAddrs{
   253  			{
   254  				OpenChannel: newChannel,
   255  			},
   256  		},
   257  	}:
   258  	case <-time.After(time.Second * 5):
   259  		t.Fatalf("update swapper didn't read new channel: %v", err)
   260  	}
   261  
   262  	backupSet[newChannel.FundingOutpoint] = NewSingle(newChannel, nil)
   263  
   264  	// At this point, the sub-swapper should now have packed a new multi,
   265  	// and then sent it to the swapper so the back up can be updated.
   266  	assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
   267  
   268  	// We'll now trigger an update to remove an existing channel.
   269  	chanToDelete := initialChanSet[0].FundingOutpoint
   270  	select {
   271  	case chanNotifier.chanEvents <- ChannelEvent{
   272  		ClosedChans: []wire.OutPoint{chanToDelete},
   273  	}:
   274  
   275  	case <-time.After(time.Second * 5):
   276  		t.Fatalf("update swapper didn't read new channel: %v", err)
   277  	}
   278  
   279  	delete(backupSet, chanToDelete)
   280  
   281  	// Verify that the new set of backups, now has one less after the
   282  	// sub-swapper switches the new set with the old.
   283  	assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet)
   284  }