github.com/status-im/status-go@v1.1.0/services/ext/mailservers/connmanager_test.go (about)

     1  package mailservers
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/ethereum/go-ethereum/event"
    13  	"github.com/ethereum/go-ethereum/p2p"
    14  	"github.com/ethereum/go-ethereum/p2p/enode"
    15  
    16  	"github.com/status-im/status-go/eth-node/types"
    17  	"github.com/status-im/status-go/t/utils"
    18  )
    19  
    20  type fakePeerEvents struct {
    21  	mu    sync.Mutex
    22  	nodes map[types.EnodeID]struct{}
    23  	input chan *p2p.PeerEvent
    24  }
    25  
    26  func (f *fakePeerEvents) Nodes() []types.EnodeID {
    27  	f.mu.Lock()
    28  	rst := make([]types.EnodeID, 0, len(f.nodes))
    29  	for n := range f.nodes {
    30  		rst = append(rst, n)
    31  	}
    32  	f.mu.Unlock()
    33  	return rst
    34  }
    35  
    36  func (f *fakePeerEvents) AddPeer(node *enode.Node) {
    37  	f.mu.Lock()
    38  	f.nodes[types.EnodeID(node.ID())] = struct{}{}
    39  	f.mu.Unlock()
    40  	if f.input == nil {
    41  		return
    42  	}
    43  	f.input <- &p2p.PeerEvent{
    44  		Peer: node.ID(),
    45  		Type: p2p.PeerEventTypeAdd,
    46  	}
    47  }
    48  
    49  func (f *fakePeerEvents) RemovePeer(node *enode.Node) {
    50  	f.mu.Lock()
    51  	delete(f.nodes, types.EnodeID(node.ID()))
    52  	f.mu.Unlock()
    53  	if f.input == nil {
    54  		return
    55  	}
    56  	f.input <- &p2p.PeerEvent{
    57  		Peer: node.ID(),
    58  		Type: p2p.PeerEventTypeDrop,
    59  	}
    60  }
    61  
    62  func newFakePeerAdderRemover() *fakePeerEvents {
    63  	return &fakePeerEvents{nodes: map[types.EnodeID]struct{}{}}
    64  }
    65  
    66  func (f *fakePeerEvents) SubscribeEvents(output chan *p2p.PeerEvent) event.Subscription {
    67  	return event.NewSubscription(func(quit <-chan struct{}) error {
    68  		for {
    69  			select {
    70  			case <-quit:
    71  				return nil
    72  			case ev := <-f.input:
    73  				// will block the same way as in any feed
    74  				output <- ev
    75  			}
    76  		}
    77  	})
    78  }
    79  
    80  func newFakeServer() *fakePeerEvents {
    81  	srv := newFakePeerAdderRemover()
    82  	srv.input = make(chan *p2p.PeerEvent, 20)
    83  	return srv
    84  }
    85  
    86  type fakeEnvelopeEvents struct {
    87  	input chan types.EnvelopeEvent
    88  }
    89  
    90  func (f *fakeEnvelopeEvents) SubscribeEnvelopeEvents(output chan<- types.EnvelopeEvent) types.Subscription {
    91  	return event.NewSubscription(func(quit <-chan struct{}) error {
    92  		for {
    93  			select {
    94  			case <-quit:
    95  				return nil
    96  			case ev := <-f.input:
    97  				// will block the same way as in any feed
    98  				output <- ev
    99  			}
   100  		}
   101  	})
   102  }
   103  
   104  func newFakeEnvelopesEvents() *fakeEnvelopeEvents {
   105  	return &fakeEnvelopeEvents{
   106  		input: make(chan types.EnvelopeEvent),
   107  	}
   108  }
   109  
   110  func fillWithRandomNodes(t *testing.T, nodes []*enode.Node) {
   111  	var err error
   112  	for i := range nodes {
   113  		nodes[i], err = RandomNode()
   114  		require.NoError(t, err)
   115  	}
   116  }
   117  
   118  func getMapWithRandomNodes(t *testing.T, n int) map[types.EnodeID]*enode.Node {
   119  	nodes := make([]*enode.Node, n)
   120  	fillWithRandomNodes(t, nodes)
   121  	return nodesToMap(nodes)
   122  }
   123  
   124  func mergeOldIntoNew(old, new map[types.EnodeID]*enode.Node) {
   125  	for n := range old {
   126  		new[n] = old[n]
   127  	}
   128  }
   129  
   130  func TestReplaceNodes(t *testing.T) {
   131  	type testCase struct {
   132  		description string
   133  		old         map[types.EnodeID]*enode.Node
   134  		new         map[types.EnodeID]*enode.Node
   135  		target      int
   136  	}
   137  	for _, tc := range []testCase{
   138  		{
   139  			"InitialReplace",
   140  			getMapWithRandomNodes(t, 0),
   141  			getMapWithRandomNodes(t, 3),
   142  			2,
   143  		},
   144  		{
   145  			"FullReplace",
   146  			getMapWithRandomNodes(t, 3),
   147  			getMapWithRandomNodes(t, 3),
   148  			2,
   149  		},
   150  	} {
   151  		t.Run(tc.description, func(t *testing.T) {
   152  			peers := newFakePeerAdderRemover()
   153  			state := newInternalState(peers, tc.target, 0)
   154  			state.replaceNodes(tc.old)
   155  			require.Len(t, peers.nodes, len(tc.old))
   156  			for n := range peers.nodes {
   157  				require.Contains(t, tc.old, n)
   158  			}
   159  			state.replaceNodes(tc.new)
   160  			require.Len(t, peers.nodes, len(tc.new))
   161  			for n := range peers.nodes {
   162  				require.Contains(t, tc.new, n)
   163  			}
   164  		})
   165  	}
   166  }
   167  
   168  func TestPartialReplaceNodesBelowTarget(t *testing.T) {
   169  	peers := newFakePeerAdderRemover()
   170  	old := getMapWithRandomNodes(t, 1)
   171  	new := getMapWithRandomNodes(t, 2)
   172  	state := newInternalState(peers, 2, 0)
   173  	state.replaceNodes(old)
   174  	mergeOldIntoNew(old, new)
   175  	state.replaceNodes(new)
   176  	require.Len(t, peers.nodes, len(new))
   177  }
   178  
   179  func TestPartialReplaceNodesAboveTarget(t *testing.T) {
   180  	peers := newFakePeerAdderRemover()
   181  	initial, err := RandomNode()
   182  	require.NoError(t, err)
   183  	old := nodesToMap([]*enode.Node{initial})
   184  	new := getMapWithRandomNodes(t, 2)
   185  	state := newInternalState(peers, 1, 0)
   186  	state.replaceNodes(old)
   187  	state.nodeAdded(types.EnodeID(initial.ID()))
   188  	mergeOldIntoNew(old, new)
   189  	state.replaceNodes(new)
   190  	require.Len(t, peers.nodes, 1)
   191  }
   192  
   193  func TestConnectionManagerAddDrop(t *testing.T) {
   194  	server := newFakeServer()
   195  	whisper := newFakeEnvelopesEvents()
   196  	target := 1
   197  	connmanager := NewConnectionManager(server, whisper, target, 1, 0)
   198  	connmanager.Start()
   199  	defer connmanager.Stop()
   200  	nodes := []*enode.Node{}
   201  	for _, n := range getMapWithRandomNodes(t, 3) {
   202  		nodes = append(nodes, n)
   203  	}
   204  	// Send 3 random nodes to connection manager.
   205  	connmanager.Notify(nodes)
   206  	var initial enode.ID
   207  	// Wait till connection manager establishes connection with 1 peer.
   208  	require.NoError(t, utils.Eventually(func() error {
   209  		nodes := server.Nodes()
   210  		if len(nodes) != target {
   211  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   212  		}
   213  		initial = enode.ID(nodes[0])
   214  		return nil
   215  	}, time.Second, 100*time.Millisecond))
   216  	// Send an event that peer was dropped.
   217  	select {
   218  	case server.input <- &p2p.PeerEvent{Peer: initial, Type: p2p.PeerEventTypeDrop}:
   219  	case <-time.After(time.Second):
   220  		require.FailNow(t, "can't send a drop event")
   221  	}
   222  	// Connection manager should establish connection with any other peer from initial list.
   223  	require.NoError(t, utils.Eventually(func() error {
   224  		nodes := server.Nodes()
   225  		if len(nodes) != target {
   226  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   227  		}
   228  		if enode.ID(nodes[0]) == initial {
   229  			return fmt.Errorf("connected node wasn't changed from %s", initial)
   230  		}
   231  		return nil
   232  	}, time.Second, 100*time.Millisecond))
   233  }
   234  
   235  func TestConnectionManagerReplace(t *testing.T) {
   236  	server := newFakeServer()
   237  	whisper := newFakeEnvelopesEvents()
   238  	target := 1
   239  	connmanager := NewConnectionManager(server, whisper, target, 1, 0)
   240  	connmanager.Start()
   241  	defer connmanager.Stop()
   242  	nodes := []*enode.Node{}
   243  	for _, n := range getMapWithRandomNodes(t, 3) {
   244  		nodes = append(nodes, n)
   245  	}
   246  	// Send a single node to connection manager.
   247  	connmanager.Notify(nodes[:1])
   248  	// Wait until this node will get connected.
   249  	require.NoError(t, utils.Eventually(func() error {
   250  		connected := server.Nodes()
   251  		if len(connected) != target {
   252  			return fmt.Errorf("unexpected number of connected servers: %d", len(connected))
   253  		}
   254  		if types.EnodeID(nodes[0].ID()) != connected[0] {
   255  			return fmt.Errorf("connected with a wrong peer. expected %s, got %s", nodes[0].ID(), connected[0])
   256  		}
   257  		return nil
   258  	}, time.Second, 100*time.Millisecond))
   259  	// Replace previously sent node with 2 different nodes.
   260  	connmanager.Notify(nodes[1:])
   261  	// Wait until connection manager replaces node connected in the first round.
   262  	require.NoError(t, utils.Eventually(func() error {
   263  		connected := server.Nodes()
   264  		if len(connected) != target {
   265  			return fmt.Errorf("unexpected number of connected servers: %d", len(connected))
   266  		}
   267  		switch enode.ID(connected[0]) {
   268  		case nodes[1].ID():
   269  		case nodes[2].ID():
   270  		default:
   271  			return fmt.Errorf("connected with unexpected peer. got %s, expected %+v", connected[0], nodes[1:])
   272  		}
   273  		return nil
   274  	}, time.Second, 100*time.Millisecond))
   275  }
   276  
   277  func setupTestConnectionAfterExpiry(t *testing.T, server *fakePeerEvents, whisperMock *fakeEnvelopeEvents, target, maxFailures int, hash types.Hash) (*ConnectionManager, types.EnodeID) {
   278  	connmanager := NewConnectionManager(server, whisperMock, target, maxFailures, 0)
   279  	connmanager.Start()
   280  	nodes := []*enode.Node{}
   281  	for _, n := range getMapWithRandomNodes(t, 2) {
   282  		nodes = append(nodes, n)
   283  	}
   284  	// Send two random nodes to connection manager.
   285  	connmanager.Notify(nodes)
   286  	var initial types.EnodeID
   287  	// Wait until connection manager establishes connection with one node.
   288  	require.NoError(t, utils.Eventually(func() error {
   289  		nodes := server.Nodes()
   290  		if len(nodes) != target {
   291  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   292  		}
   293  		initial = nodes[0]
   294  		return nil
   295  	}, time.Second, 100*time.Millisecond))
   296  	// Send event that history request for connected peer was sent.
   297  	select {
   298  	case whisperMock.input <- types.EnvelopeEvent{
   299  		Event: types.EventMailServerRequestSent, Peer: initial, Hash: hash}:
   300  	case <-time.After(time.Second):
   301  		require.FailNow(t, "can't send a 'sent' event")
   302  	}
   303  	return connmanager, initial
   304  }
   305  
   306  func TestConnectionChangedAfterExpiry(t *testing.T) {
   307  	server := newFakeServer()
   308  	whisperMock := newFakeEnvelopesEvents()
   309  	target := 1
   310  	maxFailures := 1
   311  	hash := types.Hash{1}
   312  	connmanager, initial := setupTestConnectionAfterExpiry(t, server, whisperMock, target, maxFailures, hash)
   313  	defer connmanager.Stop()
   314  
   315  	// And eventually expired.
   316  	select {
   317  	case whisperMock.input <- types.EnvelopeEvent{
   318  		Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}:
   319  	case <-time.After(time.Second):
   320  		require.FailNow(t, "can't send an 'expiry' event")
   321  	}
   322  	require.NoError(t, utils.Eventually(func() error {
   323  		nodes := server.Nodes()
   324  		if len(nodes) != target {
   325  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   326  		}
   327  		if nodes[0] == initial {
   328  			return fmt.Errorf("connected node wasn't changed from %s", initial)
   329  		}
   330  		return nil
   331  	}, time.Second, 100*time.Millisecond))
   332  }
   333  
   334  func TestConnectionChangedAfterSecondExpiry(t *testing.T) {
   335  	server := newFakeServer()
   336  	whisperMock := newFakeEnvelopesEvents()
   337  	target := 1
   338  	maxFailures := 2
   339  	hash := types.Hash{1}
   340  	connmanager, initial := setupTestConnectionAfterExpiry(t, server, whisperMock, target, maxFailures, hash)
   341  	defer connmanager.Stop()
   342  
   343  	// First expired is sent. Nothing should happen.
   344  	select {
   345  	case whisperMock.input <- types.EnvelopeEvent{
   346  		Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}:
   347  	case <-time.After(time.Second):
   348  		require.FailNow(t, "can't send an 'expiry' event")
   349  	}
   350  
   351  	// we use 'eventually' as 'consistently' because this function will retry for a given timeout while error is received
   352  	require.EqualError(t, utils.Eventually(func() error {
   353  		nodes := server.Nodes()
   354  		if len(nodes) != target {
   355  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   356  		}
   357  		if nodes[0] == initial {
   358  			return fmt.Errorf("connected node wasn't changed from %s", initial)
   359  		}
   360  		return nil
   361  	}, time.Second, 100*time.Millisecond), fmt.Sprintf("connected node wasn't changed from %s", initial))
   362  
   363  	// second expiry event
   364  	select {
   365  	case whisperMock.input <- types.EnvelopeEvent{
   366  		Event: types.EventMailServerRequestExpired, Peer: initial, Hash: hash}:
   367  	case <-time.After(time.Second):
   368  		require.FailNow(t, "can't send an 'expiry' event")
   369  	}
   370  	require.NoError(t, utils.Eventually(func() error {
   371  		nodes := server.Nodes()
   372  		if len(nodes) != target {
   373  			return fmt.Errorf("unexpected number of connected servers: %d", len(nodes))
   374  		}
   375  		if nodes[0] == initial {
   376  			return fmt.Errorf("connected node wasn't changed from %s", initial)
   377  		}
   378  		return nil
   379  	}, time.Second, 100*time.Millisecond))
   380  }
   381  
   382  func TestProcessReplacementWaitsForConnections(t *testing.T) {
   383  	srv := newFakePeerAdderRemover()
   384  	target := 1
   385  	timeout := time.Second
   386  	nodes := make([]*enode.Node, 2)
   387  	fillWithRandomNodes(t, nodes)
   388  	events := make(chan *p2p.PeerEvent)
   389  	state := newInternalState(srv, target, timeout)
   390  	state.currentNodes = nodesToMap(nodes)
   391  	go func() {
   392  		select {
   393  		case events <- &p2p.PeerEvent{Peer: nodes[0].ID(), Type: p2p.PeerEventTypeAdd}:
   394  		case <-time.After(time.Second):
   395  			assert.FailNow(t, "can't send a drop event")
   396  		}
   397  	}()
   398  	state.processReplacement(nodes, events)
   399  	require.Len(t, state.connected, 1)
   400  }