github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/pex/addrbook_test.go (about)

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