package pex
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
"github.com/tendermint/tendermint/p2p"
|
|
)
|
|
|
|
func createTempFileName(prefix string) string {
|
|
f, err := ioutil.TempFile("", prefix)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fname := f.Name()
|
|
err = f.Close()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return fname
|
|
}
|
|
|
|
func deleteTempFile(fname string) {
|
|
err := os.Remove(fname)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func TestAddrBookPickAddress(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
// 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]
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
|
|
// 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)
|
|
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("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
// 0 addresses
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
book.saveToFile(fname)
|
|
|
|
book = NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
book.loadFromFile(fname)
|
|
|
|
assert.Zero(t, book.Size())
|
|
|
|
// 100 addresses
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
for _, addrSrc := range randAddrs {
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
}
|
|
|
|
assert.Equal(t, 100, book.Size())
|
|
book.saveToFile(fname)
|
|
|
|
book = NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
book.loadFromFile(fname)
|
|
|
|
assert.Equal(t, 100, book.Size())
|
|
}
|
|
|
|
func TestAddrBookLookup(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
for _, addrSrc := range randAddrs {
|
|
addr := addrSrc.addr
|
|
src := addrSrc.src
|
|
book.AddAddress(addr, src)
|
|
|
|
ka := book.addrLookup[addr.ID]
|
|
assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
|
|
|
|
if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) {
|
|
t.Fatalf("KnownAddress doesn't match addr & src")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddrBookPromoteToOld(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
for _, addrSrc := range randAddrs {
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
// 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("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
randAddrs := randNetAddressPairs(t, 100)
|
|
|
|
differentSrc := randIPv4Address(t)
|
|
for _, addrSrc := range randAddrs {
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
book.AddAddress(addrSrc.addr, addrSrc.src) // duplicate
|
|
book.AddAddress(addrSrc.addr, differentSrc) // different src
|
|
}
|
|
|
|
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",
|
|
cmn.RandIntn(254)+1,
|
|
cmn.RandIntn(255),
|
|
cmn.RandIntn(255),
|
|
cmn.RandIntn(255),
|
|
)
|
|
port := cmn.RandIntn(65535-1) + 1
|
|
id := p2p.ID(hex.EncodeToString(cmn.RandBytes(p2p.IDByteLength)))
|
|
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("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
addr := randIPv4Address(t)
|
|
book.AddAddress(addr, addr)
|
|
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 TestAddrBookGetSelection(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
|
|
// 1) empty book
|
|
assert.Empty(t, book.GetSelection())
|
|
|
|
// 2) add one address
|
|
addr := randIPv4Address(t)
|
|
book.AddAddress(addr, addr)
|
|
|
|
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 {
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
}
|
|
|
|
// 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("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
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)
|
|
book.AddAddress(addr, addr)
|
|
|
|
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 {
|
|
book.AddAddress(addrSrc.addr, addrSrc.src)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
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)
|
|
if got >= expected {
|
|
t.Fatalf("expected more 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("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
book := NewAddrBook(fname, true)
|
|
book.SetLogger(log.TestingLogger())
|
|
addr := randIPv4Address(t)
|
|
book.AddAddress(addr, addr)
|
|
|
|
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) {
|
|
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 TestAddrBookEmpty(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
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
|
|
book.AddAddress(randIPv4Address(t), randIPv4Address(t))
|
|
require.False(t, book.Empty())
|
|
}
|
|
|
|
func TestPrivatePeers(t *testing.T) {
|
|
fname := createTempFileName("addrbook_test")
|
|
defer deleteTempFile(fname)
|
|
|
|
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)
|
|
}
|
|
}
|