Browse Source

Merge pull request #16 from tendermint/develop

v0.4.0
pull/456/head
Ethan Buchman 8 years ago
committed by GitHub
parent
commit
97a5ed2d1a
9 changed files with 218 additions and 111 deletions
  1. +5
    -0
      .gitignore
  2. +57
    -0
      CHANGELOG.md
  3. +10
    -0
      addrbook.go
  4. +79
    -92
      addrbook_test.go
  5. +5
    -1
      listener.go
  6. +24
    -7
      netaddress.go
  7. +12
    -3
      pex_reactor.go
  8. +25
    -7
      switch.go
  9. +1
    -1
      version.go

+ 5
- 0
.gitignore View File

@ -0,0 +1,5 @@
*.swp
*.swo
*.bak
.DS_Store
vendor

+ 57
- 0
CHANGELOG.md View File

@ -0,0 +1,57 @@
# Changelog
## 0.4.0 (March 6, 2017)
BREAKING CHANGES:
- DialSeeds now takes an AddrBook and returns an error: `DialSeeds(*AddrBook, []string) error`
- NewNetAddressString now returns an error: `NewNetAddressString(string) (*NetAddress, error)`
FEATURES:
- `NewNetAddressStrings([]string) ([]*NetAddress, error)`
- `AddrBook.Save()`
IMPROVEMENTS:
- PexReactor responsible for starting and stopping the AddrBook
BUG FIXES:
- DialSeeds returns an error instead of panicking on bad addresses
## 0.3.5 (January 12, 2017)
FEATURES
- Toggle strict routability in the AddrBook
BUG FIXES
- Close filtered out connections
- Fixes for MakeConnectedSwitches and Connect2Switches
## 0.3.4 (August 10, 2016)
FEATURES:
- Optionally filter connections by address or public key
## 0.3.3 (May 12, 2016)
FEATURES:
- FuzzConn
## 0.3.2 (March 12, 2016)
IMPROVEMENTS:
- Memory optimizations
## 0.3.1 ()
FEATURES:
- Configurable parameters

+ 10
- 0
addrbook.go View File

@ -135,6 +135,9 @@ func (a *AddrBook) OnStart() error {
func (a *AddrBook) OnStop() { func (a *AddrBook) OnStop() {
a.BaseService.OnStop() a.BaseService.OnStop()
}
func (a *AddrBook) Wait() {
a.wg.Wait() a.wg.Wait()
} }
@ -153,6 +156,7 @@ func (a *AddrBook) OurAddresses() []*NetAddress {
return addrs return addrs
} }
// NOTE: addr must not be nil
func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) { func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {
a.mtx.Lock() a.mtx.Lock()
defer a.mtx.Unlock() defer a.mtx.Unlock()
@ -368,6 +372,12 @@ func (a *AddrBook) loadFromFile(filePath string) bool {
return true return true
} }
// Save saves the book.
func (a *AddrBook) Save() {
log.Info("Saving AddrBook to file", "size", a.Size())
a.saveToFile(a.filePath)
}
/* Private methods */ /* Private methods */
func (a *AddrBook) saveRoutine() { func (a *AddrBook) saveRoutine() {


+ 79
- 92
addrbook_test.go View File

@ -5,9 +5,9 @@ import (
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"testing" "testing"
)
const addrBookStrict = true
"github.com/stretchr/testify/assert"
)
func createTempFileName(prefix string) string { func createTempFileName(prefix string) string {
f, err := ioutil.TempFile("", prefix) f, err := ioutil.TempFile("", prefix)
@ -22,142 +22,129 @@ func createTempFileName(prefix string) string {
return fname return fname
} }
func TestEmpty(t *testing.T) {
func TestAddrBookSaveLoad(t *testing.T) {
fname := createTempFileName("addrbook_test") fname := createTempFileName("addrbook_test")
// t.Logf("New tempfile name: %v", fname)
// Save an empty book & load it
book := NewAddrBook(fname, addrBookStrict)
// 0 addresses
book := NewAddrBook(fname, true)
book.saveToFile(fname) book.saveToFile(fname)
book = NewAddrBook(fname, addrBookStrict)
book = NewAddrBook(fname, true)
book.loadFromFile(fname) book.loadFromFile(fname)
if book.Size() != 0 {
t.Errorf("Expected 0 addresses, found %v", book.Size())
}
}
func randIPv4Address() *NetAddress {
for {
ip := fmt.Sprintf("%v.%v.%v.%v",
rand.Intn(254)+1,
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
)
port := rand.Intn(65535-1) + 1
addr := NewNetAddressString(fmt.Sprintf("%v:%v", ip, port))
if addr.Routable() {
return addr
}
}
}
assert.Zero(t, book.Size())
func TestSaveAddresses(t *testing.T) {
fname := createTempFileName("addrbook_test")
//t.Logf("New tempfile name: %v", fname)
// Create some random addresses
randAddrs := []struct {
addr *NetAddress
src *NetAddress
}{}
for i := 0; i < 100; i++ {
addr := randIPv4Address()
src := randIPv4Address()
randAddrs = append(randAddrs, struct {
addr *NetAddress
src *NetAddress
}{
addr: addr,
src: src,
})
}
// 100 addresses
randAddrs := randNetAddressPairs(t, 100)
// Create the book & populate & save
book := NewAddrBook(fname, addrBookStrict)
for _, addrSrc := range randAddrs { for _, addrSrc := range randAddrs {
book.AddAddress(addrSrc.addr, addrSrc.src) book.AddAddress(addrSrc.addr, addrSrc.src)
} }
if book.Size() != 100 {
t.Errorf("Expected 100 addresses, found %v", book.Size())
}
assert.Equal(t, 100, book.Size())
book.saveToFile(fname) book.saveToFile(fname)
// Reload the book
book = NewAddrBook(fname, addrBookStrict)
book = NewAddrBook(fname, true)
book.loadFromFile(fname) book.loadFromFile(fname)
// Test ...
assert.Equal(t, 100, book.Size())
}
if book.Size() != 100 {
t.Errorf("Expected 100 addresses, found %v", book.Size())
}
func TestAddrBookLookup(t *testing.T) {
fname := createTempFileName("addrbook_test")
randAddrs := randNetAddressPairs(t, 100)
book := NewAddrBook(fname, true)
for _, addrSrc := range randAddrs { for _, addrSrc := range randAddrs {
addr := addrSrc.addr addr := addrSrc.addr
src := addrSrc.src src := addrSrc.src
book.AddAddress(addr, src)
ka := book.addrLookup[addr.String()] ka := book.addrLookup[addr.String()]
if ka == nil {
t.Fatalf("Expected to find KnownAddress %v but wasn't there.", addr)
}
assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr)
if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) { if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) {
t.Fatalf("KnownAddress doesn't match addr & src") t.Fatalf("KnownAddress doesn't match addr & src")
} }
} }
} }
func TestPromoteToOld(t *testing.T) {
func TestAddrBookPromoteToOld(t *testing.T) {
fname := createTempFileName("addrbook_test") fname := createTempFileName("addrbook_test")
t.Logf("New tempfile name: %v", fname)
// Create some random addresses
randAddrs := []struct {
addr *NetAddress
src *NetAddress
}{}
for i := 0; i < 100; i++ {
addr := randIPv4Address()
src := randIPv4Address()
randAddrs = append(randAddrs, struct {
addr *NetAddress
src *NetAddress
}{
addr: addr,
src: src,
})
}
// Create the book & populate & save
book := NewAddrBook(fname, addrBookStrict)
randAddrs := randNetAddressPairs(t, 100)
book := NewAddrBook(fname, true)
for _, addrSrc := range randAddrs { for _, addrSrc := range randAddrs {
book.AddAddress(addrSrc.addr, addrSrc.src) book.AddAddress(addrSrc.addr, addrSrc.src)
} }
// Attempt all addresses. // Attempt all addresses.
for _, addrSrc := range randAddrs { for _, addrSrc := range randAddrs {
book.MarkAttempt(addrSrc.addr) book.MarkAttempt(addrSrc.addr)
} }
// Promote half of them // Promote half of them
for i, addrSrc := range randAddrs { for i, addrSrc := range randAddrs {
if i%2 == 0 { if i%2 == 0 {
book.MarkGood(addrSrc.addr) book.MarkGood(addrSrc.addr)
} }
} }
book.saveToFile(fname)
// Reload the book
book = NewAddrBook(fname, addrBookStrict)
book.loadFromFile(fname)
// TODO: do more testing :)
// Test ...
selection := book.GetSelection()
t.Logf("selection: %v", selection)
if book.Size() != 100 {
t.Errorf("Expected 100 addresses, found %v", book.Size())
if len(selection) > book.Size() {
t.Errorf("selection could not be bigger than the book")
} }
}
// TODO: do more testing :)
func TestAddrBookHandlesDuplicates(t *testing.T) {
fname := createTempFileName("addrbook_test")
selection := book.GetSelection()
t.Logf("selection: %v", selection)
book := NewAddrBook(fname, true)
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 *NetAddress
src *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) *NetAddress {
for {
ip := fmt.Sprintf("%v.%v.%v.%v",
rand.Intn(254)+1,
rand.Intn(255),
rand.Intn(255),
rand.Intn(255),
)
port := rand.Intn(65535-1) + 1
addr, err := NewNetAddressString(fmt.Sprintf("%v:%v", ip, port))
assert.Nil(t, err, "error generating rand network address")
if addr.Routable() {
return addr
}
}
} }

+ 5
- 1
listener.go View File

@ -70,7 +70,11 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool) Listener {
log.Info("Local listener", "ip", listenerIP, "port", listenerPort) log.Info("Local listener", "ip", listenerIP, "port", listenerPort)
// Determine internal address... // Determine internal address...
var intAddr *NetAddress = NewNetAddressString(lAddr)
var intAddr *NetAddress
intAddr, err = NewNetAddressString(lAddr)
if err != nil {
PanicCrisis(err)
}
// Determine external address... // Determine external address...
var extAddr *NetAddress var extAddr *NetAddress


+ 24
- 7
netaddress.go View File

@ -5,11 +5,12 @@
package p2p package p2p
import ( import (
"errors"
"net" "net"
"strconv" "strconv"
"time" "time"
. "github.com/tendermint/go-common"
cmn "github.com/tendermint/go-common"
) )
type NetAddress struct { type NetAddress struct {
@ -34,27 +35,43 @@ func NewNetAddress(addr net.Addr) *NetAddress {
} }
// Also resolves the host if host is not an IP. // Also resolves the host if host is not an IP.
func NewNetAddressString(addr string) *NetAddress {
func NewNetAddressString(addr string) (*NetAddress, error) {
host, portStr, err := net.SplitHostPort(addr) host, portStr, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
PanicSanity(err)
return nil, err
} }
ip := net.ParseIP(host) ip := net.ParseIP(host)
if ip == nil { if ip == nil {
if len(host) > 0 { if len(host) > 0 {
ips, err := net.LookupIP(host) ips, err := net.LookupIP(host)
if err != nil { if err != nil {
PanicSanity(err)
return nil, err
} }
ip = ips[0] ip = ips[0]
} }
} }
port, err := strconv.ParseUint(portStr, 10, 16) port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil { if err != nil {
PanicSanity(err)
return nil, err
} }
na := NewNetAddressIPPort(ip, uint16(port)) na := NewNetAddressIPPort(ip, uint16(port))
return na
return na, nil
}
func NewNetAddressStrings(addrs []string) ([]*NetAddress, error) {
netAddrs := make([]*NetAddress, len(addrs))
for i, addr := range addrs {
netAddr, err := NewNetAddressString(addr)
if err != nil {
return nil, errors.New(cmn.Fmt("Error in address %s: %v", addr, err))
}
netAddrs[i] = netAddr
}
return netAddrs, nil
} }
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress { func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
@ -81,7 +98,7 @@ func (na *NetAddress) Less(other interface{}) bool {
if o, ok := other.(*NetAddress); ok { if o, ok := other.(*NetAddress); ok {
return na.String() < o.String() return na.String() < o.String()
} else { } else {
PanicSanity("Cannot compare unequal types")
cmn.PanicSanity("Cannot compare unequal types")
return false return false
} }
} }


+ 12
- 3
pex_reactor.go View File

@ -42,12 +42,14 @@ func NewPEXReactor(book *AddrBook) *PEXReactor {
func (pexR *PEXReactor) OnStart() error { func (pexR *PEXReactor) OnStart() error {
pexR.BaseReactor.OnStart() pexR.BaseReactor.OnStart()
pexR.book.Start()
go pexR.ensurePeersRoutine() go pexR.ensurePeersRoutine()
return nil return nil
} }
func (pexR *PEXReactor) OnStop() { func (pexR *PEXReactor) OnStop() {
pexR.BaseReactor.OnStop() pexR.BaseReactor.OnStop()
pexR.book.Stop()
} }
// Implements Reactor // Implements Reactor
@ -64,7 +66,13 @@ func (pexR *PEXReactor) GetChannels() []*ChannelDescriptor {
// Implements Reactor // Implements Reactor
func (pexR *PEXReactor) AddPeer(peer *Peer) { func (pexR *PEXReactor) AddPeer(peer *Peer) {
// Add the peer to the address book // Add the peer to the address book
netAddr := NewNetAddressString(peer.ListenAddr)
netAddr, err := NewNetAddressString(peer.ListenAddr)
if err != nil {
// this should never happen
log.Error("Error in AddPeer: invalid peer address", "addr", peer.ListenAddr, "error", err)
return
}
if peer.IsOutbound() { if peer.IsOutbound() {
if pexR.book.NeedMoreAddrs() { if pexR.book.NeedMoreAddrs() {
pexR.RequestPEX(peer) pexR.RequestPEX(peer)
@ -104,12 +112,13 @@ func (pexR *PEXReactor) Receive(chID byte, src *Peer, msgBytes []byte) {
// (We don't want to get spammed with bad peers) // (We don't want to get spammed with bad peers)
srcAddr := src.Connection().RemoteAddress srcAddr := src.Connection().RemoteAddress
for _, addr := range msg.Addrs { for _, addr := range msg.Addrs {
pexR.book.AddAddress(addr, srcAddr)
if addr != nil {
pexR.book.AddAddress(addr, srcAddr)
}
} }
default: default:
log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg))) log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
} }
} }
// Asks peer for more addresses. // Asks peer for more addresses.


+ 25
- 7
switch.go View File

@ -296,20 +296,38 @@ func (sw *Switch) startInitPeer(peer *Peer) {
sw.addPeerToReactors(peer) // run AddPeer on each reactor sw.addPeerToReactors(peer) // run AddPeer on each reactor
} }
// Dial a list of seeds in random order
// Spawns a go routine for each dial
func (sw *Switch) DialSeeds(seeds []string) {
// Dial a list of seeds asynchronously in random order
func (sw *Switch) DialSeeds(addrBook *AddrBook, seeds []string) error {
netAddrs, err := NewNetAddressStrings(seeds)
if err != nil {
return err
}
if addrBook != nil {
// add seeds to `addrBook`
ourAddrS := sw.nodeInfo.ListenAddr
ourAddr, _ := NewNetAddressString(ourAddrS)
for _, netAddr := range netAddrs {
// do not add ourselves
if netAddr.Equals(ourAddr) {
continue
}
addrBook.AddAddress(netAddr, ourAddr)
}
addrBook.Save()
}
// permute the list, dial them in random order. // permute the list, dial them in random order.
perm := rand.Perm(len(seeds))
perm := rand.Perm(len(netAddrs))
for i := 0; i < len(perm); i++ { for i := 0; i < len(perm); i++ {
go func(i int) { go func(i int) {
time.Sleep(time.Duration(rand.Int63n(3000)) * time.Millisecond) time.Sleep(time.Duration(rand.Int63n(3000)) * time.Millisecond)
j := perm[i] j := perm[i]
addr := NewNetAddressString(seeds[j])
sw.dialSeed(addr)
sw.dialSeed(netAddrs[j])
}(i) }(i)
} }
return nil
} }
func (sw *Switch) dialSeed(addr *NetAddress) { func (sw *Switch) dialSeed(addr *NetAddress) {


+ 1
- 1
version.go View File

@ -1,3 +1,3 @@
package p2p package p2p
const Version = "0.3.5" // minor fixes
const Version = "0.4.0" // DialSeeds returns an error

Loading…
Cancel
Save