github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/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/ethereum/go-ethereum/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  	// set searchTimeOut to low value so the test is quicker
   288  	fetcher.searchTimeout = 250 * time.Millisecond
   289  
   290  	peersToSkip := &sync.Map{}
   291  
   292  	ctx, cancel := context.WithCancel(context.Background())
   293  	defer cancel()
   294  
   295  	// start the fetcher
   296  	go fetcher.run(ctx, peersToSkip)
   297  
   298  	// call the fetch function with an active context
   299  	rctx := context.Background()
   300  	fetcher.Request(rctx, 0)
   301  
   302  	// after 100ms the first request should be initiated
   303  	time.Sleep(100 * time.Millisecond)
   304  
   305  	select {
   306  	case <-requester.requestC:
   307  	default:
   308  		t.Fatalf("fetch did not initiate request")
   309  	}
   310  
   311  	// after another 100ms no new request should be initiated, because search timeout is 250ms
   312  	time.Sleep(100 * time.Millisecond)
   313  
   314  	select {
   315  	case <-requester.requestC:
   316  		t.Fatalf("unexpected request from fetcher")
   317  	default:
   318  	}
   319  
   320  	// after another 300ms search timeout is over, there should be a new request
   321  	time.Sleep(300 * time.Millisecond)
   322  
   323  	select {
   324  	case <-requester.requestC:
   325  	default:
   326  		t.Fatalf("fetch did not retry request")
   327  	}
   328  }
   329  
   330  // TestFetcherFactory creates a FetcherFactory and checks if the factory really creates and starts
   331  // a Fetcher when it return a fetch function. We test the fetching functionality just by checking if
   332  // a request is initiated when the fetch function is called
   333  func TestFetcherFactory(t *testing.T) {
   334  	requester := newMockRequester(100 * time.Millisecond)
   335  	addr := make([]byte, 32)
   336  	fetcherFactory := NewFetcherFactory(requester.doRequest, false)
   337  
   338  	peersToSkip := &sync.Map{}
   339  
   340  	fetcher := fetcherFactory.New(context.Background(), addr, peersToSkip)
   341  
   342  	fetcher.Request(context.Background(), 0)
   343  
   344  	// check if the created fetchFunction really starts a fetcher and initiates a request
   345  	select {
   346  	case <-requester.requestC:
   347  	case <-time.After(200 * time.Millisecond):
   348  		t.Fatalf("fetch timeout")
   349  	}
   350  
   351  }
   352  
   353  func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
   354  	requester := newMockRequester()
   355  	addr := make([]byte, 32)
   356  	fetcher := NewFetcher(addr, requester.doRequest, true)
   357  
   358  	// make sure the searchTimeout is long so it is sure the request is not
   359  	// retried because of timeout
   360  	fetcher.searchTimeout = 10 * time.Second
   361  
   362  	peersToSkip := &sync.Map{}
   363  
   364  	ctx, cancel := context.WithCancel(context.Background())
   365  	defer cancel()
   366  
   367  	go fetcher.run(ctx, peersToSkip)
   368  
   369  	rctx := context.Background()
   370  	fetcher.Request(rctx, 0)
   371  
   372  	select {
   373  	case <-requester.requestC:
   374  	case <-time.After(200 * time.Millisecond):
   375  		t.Fatalf("request is not initiated")
   376  	}
   377  
   378  	close(requester.quitC)
   379  
   380  	select {
   381  	case <-requester.requestC:
   382  	case <-time.After(200 * time.Millisecond):
   383  		t.Fatalf("request is not initiated after failed request")
   384  	}
   385  }
   386  
   387  // TestRequestSkipPeer checks if PeerSkip function will skip provided peer
   388  // and not skip unknown one.
   389  func TestRequestSkipPeer(t *testing.T) {
   390  	addr := make([]byte, 32)
   391  	peers := []enode.ID{
   392  		enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8"),
   393  		enode.HexID("99d8594b52298567d2ca3f4c441a5ba0140ee9245e26460d01102a52773c73b9"),
   394  	}
   395  
   396  	peersToSkip := new(sync.Map)
   397  	peersToSkip.Store(peers[0].String(), time.Now())
   398  	r := NewRequest(addr, false, peersToSkip)
   399  
   400  	if !r.SkipPeer(peers[0].String()) {
   401  		t.Errorf("peer not skipped")
   402  	}
   403  
   404  	if r.SkipPeer(peers[1].String()) {
   405  		t.Errorf("peer skipped")
   406  	}
   407  }
   408  
   409  // TestRequestSkipPeerExpired checks if a peer to skip is not skipped
   410  // after RequestTimeout has passed.
   411  func TestRequestSkipPeerExpired(t *testing.T) {
   412  	addr := make([]byte, 32)
   413  	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
   414  
   415  	// set RequestTimeout to a low value and reset it after the test
   416  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   417  	RequestTimeout = 250 * time.Millisecond
   418  
   419  	peersToSkip := new(sync.Map)
   420  	peersToSkip.Store(peer.String(), time.Now())
   421  	r := NewRequest(addr, false, peersToSkip)
   422  
   423  	if !r.SkipPeer(peer.String()) {
   424  		t.Errorf("peer not skipped")
   425  	}
   426  
   427  	time.Sleep(500 * time.Millisecond)
   428  
   429  	if r.SkipPeer(peer.String()) {
   430  		t.Errorf("peer skipped")
   431  	}
   432  }
   433  
   434  // TestRequestSkipPeerPermanent checks if a peer to skip is not skipped
   435  // after RequestTimeout is not skipped if it is set for a permanent skipping
   436  // by value to peersToSkip map is not time.Duration.
   437  func TestRequestSkipPeerPermanent(t *testing.T) {
   438  	addr := make([]byte, 32)
   439  	peer := enode.HexID("3431c3939e1ee2a6345e976a8234f9870152d64879f30bc272a074f6859e75e8")
   440  
   441  	// set RequestTimeout to a low value and reset it after the test
   442  	defer func(t time.Duration) { RequestTimeout = t }(RequestTimeout)
   443  	RequestTimeout = 250 * time.Millisecond
   444  
   445  	peersToSkip := new(sync.Map)
   446  	peersToSkip.Store(peer.String(), true)
   447  	r := NewRequest(addr, false, peersToSkip)
   448  
   449  	if !r.SkipPeer(peer.String()) {
   450  		t.Errorf("peer not skipped")
   451  	}
   452  
   453  	time.Sleep(500 * time.Millisecond)
   454  
   455  	if !r.SkipPeer(peer.String()) {
   456  		t.Errorf("peer not skipped")
   457  	}
   458  }
   459  
   460  func TestFetcherMaxHopCount(t *testing.T) {
   461  	requester := newMockRequester()
   462  	addr := make([]byte, 32)
   463  	fetcher := NewFetcher(addr, requester.doRequest, true)
   464  
   465  	ctx, cancel := context.WithCancel(context.Background())
   466  	defer cancel()
   467  
   468  	peersToSkip := &sync.Map{}
   469  
   470  	go fetcher.run(ctx, peersToSkip)
   471  
   472  	rctx := context.Background()
   473  	fetcher.Request(rctx, maxHopCount)
   474  
   475  	// if hopCount is already at max no request should be initiated
   476  	select {
   477  	case <-requester.requestC:
   478  		t.Fatalf("cancelled fetcher initiated request")
   479  	case <-time.After(200 * time.Millisecond):
   480  	}
   481  }