github.com/okex/exchain@v1.8.0/libs/tendermint/p2p/pex/addrbook_test.go (about)

     1  package pex
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"math"
     8  	"net"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/okex/exchain/libs/tendermint/libs/log"
    17  	tmmath "github.com/okex/exchain/libs/tendermint/libs/math"
    18  	tmrand "github.com/okex/exchain/libs/tendermint/libs/rand"
    19  	"github.com/okex/exchain/libs/tendermint/p2p"
    20  )
    21  
    22  // FIXME These tests should not rely on .(*addrBook) assertions
    23  
    24  func TestAddrBookPickAddress(t *testing.T) {
    25  	fname := createTempFileName("addrbook_test")
    26  	defer deleteTempFile(fname)
    27  
    28  	// 0 addresses
    29  	book := NewAddrBook(fname, true)
    30  	book.SetLogger(log.TestingLogger())
    31  	assert.Zero(t, book.Size())
    32  
    33  	addr := book.PickAddress(50)
    34  	assert.Nil(t, addr, "expected no address")
    35  
    36  	randAddrs := randNetAddressPairs(t, 1)
    37  	addrSrc := randAddrs[0]
    38  	book.AddAddress(addrSrc.addr, addrSrc.src)
    39  
    40  	// pick an address when we only have new address
    41  	addr = book.PickAddress(0)
    42  	assert.NotNil(t, addr, "expected an address")
    43  	addr = book.PickAddress(50)
    44  	assert.NotNil(t, addr, "expected an address")
    45  	addr = book.PickAddress(100)
    46  	assert.NotNil(t, addr, "expected an address")
    47  
    48  	// pick an address when we only have old address
    49  	book.MarkGood(addrSrc.addr.ID)
    50  	addr = book.PickAddress(0)
    51  	assert.NotNil(t, addr, "expected an address")
    52  	addr = book.PickAddress(50)
    53  	assert.NotNil(t, addr, "expected an address")
    54  
    55  	// in this case, nNew==0 but we biased 100% to new, so we return nil
    56  	addr = book.PickAddress(100)
    57  	assert.Nil(t, addr, "did not expected an address")
    58  }
    59  
    60  func TestAddrBookSaveLoad(t *testing.T) {
    61  	fname := createTempFileName("addrbook_test")
    62  	defer deleteTempFile(fname)
    63  
    64  	// 0 addresses
    65  	book := NewAddrBook(fname, true)
    66  	book.SetLogger(log.TestingLogger())
    67  	book.Save()
    68  
    69  	book = NewAddrBook(fname, true)
    70  	book.SetLogger(log.TestingLogger())
    71  	book.Start()
    72  
    73  	assert.True(t, book.Empty())
    74  
    75  	// 100 addresses
    76  	randAddrs := randNetAddressPairs(t, 100)
    77  
    78  	for _, addrSrc := range randAddrs {
    79  		book.AddAddress(addrSrc.addr, addrSrc.src)
    80  	}
    81  
    82  	assert.Equal(t, 100, book.Size())
    83  	book.Save()
    84  
    85  	book = NewAddrBook(fname, true)
    86  	book.SetLogger(log.TestingLogger())
    87  	book.Start()
    88  
    89  	assert.Equal(t, 100, book.Size())
    90  }
    91  
    92  func TestAddrBookLookup(t *testing.T) {
    93  	fname := createTempFileName("addrbook_test")
    94  	defer deleteTempFile(fname)
    95  
    96  	randAddrs := randNetAddressPairs(t, 100)
    97  
    98  	book := NewAddrBook(fname, true)
    99  	book.SetLogger(log.TestingLogger())
   100  	for _, addrSrc := range randAddrs {
   101  		addr := addrSrc.addr
   102  		src := addrSrc.src
   103  		book.AddAddress(addr, src)
   104  
   105  		ka := book.HasAddress(addr)
   106  		assert.True(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
   107  	}
   108  }
   109  
   110  func TestAddrBookPromoteToOld(t *testing.T) {
   111  	fname := createTempFileName("addrbook_test")
   112  	defer deleteTempFile(fname)
   113  
   114  	randAddrs := randNetAddressPairs(t, 100)
   115  
   116  	book := NewAddrBook(fname, true)
   117  	book.SetLogger(log.TestingLogger())
   118  	for _, addrSrc := range randAddrs {
   119  		book.AddAddress(addrSrc.addr, addrSrc.src)
   120  	}
   121  
   122  	// Attempt all addresses.
   123  	for _, addrSrc := range randAddrs {
   124  		book.MarkAttempt(addrSrc.addr)
   125  	}
   126  
   127  	// Promote half of them
   128  	for i, addrSrc := range randAddrs {
   129  		if i%2 == 0 {
   130  			book.MarkGood(addrSrc.addr.ID)
   131  		}
   132  	}
   133  
   134  	// TODO: do more testing :)
   135  
   136  	selection := book.GetSelection()
   137  	t.Logf("selection: %v", selection)
   138  
   139  	if len(selection) > book.Size() {
   140  		t.Errorf("selection could not be bigger than the book")
   141  	}
   142  
   143  	selection = book.GetSelectionWithBias(30)
   144  	t.Logf("selection: %v", selection)
   145  
   146  	if len(selection) > book.Size() {
   147  		t.Errorf("selection with bias could not be bigger than the book")
   148  	}
   149  
   150  	assert.Equal(t, book.Size(), 100, "expecting book size to be 100")
   151  }
   152  
   153  func TestAddrBookHandlesDuplicates(t *testing.T) {
   154  	fname := createTempFileName("addrbook_test")
   155  	defer deleteTempFile(fname)
   156  
   157  	book := NewAddrBook(fname, true)
   158  	book.SetLogger(log.TestingLogger())
   159  
   160  	randAddrs := randNetAddressPairs(t, 100)
   161  
   162  	differentSrc := randIPv4Address(t)
   163  	for _, addrSrc := range randAddrs {
   164  		book.AddAddress(addrSrc.addr, addrSrc.src)
   165  		book.AddAddress(addrSrc.addr, addrSrc.src)  // duplicate
   166  		book.AddAddress(addrSrc.addr, differentSrc) // different src
   167  	}
   168  
   169  	assert.Equal(t, 100, book.Size())
   170  }
   171  
   172  type netAddressPair struct {
   173  	addr *p2p.NetAddress
   174  	src  *p2p.NetAddress
   175  }
   176  
   177  func randNetAddressPairs(t *testing.T, n int) []netAddressPair {
   178  	randAddrs := make([]netAddressPair, n)
   179  	for i := 0; i < n; i++ {
   180  		randAddrs[i] = netAddressPair{addr: randIPv4Address(t), src: randIPv4Address(t)}
   181  	}
   182  	return randAddrs
   183  }
   184  
   185  func randIPv4Address(t *testing.T) *p2p.NetAddress {
   186  	for {
   187  		ip := fmt.Sprintf("%v.%v.%v.%v",
   188  			tmrand.Intn(254)+1,
   189  			tmrand.Intn(255),
   190  			tmrand.Intn(255),
   191  			tmrand.Intn(255),
   192  		)
   193  		port := tmrand.Intn(65535-1) + 1
   194  		id := p2p.ID(hex.EncodeToString(tmrand.Bytes(p2p.IDByteLength)))
   195  		idAddr := p2p.IDAddressString(id, fmt.Sprintf("%v:%v", ip, port))
   196  		addr, err := p2p.NewNetAddressString(idAddr)
   197  		assert.Nil(t, err, "error generating rand network address")
   198  		if addr.Routable() {
   199  			return addr
   200  		}
   201  	}
   202  }
   203  
   204  func TestAddrBookRemoveAddress(t *testing.T) {
   205  	fname := createTempFileName("addrbook_test")
   206  	defer deleteTempFile(fname)
   207  
   208  	book := NewAddrBook(fname, true)
   209  	book.SetLogger(log.TestingLogger())
   210  
   211  	addr := randIPv4Address(t)
   212  	book.AddAddress(addr, addr)
   213  	assert.Equal(t, 1, book.Size())
   214  
   215  	book.RemoveAddress(addr)
   216  	assert.Equal(t, 0, book.Size())
   217  
   218  	nonExistingAddr := randIPv4Address(t)
   219  	book.RemoveAddress(nonExistingAddr)
   220  	assert.Equal(t, 0, book.Size())
   221  }
   222  
   223  func TestAddrBookGetSelectionWithOneMarkedGood(t *testing.T) {
   224  	// create a book with 10 addresses, 1 good/old and 9 new
   225  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 1, 9)
   226  	defer deleteTempFile(fname)
   227  
   228  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   229  	assert.NotNil(t, addrs)
   230  	assertMOldAndNNewAddrsInSelection(t, 1, 9, addrs, book)
   231  }
   232  
   233  func TestAddrBookGetSelectionWithOneNotMarkedGood(t *testing.T) {
   234  	// create a book with 10 addresses, 9 good/old and 1 new
   235  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 9, 1)
   236  	defer deleteTempFile(fname)
   237  
   238  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   239  	assert.NotNil(t, addrs)
   240  	assertMOldAndNNewAddrsInSelection(t, 9, 1, addrs, book)
   241  }
   242  
   243  func TestAddrBookGetSelectionReturnsNilWhenAddrBookIsEmpty(t *testing.T) {
   244  	book, fname := createAddrBookWithMOldAndNNewAddrs(t, 0, 0)
   245  	defer deleteTempFile(fname)
   246  
   247  	addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   248  	assert.Nil(t, addrs)
   249  }
   250  
   251  func TestAddrBookGetSelection(t *testing.T) {
   252  	fname := createTempFileName("addrbook_test")
   253  	defer deleteTempFile(fname)
   254  
   255  	book := NewAddrBook(fname, true)
   256  	book.SetLogger(log.TestingLogger())
   257  
   258  	// 1) empty book
   259  	assert.Empty(t, book.GetSelection())
   260  
   261  	// 2) add one address
   262  	addr := randIPv4Address(t)
   263  	book.AddAddress(addr, addr)
   264  
   265  	assert.Equal(t, 1, len(book.GetSelection()))
   266  	assert.Equal(t, addr, book.GetSelection()[0])
   267  
   268  	// 3) add a bunch of addresses
   269  	randAddrs := randNetAddressPairs(t, 100)
   270  	for _, addrSrc := range randAddrs {
   271  		book.AddAddress(addrSrc.addr, addrSrc.src)
   272  	}
   273  
   274  	// check there is no duplicates
   275  	addrs := make(map[string]*p2p.NetAddress)
   276  	selection := book.GetSelection()
   277  	for _, addr := range selection {
   278  		if dup, ok := addrs[addr.String()]; ok {
   279  			t.Fatalf("selection %v contains duplicates %v", selection, dup)
   280  		}
   281  		addrs[addr.String()] = addr
   282  	}
   283  
   284  	if len(selection) > book.Size() {
   285  		t.Errorf("selection %v could not be bigger than the book", selection)
   286  	}
   287  }
   288  
   289  func TestAddrBookGetSelectionWithBias(t *testing.T) {
   290  	const biasTowardsNewAddrs = 30
   291  
   292  	fname := createTempFileName("addrbook_test")
   293  	defer deleteTempFile(fname)
   294  
   295  	book := NewAddrBook(fname, true)
   296  	book.SetLogger(log.TestingLogger())
   297  
   298  	// 1) empty book
   299  	selection := book.GetSelectionWithBias(biasTowardsNewAddrs)
   300  	assert.Empty(t, selection)
   301  
   302  	// 2) add one address
   303  	addr := randIPv4Address(t)
   304  	book.AddAddress(addr, addr)
   305  
   306  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   307  	assert.Equal(t, 1, len(selection))
   308  	assert.Equal(t, addr, selection[0])
   309  
   310  	// 3) add a bunch of addresses
   311  	randAddrs := randNetAddressPairs(t, 100)
   312  	for _, addrSrc := range randAddrs {
   313  		book.AddAddress(addrSrc.addr, addrSrc.src)
   314  	}
   315  
   316  	// check there is no duplicates
   317  	addrs := make(map[string]*p2p.NetAddress)
   318  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   319  	for _, addr := range selection {
   320  		if dup, ok := addrs[addr.String()]; ok {
   321  			t.Fatalf("selection %v contains duplicates %v", selection, dup)
   322  		}
   323  		addrs[addr.String()] = addr
   324  	}
   325  
   326  	if len(selection) > book.Size() {
   327  		t.Fatalf("selection %v could not be bigger than the book", selection)
   328  	}
   329  
   330  	// 4) mark 80% of the addresses as good
   331  	randAddrsLen := len(randAddrs)
   332  	for i, addrSrc := range randAddrs {
   333  		if int((float64(i)/float64(randAddrsLen))*100) >= 20 {
   334  			book.MarkGood(addrSrc.addr.ID)
   335  		}
   336  	}
   337  
   338  	selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
   339  
   340  	// check that ~70% of addresses returned are good
   341  	good := 0
   342  	for _, addr := range selection {
   343  		if book.IsGood(addr) {
   344  			good++
   345  		}
   346  	}
   347  
   348  	got, expected := int((float64(good)/float64(len(selection)))*100), 100-biasTowardsNewAddrs
   349  
   350  	// compute some slack to protect against small differences due to rounding:
   351  	slack := int(math.Round(float64(100) / float64(len(selection))))
   352  	if got > expected+slack {
   353  		t.Fatalf(
   354  			"got more good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)",
   355  			got,
   356  			expected,
   357  			good,
   358  			len(selection),
   359  		)
   360  	}
   361  	if got < expected-slack {
   362  		t.Fatalf(
   363  			"got fewer good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)",
   364  			got,
   365  			expected,
   366  			good,
   367  			len(selection),
   368  		)
   369  	}
   370  }
   371  
   372  func TestAddrBookHasAddress(t *testing.T) {
   373  	fname := createTempFileName("addrbook_test")
   374  	defer deleteTempFile(fname)
   375  
   376  	book := NewAddrBook(fname, true)
   377  	book.SetLogger(log.TestingLogger())
   378  	addr := randIPv4Address(t)
   379  	book.AddAddress(addr, addr)
   380  
   381  	assert.True(t, book.HasAddress(addr))
   382  
   383  	book.RemoveAddress(addr)
   384  
   385  	assert.False(t, book.HasAddress(addr))
   386  }
   387  
   388  func testCreatePrivateAddrs(t *testing.T, numAddrs int) ([]*p2p.NetAddress, []string) {
   389  	addrs := make([]*p2p.NetAddress, numAddrs)
   390  	for i := 0; i < numAddrs; i++ {
   391  		addrs[i] = randIPv4Address(t)
   392  	}
   393  
   394  	private := make([]string, numAddrs)
   395  	for i, addr := range addrs {
   396  		private[i] = string(addr.ID)
   397  	}
   398  	return addrs, private
   399  }
   400  
   401  func TestBanBadPeers(t *testing.T) {
   402  	fname := createTempFileName("addrbook_test")
   403  	defer deleteTempFile(fname)
   404  
   405  	book := NewAddrBook(fname, true)
   406  	book.SetLogger(log.TestingLogger())
   407  
   408  	addr := randIPv4Address(t)
   409  	_ = book.AddAddress(addr, addr)
   410  
   411  	book.MarkBad(addr, 1*time.Second)
   412  	// addr should not reachable
   413  	assert.False(t, book.HasAddress(addr))
   414  	assert.True(t, book.IsBanned(addr))
   415  
   416  	err := book.AddAddress(addr, addr)
   417  	// book should not add address from the blacklist
   418  	assert.Error(t, err)
   419  
   420  	time.Sleep(1 * time.Second)
   421  	book.ReinstateBadPeers()
   422  	// address should be reinstated in the new bucket
   423  	assert.EqualValues(t, 1, book.Size())
   424  	assert.True(t, book.HasAddress(addr))
   425  	assert.False(t, book.IsGood(addr))
   426  }
   427  
   428  func TestAddrBookEmpty(t *testing.T) {
   429  	fname := createTempFileName("addrbook_test")
   430  	defer deleteTempFile(fname)
   431  
   432  	book := NewAddrBook(fname, true)
   433  	book.SetLogger(log.TestingLogger())
   434  	// Check that empty book is empty
   435  	require.True(t, book.Empty())
   436  	// Check that book with our address is empty
   437  	book.AddOurAddress(randIPv4Address(t))
   438  	require.True(t, book.Empty())
   439  	// Check that book with private addrs is empty
   440  	_, privateIds := testCreatePrivateAddrs(t, 5)
   441  	book.AddPrivateIDs(privateIds)
   442  	require.True(t, book.Empty())
   443  
   444  	// Check that book with address is not empty
   445  	book.AddAddress(randIPv4Address(t), randIPv4Address(t))
   446  	require.False(t, book.Empty())
   447  }
   448  
   449  func TestPrivatePeers(t *testing.T) {
   450  	fname := createTempFileName("addrbook_test")
   451  	defer deleteTempFile(fname)
   452  
   453  	book := NewAddrBook(fname, true)
   454  	book.SetLogger(log.TestingLogger())
   455  
   456  	addrs, private := testCreatePrivateAddrs(t, 10)
   457  	book.AddPrivateIDs(private)
   458  
   459  	// private addrs must not be added
   460  	for _, addr := range addrs {
   461  		err := book.AddAddress(addr, addr)
   462  		if assert.Error(t, err) {
   463  			_, ok := err.(ErrAddrBookPrivate)
   464  			assert.True(t, ok)
   465  		}
   466  	}
   467  
   468  	// addrs coming from private peers must not be added
   469  	err := book.AddAddress(randIPv4Address(t), addrs[0])
   470  	if assert.Error(t, err) {
   471  		_, ok := err.(ErrAddrBookPrivateSrc)
   472  		assert.True(t, ok)
   473  	}
   474  }
   475  
   476  func testAddrBookAddressSelection(t *testing.T, bookSize int) {
   477  	// generate all combinations of old (m) and new addresses
   478  	for nBookOld := 0; nBookOld <= bookSize; nBookOld++ {
   479  		nBookNew := bookSize - nBookOld
   480  		dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nBookNew, nBookOld)
   481  
   482  		// create book and get selection
   483  		book, fname := createAddrBookWithMOldAndNNewAddrs(t, nBookOld, nBookNew)
   484  		defer deleteTempFile(fname)
   485  		addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
   486  		assert.NotNil(t, addrs, "%s - expected a non-nil selection", dbgStr)
   487  		nAddrs := len(addrs)
   488  		assert.NotZero(t, nAddrs, "%s - expected at least one address in selection", dbgStr)
   489  
   490  		// check there's no nil addresses
   491  		for _, addr := range addrs {
   492  			if addr == nil {
   493  				t.Fatalf("%s - got nil address in selection %v", dbgStr, addrs)
   494  			}
   495  		}
   496  
   497  		// XXX: shadowing
   498  		nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
   499  
   500  		// Given:
   501  		// n - num new addrs, m - num old addrs
   502  		// k - num new addrs expected in the beginning (based on bias %)
   503  		// i=min(n, max(k,r-m)), aka expNew
   504  		// j=min(m, r-i), aka expOld
   505  		//
   506  		// We expect this layout:
   507  		// indices:      0...i-1   i...i+j-1
   508  		// addresses:    N0..Ni-1  O0..Oj-1
   509  		//
   510  		// There is at least one partition and at most three.
   511  		var (
   512  			k      = percentageOfNum(biasToSelectNewPeers, nAddrs)
   513  			expNew = tmmath.MinInt(nNew, tmmath.MaxInt(k, nAddrs-nBookOld))
   514  			expOld = tmmath.MinInt(nOld, nAddrs-expNew)
   515  		)
   516  
   517  		// Verify that the number of old and new addresses are as expected
   518  		if nNew != expNew {
   519  			t.Fatalf("%s - expected new addrs %d, got %d", dbgStr, expNew, nNew)
   520  		}
   521  		if nOld != expOld {
   522  			t.Fatalf("%s - expected old addrs %d, got %d", dbgStr, expOld, nOld)
   523  		}
   524  
   525  		// Verify that the order of addresses is as expected
   526  		// Get the sequence types and lengths of the selection
   527  		seqLens, seqTypes, err := analyseSelectionLayout(book, addrs)
   528  		assert.NoError(t, err, "%s", dbgStr)
   529  
   530  		// Build a list with the expected lengths of partitions and another with the expected types, e.g.:
   531  		// expSeqLens = [10, 22], expSeqTypes = [1, 2]
   532  		// means we expect 10 new (type 1) addresses followed by 22 old (type 2) addresses.
   533  		var expSeqLens []int
   534  		var expSeqTypes []int
   535  
   536  		switch {
   537  		case expOld == 0: // all new addresses
   538  			expSeqLens = []int{nAddrs}
   539  			expSeqTypes = []int{1}
   540  		case expNew == 0: // all old addresses
   541  			expSeqLens = []int{nAddrs}
   542  			expSeqTypes = []int{2}
   543  		case nAddrs-expNew-expOld == 0: // new addresses, old addresses
   544  			expSeqLens = []int{expNew, expOld}
   545  			expSeqTypes = []int{1, 2}
   546  		}
   547  
   548  		assert.Equal(t, expSeqLens, seqLens,
   549  			"%s - expected sequence lengths of old/new %v, got %v",
   550  			dbgStr, expSeqLens, seqLens)
   551  		assert.Equal(t, expSeqTypes, seqTypes,
   552  			"%s - expected sequence types (1-new, 2-old) was %v, got %v",
   553  			dbgStr, expSeqTypes, seqTypes)
   554  	}
   555  }
   556  
   557  func TestMultipleAddrBookAddressSelection(t *testing.T) {
   558  	// test books with smaller size, < N
   559  	const N = 32
   560  	for bookSize := 1; bookSize < N; bookSize++ {
   561  		testAddrBookAddressSelection(t, bookSize)
   562  	}
   563  
   564  	// Test for two books with sizes from following ranges
   565  	ranges := [...][]int{{33, 100}, {100, 175}}
   566  	bookSizes := make([]int, 0, len(ranges))
   567  	for _, r := range ranges {
   568  		bookSizes = append(bookSizes, tmrand.Intn(r[1]-r[0])+r[0])
   569  	}
   570  	t.Logf("Testing address selection for the following book sizes %v\n", bookSizes)
   571  	for _, bookSize := range bookSizes {
   572  		testAddrBookAddressSelection(t, bookSize)
   573  	}
   574  }
   575  
   576  func TestAddrBookGroupKey(t *testing.T) {
   577  	// non-strict routability
   578  	testCases := []struct {
   579  		name   string
   580  		ip     string
   581  		expKey string
   582  	}{
   583  		// IPv4 normal.
   584  		{"ipv4 normal class a", "12.1.2.3", "12.1.0.0"},
   585  		{"ipv4 normal class b", "173.1.2.3", "173.1.0.0"},
   586  		{"ipv4 normal class c", "196.1.2.3", "196.1.0.0"},
   587  
   588  		// IPv6/IPv4 translations.
   589  		{"ipv6 rfc3964 with ipv4 encap", "2002:0c01:0203::", "12.1.0.0"},
   590  		{"ipv6 rfc4380 toredo ipv4", "2001:0:1234::f3fe:fdfc", "12.1.0.0"},
   591  		{"ipv6 rfc6052 well-known prefix with ipv4", "64:ff9b::0c01:0203", "12.1.0.0"},
   592  		{"ipv6 rfc6145 translated ipv4", "::ffff:0:0c01:0203", "12.1.0.0"},
   593  
   594  		// Tor.
   595  		{"ipv6 tor onioncat", "fd87:d87e:eb43:1234::5678", "tor:2"},
   596  		{"ipv6 tor onioncat 2", "fd87:d87e:eb43:1245::6789", "tor:2"},
   597  		{"ipv6 tor onioncat 3", "fd87:d87e:eb43:1345::6789", "tor:3"},
   598  
   599  		// IPv6 normal.
   600  		{"ipv6 normal", "2602:100::1", "2602:100::"},
   601  		{"ipv6 normal 2", "2602:0100::1234", "2602:100::"},
   602  		{"ipv6 hurricane electric", "2001:470:1f10:a1::2", "2001:470:1000::"},
   603  		{"ipv6 hurricane electric 2", "2001:0470:1f10:a1::2", "2001:470:1000::"},
   604  	}
   605  
   606  	for i, tc := range testCases {
   607  		nip := net.ParseIP(tc.ip)
   608  		key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), false)
   609  		assert.Equal(t, tc.expKey, key, "#%d", i)
   610  	}
   611  
   612  	// strict routability
   613  	testCases = []struct {
   614  		name   string
   615  		ip     string
   616  		expKey string
   617  	}{
   618  		// Local addresses.
   619  		{"ipv4 localhost", "127.0.0.1", "local"},
   620  		{"ipv6 localhost", "::1", "local"},
   621  		{"ipv4 zero", "0.0.0.0", "local"},
   622  		{"ipv4 first octet zero", "0.1.2.3", "local"},
   623  
   624  		// Unroutable addresses.
   625  		{"ipv4 invalid bcast", "255.255.255.255", "unroutable"},
   626  		{"ipv4 rfc1918 10/8", "10.1.2.3", "unroutable"},
   627  		{"ipv4 rfc1918 172.16/12", "172.16.1.2", "unroutable"},
   628  		{"ipv4 rfc1918 192.168/16", "192.168.1.2", "unroutable"},
   629  		{"ipv6 rfc3849 2001:db8::/32", "2001:db8::1234", "unroutable"},
   630  		{"ipv4 rfc3927 169.254/16", "169.254.1.2", "unroutable"},
   631  		{"ipv6 rfc4193 fc00::/7", "fc00::1234", "unroutable"},
   632  		{"ipv6 rfc4843 2001:10::/28", "2001:10::1234", "unroutable"},
   633  		{"ipv6 rfc4862 fe80::/64", "fe80::1234", "unroutable"},
   634  	}
   635  
   636  	for i, tc := range testCases {
   637  		nip := net.ParseIP(tc.ip)
   638  		key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), true)
   639  		assert.Equal(t, tc.expKey, key, "#%d", i)
   640  	}
   641  }
   642  
   643  func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) {
   644  	nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
   645  	assert.Equal(t, m, nOld, "old addresses")
   646  	assert.Equal(t, n, nNew, "new addresses")
   647  }
   648  
   649  func createTempFileName(prefix string) string {
   650  	f, err := ioutil.TempFile("", prefix)
   651  	if err != nil {
   652  		panic(err)
   653  	}
   654  	fname := f.Name()
   655  	err = f.Close()
   656  	if err != nil {
   657  		panic(err)
   658  	}
   659  	return fname
   660  }
   661  
   662  func deleteTempFile(fname string) {
   663  	err := os.Remove(fname)
   664  	if err != nil {
   665  		panic(err)
   666  	}
   667  }
   668  
   669  func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *addrBook, fname string) {
   670  	fname = createTempFileName("addrbook_test")
   671  
   672  	book = NewAddrBook(fname, true).(*addrBook)
   673  	book.SetLogger(log.TestingLogger())
   674  	assert.Zero(t, book.Size())
   675  
   676  	randAddrs := randNetAddressPairs(t, nOld)
   677  	for _, addr := range randAddrs {
   678  		book.AddAddress(addr.addr, addr.src)
   679  		book.MarkGood(addr.addr.ID)
   680  	}
   681  
   682  	randAddrs = randNetAddressPairs(t, nNew)
   683  	for _, addr := range randAddrs {
   684  		book.AddAddress(addr.addr, addr.src)
   685  	}
   686  
   687  	return
   688  }
   689  
   690  func countOldAndNewAddrsInSelection(addrs []*p2p.NetAddress, book *addrBook) (nOld, nNew int) {
   691  	for _, addr := range addrs {
   692  		if book.IsGood(addr) {
   693  			nOld++
   694  		} else {
   695  			nNew++
   696  		}
   697  	}
   698  	return
   699  }
   700  
   701  // Analyse the layout of the selection specified by 'addrs'
   702  // Returns:
   703  // - seqLens - the lengths of the sequences of addresses of same type
   704  // - seqTypes - the types of sequences in selection
   705  func analyseSelectionLayout(book *addrBook, addrs []*p2p.NetAddress) (seqLens, seqTypes []int, err error) {
   706  	// address types are: 0 - nil, 1 - new, 2 - old
   707  	var (
   708  		prevType      = 0
   709  		currentSeqLen = 0
   710  	)
   711  
   712  	for _, addr := range addrs {
   713  		addrType := 0
   714  		if book.IsGood(addr) {
   715  			addrType = 2
   716  		} else {
   717  			addrType = 1
   718  		}
   719  		if addrType != prevType && prevType != 0 {
   720  			seqLens = append(seqLens, currentSeqLen)
   721  			seqTypes = append(seqTypes, prevType)
   722  			currentSeqLen = 0
   723  		}
   724  		currentSeqLen++
   725  		prevType = addrType
   726  	}
   727  
   728  	seqLens = append(seqLens, currentSeqLen)
   729  	seqTypes = append(seqTypes, prevType)
   730  
   731  	return
   732  }
   733  
   734  func BenchmarkAddrBook_hash(b *testing.B) {
   735  	book := &addrBook{
   736  		ourAddrs:          make(map[string]struct{}),
   737  		privateIDs:        make(map[p2p.ID]struct{}),
   738  		addrLookup:        make(map[p2p.ID]*knownAddress),
   739  		badPeers:          make(map[p2p.ID]*knownAddress),
   740  		filePath:          "",
   741  		routabilityStrict: true,
   742  	}
   743  	book.init()
   744  	msg := []byte(`foobar`)
   745  	b.ResetTimer()
   746  	for i := 0; i < b.N; i++ {
   747  		_, _ = book.hash(msg)
   748  	}
   749  }