github.com/energicryptocurrency/go-energi@v1.1.7/les/freeclient_test.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2018 The go-ethereum Authors
     3  // This file is part of the Energi Core library.
     4  //
     5  // The Energi Core library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The Energi Core library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  // Package light implements on-demand retrieval capable state and chain objects
    19  // for the Ethereum Light Client.
    20  package les
    21  
    22  import (
    23  	"fmt"
    24  	"math/rand"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/energicryptocurrency/go-energi/common/mclock"
    29  	"github.com/energicryptocurrency/go-energi/ethdb"
    30  )
    31  
    32  func TestFreeClientPoolL10C100(t *testing.T) {
    33  	testFreeClientPool(t, 10, 100)
    34  }
    35  
    36  func TestFreeClientPoolL40C200(t *testing.T) {
    37  	testFreeClientPool(t, 40, 200)
    38  }
    39  
    40  func TestFreeClientPoolL100C300(t *testing.T) {
    41  	testFreeClientPool(t, 100, 300)
    42  }
    43  
    44  const testFreeClientPoolTicks = 500000
    45  
    46  func testFreeClientPool(t *testing.T, connLimit, clientCount int) {
    47  	var (
    48  		clock     mclock.Simulated
    49  		db        = ethdb.NewMemDatabase()
    50  		pool      = newFreeClientPool(db, connLimit, 10000, &clock)
    51  		connected = make([]bool, clientCount)
    52  		connTicks = make([]int, clientCount)
    53  		disconnCh = make(chan int, clientCount)
    54  	)
    55  	peerId := func(i int) string {
    56  		return fmt.Sprintf("test peer #%d", i)
    57  	}
    58  	disconnFn := func(i int) func() {
    59  		return func() {
    60  			disconnCh <- i
    61  		}
    62  	}
    63  
    64  	// pool should accept new peers up to its connected limit
    65  	for i := 0; i < connLimit; i++ {
    66  		if pool.connect(peerId(i), disconnFn(i)) {
    67  			connected[i] = true
    68  		} else {
    69  			t.Fatalf("Test peer #%d rejected", i)
    70  		}
    71  	}
    72  	// since all accepted peers are new and should not be kicked out, the next one should be rejected
    73  	if pool.connect(peerId(connLimit), disconnFn(connLimit)) {
    74  		connected[connLimit] = true
    75  		t.Fatalf("Peer accepted over connected limit")
    76  	}
    77  
    78  	// randomly connect and disconnect peers, expect to have a similar total connection time at the end
    79  	for tickCounter := 0; tickCounter < testFreeClientPoolTicks; tickCounter++ {
    80  		clock.Run(1 * time.Second)
    81  
    82  		i := rand.Intn(clientCount)
    83  		if connected[i] {
    84  			pool.disconnect(peerId(i))
    85  			connected[i] = false
    86  			connTicks[i] += tickCounter
    87  		} else {
    88  			if pool.connect(peerId(i), disconnFn(i)) {
    89  				connected[i] = true
    90  				connTicks[i] -= tickCounter
    91  			}
    92  		}
    93  	pollDisconnects:
    94  		for {
    95  			select {
    96  			case i := <-disconnCh:
    97  				pool.disconnect(peerId(i))
    98  				if connected[i] {
    99  					connTicks[i] += tickCounter
   100  					connected[i] = false
   101  				}
   102  			default:
   103  				break pollDisconnects
   104  			}
   105  		}
   106  	}
   107  
   108  	expTicks := testFreeClientPoolTicks * connLimit / clientCount
   109  	expMin := expTicks - expTicks/10
   110  	expMax := expTicks + expTicks/10
   111  
   112  	// check if the total connected time of peers are all in the expected range
   113  	for i, c := range connected {
   114  		if c {
   115  			connTicks[i] += testFreeClientPoolTicks
   116  		}
   117  		if connTicks[i] < expMin || connTicks[i] > expMax {
   118  			t.Errorf("Total connected time of test node #%d (%d) outside expected range (%d to %d)", i, connTicks[i], expMin, expMax)
   119  		}
   120  	}
   121  
   122  	// a previously unknown peer should be accepted now
   123  	if !pool.connect("newPeer", func() {}) {
   124  		t.Fatalf("Previously unknown peer rejected")
   125  	}
   126  
   127  	// close and restart pool
   128  	pool.stop()
   129  	pool = newFreeClientPool(db, connLimit, 10000, &clock)
   130  
   131  	// try connecting all known peers (connLimit should be filled up)
   132  	for i := 0; i < clientCount; i++ {
   133  		pool.connect(peerId(i), func() {})
   134  	}
   135  	// expect pool to remember known nodes and kick out one of them to accept a new one
   136  	if !pool.connect("newPeer2", func() {}) {
   137  		t.Errorf("Previously unknown peer rejected after restarting pool")
   138  	}
   139  	pool.stop()
   140  }