github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/swarm/network/fetcher_test.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package network
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/ShyftNetwork/go-empyrean/p2p/enode"
    26  )
    27  
    28  var requestedPeerID = enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
    29  var sourcePeerID = enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9")
    30  
    31  // mockRequester pushes every request to the requestC channel when its doRequest function is called
    32  type mockRequester struct {
    33  	// requests []Request
    34  	requestC  chan *Request   // when a request is coming it is pushed to requestC
    35  	waitTimes []time.Duration // with waitTimes[i] you can define how much to wait on the ith request (optional)
    36  	count     int             //counts the number of requests
    37  	quitC     chan struct{}
    38  }
    39  
    40  func newMockRequester(waitTimes ...time.Duration) *mockRequester {
    41  	return &mockRequester{
    42  		requestC:  make(chan *Request),
    43  		waitTimes: waitTimes,
    44  		quitC:     make(chan struct{}),
    45  	}
    46  }
    47  
    48  func (m *mockRequester) doRequest(ctx context.Context, request *Request) (*enode.ID, chan struct{}, error) {
    49  	waitTime := time.Duration(0)
    50  	if m.count < len(m.waitTimes) {
    51  		waitTime = m.waitTimes[m.count]
    52  		m.count++
    53  	}
    54  	time.Sleep(waitTime)
    55  	m.requestC <- request
    56  
    57  	// if there is a Source in the request use that, if not use the global requestedPeerId
    58  	source := request.Source
    59  	if source == nil {
    60  		source = &requestedPeerID
    61  	}
    62  	return source, m.quitC, nil
    63  }
    64  
    65  // TestFetcherSingleRequest creates a Fetcher using mockRequester, and run it with a sample set of peers to skip.
    66  // mockRequester pushes a Request on a channel every time the request function is called. Using
    67  // this channel we test if calling Fetcher.Request calls the request function, and whether it uses
    68  // the correct peers to skip which we provided for the fetcher.run function.
    69  func TestFetcherSingleRequest(t *testing.T) {
    70  	requester := newMockRequester()
    71  	addr := make([]byte, 32)
    72  	fetcher := NewFetcher(addr, requester.doRequest, true)
    73  
    74  	peers := []string{"a", "b", "c", "d"}
    75  	peersToSkip := &sync.Map{}
    76  	for _, p := range peers {
    77  		peersToSkip.Store(p, time.Now())
    78  	}
    79  
    80  	ctx, cancel := context.WithCancel(context.Background())
    81  	defer cancel()
    82  
    83  	go fetcher.run(ctx, peersToSkip)
    84  
    85  	rctx := context.Background()
    86  	fetcher.Request(rctx, 0)
    87  
    88  	select {
    89  	case request := <-requester.requestC:
    90  		// request should contain all peers from peersToSkip provided to the fetcher
    91  		for _, p := range peers {
    92  			if _, ok := request.peersToSkip.Load(p); !ok {
    93  				t.Fatalf("request.peersToSkip misses peer")
    94  			}
    95  		}
    96  
    97  		// source peer should be also added to peersToSkip eventually
    98  		time.Sleep(100 * time.Millisecond)
    99  		if _, ok := request.peersToSkip.Load(requestedPeerID.String()); !ok {
   100  			t.Fatalf("request.peersToSkip does not contain peer returned by the request function")
   101  		}
   102  
   103  		// hopCount in the forwarded request should be incremented
   104  		if request.HopCount != 1 {
   105  			t.Fatalf("Expected request.HopCount 1 got %v", request.HopCount)
   106  		}
   107  
   108  		// fetch should trigger a request, if it doesn't happen in time, test should fail
   109  	case <-time.After(200 * time.Millisecond):
   110  		t.Fatalf("fetch timeout")
   111  	}
   112  }
   113  
   114  // TestCancelStopsFetcher tests that a cancelled fetcher does not initiate further requests even if its fetch function is called
   115  func TestFetcherCancelStopsFetcher(t *testing.T) {
   116  	requester := newMockRequester()
   117  	addr := make([]byte, 32)
   118  	fetcher := NewFetcher(addr, requester.doRequest, true)
   119  
   120  	peersToSkip := &sync.Map{}
   121  
   122  	ctx, cancel := context.WithCancel(context.Background())
   123  
   124  	// we start the fetcher, and then we immediately cancel the context
   125  	go fetcher.run(ctx, peersToSkip)
   126  	cancel()
   127  
   128  	rctx, rcancel := context.WithTimeout(ctx, 100*time.Millisecond)
   129  	defer rcancel()
   130  	// we call Request with an active context
   131  	fetcher.Request(rctx, 0)
   132  
   133  	// fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening
   134  	select {
   135  	case <-requester.requestC:
   136  		t.Fatalf("cancelled fetcher initiated request")
   137  	case <-time.After(200 * time.Millisecond):
   138  	}
   139  }
   140  
   141  // TestFetchCancelStopsRequest tests that calling a Request function with a cancelled context does not initiate a request
   142  func TestFetcherCancelStopsRequest(t *testing.T) {
   143  	requester := newMockRequester(100 * time.Millisecond)
   144  	addr := make([]byte, 32)
   145  	fetcher := NewFetcher(addr, requester.doRequest, true)
   146  
   147  	peersToSkip := &sync.Map{}
   148  
   149  	ctx, cancel := context.WithCancel(context.Background())
   150  	defer cancel()
   151  
   152  	// we start the fetcher with an active context
   153  	go fetcher.run(ctx, peersToSkip)
   154  
   155  	rctx, rcancel := context.WithCancel(context.Background())
   156  	rcancel()
   157  
   158  	// we call Request with a cancelled context
   159  	fetcher.Request(rctx, 0)
   160  
   161  	// fetcher should not initiate request, we can only check by waiting a bit and making sure no request is happening
   162  	select {
   163  	case <-requester.requestC:
   164  		t.Fatalf("cancelled fetch function initiated request")
   165  	case <-time.After(200 * time.Millisecond):
   166  	}
   167  
   168  	// if there is another Request with active context, there should be a request, because the fetcher itself is not cancelled
   169  	rctx = context.Background()
   170  	fetcher.Request(rctx, 0)
   171  
   172  	select {
   173  	case <-requester.requestC:
   174  	case <-time.After(200 * time.Millisecond):
   175  		t.Fatalf("expected request")
   176  	}
   177  }
   178  
   179  // TestOfferUsesSource tests Fetcher Offer behavior.
   180  // In this case there should be 1 (and only one) request initiated from the source peer, and the
   181  // source nodeid should appear in the peersToSkip map.
   182  func TestFetcherOfferUsesSource(t *testing.T) {
   183  	requester := newMockRequester(100 * time.Millisecond)
   184  	addr := make([]byte, 32)
   185  	fetcher := NewFetcher(addr, requester.doRequest, true)
   186  
   187  	peersToSkip := &sync.Map{}
   188  
   189  	ctx, cancel := context.WithCancel(context.Background())
   190  	defer cancel()
   191  
   192  	// start the fetcher
   193  	go fetcher.run(ctx, peersToSkip)
   194  
   195  	rctx := context.Background()
   196  	// call the Offer function with the source peer
   197  	fetcher.Offer(rctx, &sourcePeerID)
   198  
   199  	// fetcher should not initiate request
   200  	select {
   201  	case <-requester.requestC:
   202  		t.Fatalf("fetcher initiated request")
   203  	case <-time.After(200 * time.Millisecond):
   204  	}
   205  
   206  	// call Request after the Offer
   207  	rctx = context.Background()
   208  	fetcher.Request(rctx, 0)
   209  
   210  	// there should be exactly 1 request coming from fetcher
   211  	var request *Request
   212  	select {
   213  	case request = <-requester.requestC:
   214  		if *request.Source != sourcePeerID {
   215  			t.Fatalf("Expected source id %v got %v", sourcePeerID, request.Source)
   216  		}
   217  	case <-time.After(200 * time.Millisecond):
   218  		t.Fatalf("fetcher did not initiate request")
   219  	}
   220  
   221  	select {
   222  	case <-requester.requestC:
   223  		t.Fatalf("Fetcher number of requests expected 1 got 2")
   224  	case <-time.After(200 * time.Millisecond):
   225  	}
   226  
   227  	// source peer should be added to peersToSkip eventually
   228  	time.Sleep(100 * time.Millisecond)
   229  	if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok {
   230  		t.Fatalf("SourcePeerId not added to peersToSkip")
   231  	}
   232  }
   233  
   234  func TestFetcherOfferAfterRequestUsesSourceFromContext(t *testing.T) {
   235  	requester := newMockRequester(100 * time.Millisecond)
   236  	addr := make([]byte, 32)
   237  	fetcher := NewFetcher(addr, requester.doRequest, true)
   238  
   239  	peersToSkip := &sync.Map{}
   240  
   241  	ctx, cancel := context.WithCancel(context.Background())
   242  	defer cancel()
   243  
   244  	// start the fetcher
   245  	go fetcher.run(ctx, peersToSkip)
   246  
   247  	// call Request first
   248  	rctx := context.Background()
   249  	fetcher.Request(rctx, 0)
   250  
   251  	// there should be a request coming from fetcher
   252  	var request *Request
   253  	select {
   254  	case request = <-requester.requestC:
   255  		if request.Source != nil {
   256  			t.Fatalf("Incorrect source peer id, expected nil got %v", request.Source)
   257  		}
   258  	case <-time.After(200 * time.Millisecond):
   259  		t.Fatalf("fetcher did not initiate request")
   260  	}
   261  
   262  	// after the Request call Offer
   263  	fetcher.Offer(context.Background(), &sourcePeerID)
   264  
   265  	// there should be a request coming from fetcher
   266  	select {
   267  	case request = <-requester.requestC:
   268  		if *request.Source != sourcePeerID {
   269  			t.Fatalf("Incorrect source peer id, expected %v got %v", sourcePeerID, request.Source)
   270  		}
   271  	case <-time.After(200 * time.Millisecond):
   272  		t.Fatalf("fetcher did not initiate request")
   273  	}
   274  
   275  	// source peer should be added to peersToSkip eventually
   276  	time.Sleep(100 * time.Millisecond)
   277  	if _, ok := request.peersToSkip.Load(sourcePeerID.String()); !ok {
   278  		t.Fatalf("SourcePeerId not added to peersToSkip")
   279  	}
   280  }
   281  
   282  // TestFetcherRetryOnTimeout tests that fetch retries after searchTimeOut has passed
   283  func TestFetcherRetryOnTimeout(t *testing.T) {
   284  	requester := newMockRequester()
   285  	addr := make([]byte, 32)
   286  	fetcher := NewFetcher(addr, requester.doRequest, true)
   287  
   288  	peersToSkip := &sync.Map{}
   289  
   290  	// set searchTimeOut to low value so the test is quicker
   291  	defer func(t time.Duration) {
   292  		searchTimeout = t
   293  	}(searchTimeout)
   294  	searchTimeout = 250 * time.Millisecond
   295  
   296  	ctx, cancel := context.WithCancel(context.Background())
   297  	defer cancel()
   298  
   299  	// start the fetcher
   300  	go fetcher.run(ctx, peersToSkip)
   301  
   302  	// call the fetch function with an active context
   303  	rctx := context.Background()
   304  	fetcher.Request(rctx, 0)
   305  
   306  	// after 100ms the first request should be initiated
   307  	time.Sleep(100 * time.Millisecond)
   308  
   309  	select {
   310  	case <-requester.requestC:
   311  	default:
   312  		t.Fatalf("fetch did not initiate request")
   313  	}
   314  
   315  	// after another 100ms no new request should be initiated, because search timeout is 250ms
   316  	time.Sleep(100 * time.Millisecond)
   317  
   318  	select {
   319  	case <-requester.requestC:
   320  		t.Fatalf("unexpected request from fetcher")
   321  	default:
   322  	}
   323  
   324  	// after another 300ms search timeout is over, there should be a new request
   325  	time.Sleep(300 * time.Millisecond)
   326  
   327  	select {
   328  	case <-requester.requestC:
   329  	default:
   330  		t.Fatalf("fetch did not retry request")
   331  	}
   332  }
   333  
   334  // TestFetcherFactory creates a FetcherFactory and checks if the factory really creates and starts
   335  // a Fetcher when it return a fetch function. We test the fetching functionality just by checking if
   336  // a request is initiated when the fetch function is called
   337  func TestFetcherFactory(t *testing.T) {
   338  	requester := newMockRequester(100 * time.Millisecond)
   339  	addr := make([]byte, 32)
   340  	fetcherFactory := NewFetcherFactory(requester.doRequest, false)
   341  
   342  	peersToSkip := &sync.Map{}
   343  
   344  	fetcher := fetcherFactory.New(context.Background(), addr, peersToSkip)
   345  
   346  	fetcher.Request(context.Background(), 0)
   347  
   348  	// check if the created fetchFunction really starts a fetcher and initiates a request
   349  	select {
   350  	case <-requester.requestC:
   351  	case <-time.After(200 * time.Millisecond):
   352  		t.Fatalf("fetch timeout")
   353  	}
   354  
   355  }
   356  
   357  func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
   358  	requester := newMockRequester()
   359  	addr := make([]byte, 32)
   360  	fetcher := NewFetcher(addr, requester.doRequest, true)
   361  
   362  	// make sure searchTimeout is long so it is sure the request is not retried because of timeout
   363  	defer func(t time.Duration) {
   364  		searchTimeout = t
   365  	}(searchTimeout)
   366  	searchTimeout = 10 * time.Second
   367  
   368  	peersToSkip := &sync.Map{}
   369  
   370  	ctx, cancel := context.WithCancel(context.Background())
   371  	defer cancel()
   372  
   373  	go fetcher.run(ctx, peersToSkip)
   374  
   375  	rctx := context.Background()
   376  	fetcher.Request(rctx, 0)
   377  
   378  	select {
   379  	case <-requester.requestC:
   380  	case <-time.After(200 * time.Millisecond):
   381  		t.Fatalf("request is not initiated")
   382  	}
   383  
   384  	close(requester.quitC)
   385  
   386  	select {
   387  	case <-requester.requestC:
   388  	case <-time.After(200 * time.Millisecond):
   389  		t.Fatalf("request is not initiated after failed request")
   390  	}
   391  }
   392  
   393  // TestRequestSkipPeer checks if PeerSkip function will skip provided peer
   394  // and not skip unknown one.
   395  func TestRequestSkipPeer(t *testing.T) {
   396  	addr := make([]byte, 32)
   397  	peers := []enode.ID{
   398  		enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"),
   399  		enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9"),
   400  	}
   401  
   402  	peersToSkip := new(sync.Map)
   403  	peersToSkip.Store(peers[0].String(), time.Now())
   404  	r := NewRequest(addr, false, peersToSkip)
   405  
   406  	if !r.SkipPeer(peers[0].String()) {
   407  		t.Errorf("peer not skipped")
   408  	}
   409  
   410  	if r.SkipPeer(peers[1].String()) {
   411  		t.Errorf("peer skipped")
   412  	}
   413  }
   414  
   415  // TestRequestSkipPeerExpired checks if a peer to skip is not skipped
   416  // after RequestTimeout has passed.
   417  func TestRequestSkipPeerExpired(t *testing.T) {
   418  	addr := make([]byte, 32)
   419  	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
   420  
   421  	// set RequestTimeout to a low value and reset it after the test
   422  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   423  	RequestTimeout = 250 * time.Millisecond
   424  
   425  	peersToSkip := new(sync.Map)
   426  	peersToSkip.Store(peer.String(), time.Now())
   427  	r := NewRequest(addr, false, peersToSkip)
   428  
   429  	if !r.SkipPeer(peer.String()) {
   430  		t.Errorf("peer not skipped")
   431  	}
   432  
   433  	time.Sleep(500 * time.Millisecond)
   434  
   435  	if r.SkipPeer(peer.String()) {
   436  		t.Errorf("peer skipped")
   437  	}
   438  }
   439  
   440  // TestRequestSkipPeerPermanent checks if a peer to skip is not skipped
   441  // after RequestTimeout is not skipped if it is set for a permanent skipping
   442  // by value to peersToSkip map is not time.Duration.
   443  func TestRequestSkipPeerPermanent(t *testing.T) {
   444  	addr := make([]byte, 32)
   445  	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
   446  
   447  	// set RequestTimeout to a low value and reset it after the test
   448  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   449  	RequestTimeout = 250 * time.Millisecond
   450  
   451  	peersToSkip := new(sync.Map)
   452  	peersToSkip.Store(peer.String(), true)
   453  	r := NewRequest(addr, false, peersToSkip)
   454  
   455  	if !r.SkipPeer(peer.String()) {
   456  		t.Errorf("peer not skipped")
   457  	}
   458  
   459  	time.Sleep(500 * time.Millisecond)
   460  
   461  	if !r.SkipPeer(peer.String()) {
   462  		t.Errorf("peer not skipped")
   463  	}
   464  }
   465  
   466  func TestFetcherMaxHopCount(t *testing.T) {
   467  	requester := newMockRequester()
   468  	addr := make([]byte, 32)
   469  	fetcher := NewFetcher(addr, requester.doRequest, true)
   470  
   471  	ctx, cancel := context.WithCancel(context.Background())
   472  	defer cancel()
   473  
   474  	peersToSkip := &sync.Map{}
   475  
   476  	go fetcher.run(ctx, peersToSkip)
   477  
   478  	rctx := context.Background()
   479  	fetcher.Request(rctx, maxHopCount)
   480  
   481  	// if hopCount is already at max no request should be initiated
   482  	select {
   483  	case <-requester.requestC:
   484  		t.Fatalf("cancelled fetcher initiated request")
   485  	case <-time.After(200 * time.Millisecond):
   486  	}
   487  }