package pex
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math"
|
|
mrand "math/rand"
|
|
"net"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/tendermint/tendermint/internal/p2p"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
tmmath "github.com/tendermint/tendermint/libs/math"
|
|
tmrand "github.com/tendermint/tendermint/libs/rand"
|
|
)
|
|
|
|
// FIXME These tests should not rely on .(*addrBook) assertions
|
|
|
|
func TestAddrBookPickAddress(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
|
|
// 0 addresses
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
assert.Zero(t, book.Size())
|
|
|
|
addr := book.PickAddress(50)
|
|
assert.Nil(t, addr, "expected no address")
|
|
|
|
randAddrs := randNetAddressPairs(t, 1)
|
|
addrSrc := randAddrs[0]
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
|
|
// pick an address when we only have new address
|
|
addr = book.PickAddress(0)
|
|
assert.NotNil(t, addr, "expected an address")
|
|
addr = book.PickAddress(50)
|
|
assert.NotNil(t, addr, "expected an address")
|
|
addr = book.PickAddress(100)
|
|
assert.NotNil(t, addr, "expected an address")
|
|
|
|
// pick an address when we only have old address
|
|
book.MarkGood(addrSrc.addr.ID)
|
|
addr = book.PickAddress(0)
|
|
assert.NotNil(t, addr, "expected an address")
|
|
addr = book.PickAddress(50)
|
|
assert.NotNil(t, addr, "expected an address")
|
|
|
|
// in this case, nNew==0 but we biased 100% to new, so we return nil
|
|
addr = book.PickAddress(100)
|
|
assert.Nil(t, addr, "did not expected an address")
|
|
}
|
|
|
|
func TestAddrBookSaveLoad(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
|
|
// 0 addresses
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
book.Save()
|
|
|
|
book = NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
err := book.Start()
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, book.Empty())
|
|
|
|
// 100 addresses
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
for _, addrSrc := range randAddrs {
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
assert.Equal(t, 100, book.Size())
|
|
book.Save()
|
|
|
|
book = NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
err = book.Start()
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 100, book.Size())
|
|
}
|
|
|
|
func TestAddrBookLookup(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
for _, addrSrc := range randAddrs {
|
|
addr := addrSrc.addr
|
|
src := addrSrc.src
|
|
err := book.AddAddress(addr, src)
|
|
require.NoError(t, err)
|
|
|
|
ka := book.HasAddress(addr)
|
|
assert.True(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
|
|
}
|
|
}
|
|
|
|
func TestAddrBookPromoteToOld(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
for _, addrSrc := range randAddrs {
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Attempt all addresses.
|
|
for _, addrSrc := range randAddrs {
|
|
book.MarkAttempt(addrSrc.addr)
|
|
}
|
|
|
|
// Promote half of them
|
|
for i, addrSrc := range randAddrs {
|
|
if i%2 == 0 {
|
|
book.MarkGood(addrSrc.addr.ID)
|
|
}
|
|
}
|
|
|
|
// TODO: do more testing :)
|
|
|
|
selection := book.GetSelection()
|
|
t.Logf("selection: %v", selection)
|
|
|
|
if len(selection) > book.Size() {
|
|
t.Errorf("selection could not be bigger than the book")
|
|
}
|
|
|
|
selection = book.GetSelectionWithBias(30)
|
|
t.Logf("selection: %v", selection)
|
|
|
|
if len(selection) > book.Size() {
|
|
t.Errorf("selection with bias could not be bigger than the book")
|
|
}
|
|
|
|
assert.Equal(t, book.Size(), 100, "expecting book size to be 100")
|
|
}
|
|
|
|
func TestAddrBookHandlesDuplicates(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
differentSrc := randIPv4Address(t)
|
|
for _, addrSrc := range randAddrs {
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
err = book.AddAddress(addrSrc.addr, addrSrc.src) // duplicate
|
|
require.NoError(t, err)
|
|
err = book.AddAddress(addrSrc.addr, differentSrc) // different src
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
assert.Equal(t, 100, book.Size())
|
|
}
|
|
|
|
type netAddressPair struct {
|
|
addr *p2p.NetAddress
|
|
src *p2p.NetAddress
|
|
}
|
|
|
|
func randNetAddressPairs(t *testing.T, n int) []netAddressPair {
|
|
randAddrs := make([]netAddressPair, n)
|
|
for i := 0; i < n; i++ {
|
|
randAddrs[i] = netAddressPair{addr: randIPv4Address(t), src: randIPv4Address(t)}
|
|
}
|
|
return randAddrs
|
|
}
|
|
|
|
func randIPv4Address(t *testing.T) *p2p.NetAddress {
|
|
for {
|
|
ip := fmt.Sprintf("%v.%v.%v.%v",
|
|
mrand.Intn(254)+1,
|
|
mrand.Intn(255),
|
|
mrand.Intn(255),
|
|
mrand.Intn(255),
|
|
)
|
|
port := mrand.Intn(65535-1) + 1
|
|
id := p2p.NodeID(hex.EncodeToString(tmrand.Bytes(p2p.NodeIDByteLength)))
|
|
idAddr := p2p.IDAddressString(id, fmt.Sprintf("%v:%v", ip, port))
|
|
addr, err := p2p.NewNetAddressString(idAddr)
|
|
assert.Nil(t, err, "error generating rand network address")
|
|
if addr.Routable() {
|
|
return addr
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddrBookRemoveAddress(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
addr := randIPv4Address(t)
|
|
err := book.AddAddress(addr, addr)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, book.Size())
|
|
|
|
book.RemoveAddress(addr)
|
|
assert.Equal(t, 0, book.Size())
|
|
|
|
nonExistingAddr := randIPv4Address(t)
|
|
book.RemoveAddress(nonExistingAddr)
|
|
assert.Equal(t, 0, book.Size())
|
|
}
|
|
|
|
func TestAddrBookGetSelectionWithOneMarkedGood(t *testing.T) {
|
|
// create a book with 10 addresses, 1 good/old and 9 new
|
|
book, _ := createAddrBookWithMOldAndNNewAddrs(t, 1, 9)
|
|
addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
|
|
assert.NotNil(t, addrs)
|
|
assertMOldAndNNewAddrsInSelection(t, 1, 9, addrs, book)
|
|
}
|
|
|
|
func TestAddrBookGetSelectionWithOneNotMarkedGood(t *testing.T) {
|
|
// create a book with 10 addresses, 9 good/old and 1 new
|
|
book, _ := createAddrBookWithMOldAndNNewAddrs(t, 9, 1)
|
|
addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
|
|
assert.NotNil(t, addrs)
|
|
assertMOldAndNNewAddrsInSelection(t, 9, 1, addrs, book)
|
|
}
|
|
|
|
func TestAddrBookGetSelectionReturnsNilWhenAddrBookIsEmpty(t *testing.T) {
|
|
book, _ := createAddrBookWithMOldAndNNewAddrs(t, 0, 0)
|
|
addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
|
|
assert.Nil(t, addrs)
|
|
}
|
|
|
|
func TestAddrBookGetSelection(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
// 1) empty book
|
|
assert.Empty(t, book.GetSelection())
|
|
|
|
// 2) add one address
|
|
addr := randIPv4Address(t)
|
|
err := book.AddAddress(addr, addr)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 1, len(book.GetSelection()))
|
|
assert.Equal(t, addr, book.GetSelection()[0])
|
|
|
|
// 3) add a bunch of addresses
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
for _, addrSrc := range randAddrs {
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// check there is no duplicates
|
|
addrs := make(map[string]*p2p.NetAddress)
|
|
selection := book.GetSelection()
|
|
for _, addr := range selection {
|
|
if dup, ok := addrs[addr.String()]; ok {
|
|
t.Fatalf("selection %v contains duplicates %v", selection, dup)
|
|
}
|
|
addrs[addr.String()] = addr
|
|
}
|
|
|
|
if len(selection) > book.Size() {
|
|
t.Errorf("selection %v could not be bigger than the book", selection)
|
|
}
|
|
}
|
|
|
|
func TestAddrBookGetSelectionWithBias(t *testing.T) {
|
|
const biasTowardsNewAddrs = 30
|
|
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
// 1) empty book
|
|
selection := book.GetSelectionWithBias(biasTowardsNewAddrs)
|
|
assert.Empty(t, selection)
|
|
|
|
// 2) add one address
|
|
addr := randIPv4Address(t)
|
|
err := book.AddAddress(addr, addr)
|
|
require.NoError(t, err)
|
|
|
|
selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
|
|
assert.Equal(t, 1, len(selection))
|
|
assert.Equal(t, addr, selection[0])
|
|
|
|
// 3) add a bunch of addresses
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
for _, addrSrc := range randAddrs {
|
|
err := book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// check there is no duplicates
|
|
addrs := make(map[string]*p2p.NetAddress)
|
|
selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
|
|
for _, addr := range selection {
|
|
if dup, ok := addrs[addr.String()]; ok {
|
|
t.Fatalf("selection %v contains duplicates %v", selection, dup)
|
|
}
|
|
addrs[addr.String()] = addr
|
|
}
|
|
|
|
if len(selection) > book.Size() {
|
|
t.Fatalf("selection %v could not be bigger than the book", selection)
|
|
}
|
|
|
|
// 4) mark 80% of the addresses as good
|
|
randAddrsLen := len(randAddrs)
|
|
for i, addrSrc := range randAddrs {
|
|
if int((float64(i)/float64(randAddrsLen))*100) >= 20 {
|
|
book.MarkGood(addrSrc.addr.ID)
|
|
}
|
|
}
|
|
|
|
selection = book.GetSelectionWithBias(biasTowardsNewAddrs)
|
|
|
|
// check that ~70% of addresses returned are good
|
|
good := 0
|
|
for _, addr := range selection {
|
|
if book.IsGood(addr) {
|
|
good++
|
|
}
|
|
}
|
|
|
|
got, expected := int((float64(good)/float64(len(selection)))*100), 100-biasTowardsNewAddrs
|
|
|
|
// compute some slack to protect against small differences due to rounding:
|
|
slack := int(math.Round(float64(100) / float64(len(selection))))
|
|
if got > expected+slack {
|
|
t.Fatalf(
|
|
"got more good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)",
|
|
got,
|
|
expected,
|
|
good,
|
|
len(selection),
|
|
)
|
|
}
|
|
if got < expected-slack {
|
|
t.Fatalf(
|
|
"got fewer good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)",
|
|
got,
|
|
expected,
|
|
good,
|
|
len(selection),
|
|
)
|
|
}
|
|
}
|
|
|
|
func TestAddrBookHasAddress(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
addr := randIPv4Address(t)
|
|
err := book.AddAddress(addr, addr)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, book.HasAddress(addr))
|
|
|
|
book.RemoveAddress(addr)
|
|
|
|
assert.False(t, book.HasAddress(addr))
|
|
}
|
|
|
|
func testCreatePrivateAddrs(t *testing.T, numAddrs int) ([]*p2p.NetAddress, []string) {
|
|
t.Helper()
|
|
addrs := make([]*p2p.NetAddress, numAddrs)
|
|
for i := 0; i < numAddrs; i++ {
|
|
addrs[i] = randIPv4Address(t)
|
|
}
|
|
|
|
private := make([]string, numAddrs)
|
|
for i, addr := range addrs {
|
|
private[i] = string(addr.ID)
|
|
}
|
|
return addrs, private
|
|
}
|
|
|
|
func TestBanBadPeers(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
addr := randIPv4Address(t)
|
|
_ = book.AddAddress(addr, addr)
|
|
|
|
book.MarkBad(addr, 1*time.Second)
|
|
// addr should not reachable
|
|
assert.False(t, book.HasAddress(addr))
|
|
assert.True(t, book.IsBanned(addr))
|
|
|
|
err := book.AddAddress(addr, addr)
|
|
// book should not add address from the blacklist
|
|
assert.Error(t, err)
|
|
|
|
time.Sleep(1 * time.Second)
|
|
book.ReinstateBadPeers()
|
|
// address should be reinstated in the new bucket
|
|
assert.EqualValues(t, 1, book.Size())
|
|
assert.True(t, book.HasAddress(addr))
|
|
assert.False(t, book.IsGood(addr))
|
|
}
|
|
|
|
func TestAddrBookEmpty(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
// Check that empty book is empty
|
|
require.True(t, book.Empty())
|
|
// Check that book with our address is empty
|
|
book.AddOurAddress(randIPv4Address(t))
|
|
require.True(t, book.Empty())
|
|
// Check that book with private addrs is empty
|
|
_, privateIds := testCreatePrivateAddrs(t, 5)
|
|
book.AddPrivateIDs(privateIds)
|
|
require.True(t, book.Empty())
|
|
|
|
// Check that book with address is not empty
|
|
err := book.AddAddress(randIPv4Address(t), randIPv4Address(t))
|
|
require.NoError(t, err)
|
|
require.False(t, book.Empty())
|
|
}
|
|
|
|
func TestPrivatePeers(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
addrs, private := testCreatePrivateAddrs(t, 10)
|
|
book.AddPrivateIDs(private)
|
|
|
|
// private addrs must not be added
|
|
for _, addr := range addrs {
|
|
err := book.AddAddress(addr, addr)
|
|
if assert.Error(t, err) {
|
|
_, ok := err.(ErrAddrBookPrivate)
|
|
assert.True(t, ok)
|
|
}
|
|
}
|
|
|
|
// addrs coming from private peers must not be added
|
|
err := book.AddAddress(randIPv4Address(t), addrs[0])
|
|
if assert.Error(t, err) {
|
|
_, ok := err.(ErrAddrBookPrivateSrc)
|
|
assert.True(t, ok)
|
|
}
|
|
}
|
|
|
|
func testAddrBookAddressSelection(t *testing.T, bookSize int) {
|
|
// generate all combinations of old (m) and new addresses
|
|
for nBookOld := 0; nBookOld <= bookSize; nBookOld++ {
|
|
nBookNew := bookSize - nBookOld
|
|
dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nBookNew, nBookOld)
|
|
|
|
// create book and get selection
|
|
book, _ := createAddrBookWithMOldAndNNewAddrs(t, nBookOld, nBookNew)
|
|
addrs := book.GetSelectionWithBias(biasToSelectNewPeers)
|
|
assert.NotNil(t, addrs, "%s - expected a non-nil selection", dbgStr)
|
|
nAddrs := len(addrs)
|
|
assert.NotZero(t, nAddrs, "%s - expected at least one address in selection", dbgStr)
|
|
|
|
// check there's no nil addresses
|
|
for _, addr := range addrs {
|
|
if addr == nil {
|
|
t.Fatalf("%s - got nil address in selection %v", dbgStr, addrs)
|
|
}
|
|
}
|
|
|
|
// XXX: shadowing
|
|
nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
|
|
|
|
// Given:
|
|
// n - num new addrs, m - num old addrs
|
|
// k - num new addrs expected in the beginning (based on bias %)
|
|
// i=min(n, max(k,r-m)), aka expNew
|
|
// j=min(m, r-i), aka expOld
|
|
//
|
|
// We expect this layout:
|
|
// indices: 0...i-1 i...i+j-1
|
|
// addresses: N0..Ni-1 O0..Oj-1
|
|
//
|
|
// There is at least one partition and at most three.
|
|
var (
|
|
k = percentageOfNum(biasToSelectNewPeers, nAddrs)
|
|
expNew = tmmath.MinInt(nNew, tmmath.MaxInt(k, nAddrs-nBookOld))
|
|
expOld = tmmath.MinInt(nOld, nAddrs-expNew)
|
|
)
|
|
|
|
// Verify that the number of old and new addresses are as expected
|
|
if nNew != expNew {
|
|
t.Fatalf("%s - expected new addrs %d, got %d", dbgStr, expNew, nNew)
|
|
}
|
|
if nOld != expOld {
|
|
t.Fatalf("%s - expected old addrs %d, got %d", dbgStr, expOld, nOld)
|
|
}
|
|
|
|
// Verify that the order of addresses is as expected
|
|
// Get the sequence types and lengths of the selection
|
|
seqLens, seqTypes, err := analyseSelectionLayout(book, addrs)
|
|
assert.NoError(t, err, "%s", dbgStr)
|
|
|
|
// Build a list with the expected lengths of partitions and another with the expected types, e.g.:
|
|
// expSeqLens = [10, 22], expSeqTypes = [1, 2]
|
|
// means we expect 10 new (type 1) addresses followed by 22 old (type 2) addresses.
|
|
var expSeqLens []int
|
|
var expSeqTypes []int
|
|
|
|
switch {
|
|
case expOld == 0: // all new addresses
|
|
expSeqLens = []int{nAddrs}
|
|
expSeqTypes = []int{1}
|
|
case expNew == 0: // all old addresses
|
|
expSeqLens = []int{nAddrs}
|
|
expSeqTypes = []int{2}
|
|
case nAddrs-expNew-expOld == 0: // new addresses, old addresses
|
|
expSeqLens = []int{expNew, expOld}
|
|
expSeqTypes = []int{1, 2}
|
|
}
|
|
|
|
assert.Equal(t, expSeqLens, seqLens,
|
|
"%s - expected sequence lengths of old/new %v, got %v",
|
|
dbgStr, expSeqLens, seqLens)
|
|
assert.Equal(t, expSeqTypes, seqTypes,
|
|
"%s - expected sequence types (1-new, 2-old) was %v, got %v",
|
|
dbgStr, expSeqTypes, seqTypes)
|
|
}
|
|
}
|
|
|
|
func TestMultipleAddrBookAddressSelection(t *testing.T) {
|
|
// test books with smaller size, < N
|
|
const N = 32
|
|
for bookSize := 1; bookSize < N; bookSize++ {
|
|
testAddrBookAddressSelection(t, bookSize)
|
|
}
|
|
|
|
// Test for two books with sizes from following ranges
|
|
ranges := [...][]int{{33, 100}, {100, 175}}
|
|
bookSizes := make([]int, 0, len(ranges))
|
|
for _, r := range ranges {
|
|
bookSizes = append(bookSizes, mrand.Intn(r[1]-r[0])+r[0])
|
|
}
|
|
t.Logf("Testing address selection for the following book sizes %v\n", bookSizes)
|
|
for _, bookSize := range bookSizes {
|
|
testAddrBookAddressSelection(t, bookSize)
|
|
}
|
|
}
|
|
|
|
func TestAddrBookAddDoesNotOverwriteOldIP(t *testing.T) {
|
|
fname := createTempFileName(t, "addrbook_test")
|
|
|
|
// This test creates adds a peer to the address book and marks it good
|
|
// It then attempts to override the peer's IP, by adding a peer with the same ID
|
|
// but different IP. We distinguish the IP's by "RealIP" and "OverrideAttemptIP"
|
|
peerID := "678503e6c8f50db7279c7da3cb9b072aac4bc0d5"
|
|
peerRealIP := "1.1.1.1:26656"
|
|
peerOverrideAttemptIP := "2.2.2.2:26656"
|
|
SrcAddr := "b0dd378c3fbc4c156cd6d302a799f0d2e4227201@159.89.121.174:26656"
|
|
|
|
// There is a chance that AddAddress will ignore the new peer its given.
|
|
// So we repeat trying to override the peer several times,
|
|
// to ensure we aren't in a case that got probabilistically ignored
|
|
numOverrideAttempts := 10
|
|
|
|
peerRealAddr, err := p2p.NewNetAddressString(peerID + "@" + peerRealIP)
|
|
require.Nil(t, err)
|
|
|
|
peerOverrideAttemptAddr, err := p2p.NewNetAddressString(peerID + "@" + peerOverrideAttemptIP)
|
|
require.Nil(t, err)
|
|
|
|
src, err := p2p.NewNetAddressString(SrcAddr)
|
|
require.Nil(t, err)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
err = book.AddAddress(peerRealAddr, src)
|
|
require.Nil(t, err)
|
|
book.MarkAttempt(peerRealAddr)
|
|
book.MarkGood(peerRealAddr.ID)
|
|
|
|
// Double check that adding a peer again doesn't error
|
|
err = book.AddAddress(peerRealAddr, src)
|
|
require.Nil(t, err)
|
|
|
|
// Try changing ip but keeping the same node id. (change 1.1.1.1 to 2.2.2.2)
|
|
// This should just be ignored, and not error.
|
|
for i := 0; i < numOverrideAttempts; i++ {
|
|
err = book.AddAddress(peerOverrideAttemptAddr, src)
|
|
require.Nil(t, err)
|
|
}
|
|
// Now check that the IP was not overridden.
|
|
// This is done by sampling several peers from addr book
|
|
// and ensuring they all have the correct IP.
|
|
// In the expected functionality, this test should only have 1 Peer, hence will pass.
|
|
for i := 0; i < numOverrideAttempts; i++ {
|
|
selection := book.GetSelection()
|
|
for _, addr := range selection {
|
|
require.Equal(t, addr.IP, peerRealAddr.IP)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddrBookGroupKey(t *testing.T) {
|
|
// non-strict routability
|
|
testCases := []struct {
|
|
name string
|
|
ip string
|
|
expKey string
|
|
}{
|
|
// IPv4 normal.
|
|
{"ipv4 normal class a", "12.1.2.3", "12.1.0.0"},
|
|
{"ipv4 normal class b", "173.1.2.3", "173.1.0.0"},
|
|
{"ipv4 normal class c", "196.1.2.3", "196.1.0.0"},
|
|
|
|
// IPv6/IPv4 translations.
|
|
{"ipv6 rfc3964 with ipv4 encap", "2002:0c01:0203::", "12.1.0.0"},
|
|
{"ipv6 rfc4380 toredo ipv4", "2001:0:1234::f3fe:fdfc", "12.1.0.0"},
|
|
{"ipv6 rfc6052 well-known prefix with ipv4", "64:ff9b::0c01:0203", "12.1.0.0"},
|
|
{"ipv6 rfc6145 translated ipv4", "::ffff:0:0c01:0203", "12.1.0.0"},
|
|
|
|
// Tor.
|
|
{"ipv6 tor onioncat", "fd87:d87e:eb43:1234::5678", "tor:2"},
|
|
{"ipv6 tor onioncat 2", "fd87:d87e:eb43:1245::6789", "tor:2"},
|
|
{"ipv6 tor onioncat 3", "fd87:d87e:eb43:1345::6789", "tor:3"},
|
|
|
|
// IPv6 normal.
|
|
{"ipv6 normal", "2602:100::1", "2602:100::"},
|
|
{"ipv6 normal 2", "2602:0100::1234", "2602:100::"},
|
|
{"ipv6 hurricane electric", "2001:470:1f10:a1::2", "2001:470:1000::"},
|
|
{"ipv6 hurricane electric 2", "2001:0470:1f10:a1::2", "2001:470:1000::"},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
nip := net.ParseIP(tc.ip)
|
|
key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), false)
|
|
assert.Equal(t, tc.expKey, key, "#%d", i)
|
|
}
|
|
|
|
// strict routability
|
|
testCases = []struct {
|
|
name string
|
|
ip string
|
|
expKey string
|
|
}{
|
|
// Local addresses.
|
|
{"ipv4 localhost", "127.0.0.1", "local"},
|
|
{"ipv6 localhost", "::1", "local"},
|
|
{"ipv4 zero", "0.0.0.0", "local"},
|
|
{"ipv4 first octet zero", "0.1.2.3", "local"},
|
|
|
|
// Unroutable addresses.
|
|
{"ipv4 invalid bcast", "255.255.255.255", "unroutable"},
|
|
{"ipv4 rfc1918 10/8", "10.1.2.3", "unroutable"},
|
|
{"ipv4 rfc1918 172.16/12", "172.16.1.2", "unroutable"},
|
|
{"ipv4 rfc1918 192.168/16", "192.168.1.2", "unroutable"},
|
|
{"ipv6 rfc3849 2001:db8::/32", "2001:db8::1234", "unroutable"},
|
|
{"ipv4 rfc3927 169.254/16", "169.254.1.2", "unroutable"},
|
|
{"ipv6 rfc4193 fc00::/7", "fc00::1234", "unroutable"},
|
|
{"ipv6 rfc4843 2001:10::/28", "2001:10::1234", "unroutable"},
|
|
{"ipv6 rfc4862 fe80::/64", "fe80::1234", "unroutable"},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
nip := net.ParseIP(tc.ip)
|
|
key := groupKeyFor(p2p.NewNetAddressIPPort(nip, 26656), true)
|
|
assert.Equal(t, tc.expKey, key, "#%d", i)
|
|
}
|
|
}
|
|
|
|
func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) {
|
|
nOld, nNew := countOldAndNewAddrsInSelection(addrs, book)
|
|
assert.Equal(t, m, nOld, "old addresses")
|
|
assert.Equal(t, n, nNew, "new addresses")
|
|
}
|
|
|
|
func createTempFileName(t *testing.T, prefix string) string {
|
|
t.Helper()
|
|
f, err := ioutil.TempFile("", prefix)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
fname := f.Name()
|
|
if err := f.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Cleanup(func() { _ = os.Remove(fname) })
|
|
|
|
return fname
|
|
}
|
|
|
|
func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *addrBook, fname string) {
|
|
t.Helper()
|
|
fname = createTempFileName(t, "addrbook_test")
|
|
|
|
book = NewAddrBook(fname, true).(*addrBook)
|
|
book.SetLogger(log.TestingLogger())
|
|
assert.Zero(t, book.Size())
|
|
|
|
randAddrs := randNetAddressPairs(t, nOld)
|
|
for _, addr := range randAddrs {
|
|
err := book.AddAddress(addr.addr, addr.src)
|
|
require.NoError(t, err)
|
|
book.MarkGood(addr.addr.ID)
|
|
}
|
|
|
|
randAddrs = randNetAddressPairs(t, nNew)
|
|
for _, addr := range randAddrs {
|
|
err := book.AddAddress(addr.addr, addr.src)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func countOldAndNewAddrsInSelection(addrs []*p2p.NetAddress, book *addrBook) (nOld, nNew int) {
|
|
for _, addr := range addrs {
|
|
if book.IsGood(addr) {
|
|
nOld++
|
|
} else {
|
|
nNew++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Analyze the layout of the selection specified by 'addrs'
|
|
// Returns:
|
|
// - seqLens - the lengths of the sequences of addresses of same type
|
|
// - seqTypes - the types of sequences in selection
|
|
func analyseSelectionLayout(book *addrBook, addrs []*p2p.NetAddress) (seqLens, seqTypes []int, err error) {
|
|
// address types are: 0 - nil, 1 - new, 2 - old
|
|
var (
|
|
prevType = 0
|
|
currentSeqLen = 0
|
|
)
|
|
|
|
for _, addr := range addrs {
|
|
addrType := 0
|
|
if book.IsGood(addr) {
|
|
addrType = 2
|
|
} else {
|
|
addrType = 1
|
|
}
|
|
if addrType != prevType && prevType != 0 {
|
|
seqLens = append(seqLens, currentSeqLen)
|
|
seqTypes = append(seqTypes, prevType)
|
|
currentSeqLen = 0
|
|
}
|
|
currentSeqLen++
|
|
prevType = addrType
|
|
}
|
|
|
|
seqLens = append(seqLens, currentSeqLen)
|
|
seqTypes = append(seqTypes, prevType)
|
|
|
|
return
|
|
}
|