From 03550c7076279317bfcbeda944f757163ff19fdc Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 20 Jan 2018 19:12:04 -0500 Subject: [PATCH 1/8] wip addrbook --- p2p/{ => addrbook}/addrbook.go | 277 ++++++++-------------------- p2p/{ => addrbook}/addrbook_test.go | 2 +- p2p/addrbook/file.go | 83 +++++++++ p2p/addrbook/known_address.go | 138 ++++++++++++++ p2p/addrbook/params.go | 55 ++++++ p2p/pex_reactor.go | 6 +- p2p/switch.go | 1 - 7 files changed, 362 insertions(+), 200 deletions(-) rename p2p/{ => addrbook}/addrbook.go (75%) rename p2p/{ => addrbook}/addrbook_test.go (99%) create mode 100644 p2p/addrbook/file.go create mode 100644 p2p/addrbook/known_address.go create mode 100644 p2p/addrbook/params.go diff --git a/p2p/addrbook.go b/p2p/addrbook/addrbook.go similarity index 75% rename from p2p/addrbook.go rename to p2p/addrbook/addrbook.go index fee179cb1..1f317a2eb 100644 --- a/p2p/addrbook.go +++ b/p2p/addrbook/addrbook.go @@ -2,17 +2,15 @@ // Originally Copyright (c) 2013-2014 Conformal Systems LLC. // https://github.com/conformal/btcd/blob/master/LICENSE -package p2p +package addrbook import ( "crypto/sha256" "encoding/binary" - "encoding/json" "fmt" "math" "math/rand" "net" - "os" "sync" "time" @@ -21,64 +19,38 @@ import ( ) const ( - // addresses under which the address manager will claim to need more addresses. - needAddressThreshold = 1000 - - // interval used to dump the address cache to disk for future use. - dumpAddressInterval = time.Minute * 2 - - // max addresses in each old address bucket. - oldBucketSize = 64 - - // buckets we split old addresses over. - oldBucketCount = 64 - - // max addresses in each new address bucket. - newBucketSize = 64 - - // buckets that we spread new addresses over. - newBucketCount = 256 - - // old buckets over which an address group will be spread. - oldBucketsPerGroup = 4 - - // new buckets over which a source address group will be spread. - newBucketsPerGroup = 32 - - // buckets a frequently seen new address may end up in. - maxNewBucketsPerAddress = 4 - - // days before which we assume an address has vanished - // if we have not seen it announced in that long. - numMissingDays = 30 - - // tries without a single success before we assume an address is bad. - numRetries = 3 + bucketTypeNew = 0x01 + bucketTypeOld = 0x02 +) - // max failures we will accept without a success before considering an address bad. - maxFailures = 10 +// AddrBook is an address book used for tracking peers +// so we can gossip about them to others and select +// peers to dial. +type AddrBook interface { + cmn.Service - // days since the last success before we will consider evicting an address. - minBadDays = 7 + // Add and remove an address + AddAddress(addr *NetAddress, src *NetAddress) + RemoveAddress(addr *NetAddress) - // % of total addresses known returned by GetSelection. - getSelectionPercent = 23 + // Do we need more peers? + NeedMoreAddrs() bool - // min addresses that must be returned by GetSelection. Useful for bootstrapping. - minGetSelection = 32 + // Pick an address to dial + PickAddress(newBias int) *NetAddress - // max addresses returned by GetSelection - // NOTE: this must match "maxPexMessageSize" - maxGetSelection = 250 -) + // Mark address + MarkGood(*NetAddress) + MarkAttempt(*Address) + MarkBad(*NetAddress) -const ( - bucketTypeNew = 0x01 - bucketTypeOld = 0x02 -) + // Send a selection of addresses to peers + GetSelection() []*NetAddress +} -// AddrBook - concurrency safe peer address manager. -type AddrBook struct { +// addrBook - concurrency safe peer address manager. +// Implements AddrBook. +type addrBook struct { cmn.BaseService // immutable after creation @@ -101,7 +73,7 @@ type AddrBook struct { // NewAddrBook creates a new address book. // Use Start to begin processing asynchronous address updates. -func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook { +func NewAddrBook(filePath string, routabilityStrict bool) *addrBook { am := &AddrBook{ rand: rand.New(rand.NewSource(time.Now().UnixNano())), ourAddrs: make(map[string]*NetAddress), @@ -114,8 +86,9 @@ func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook { return am } +// Initialize the buckets. // When modifying this, don't forget to update loadFromFile() -func (a *AddrBook) init() { +func (a *addrBook) init() { a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits // New addr buckets a.bucketsNew = make([]map[string]*knownAddress, newBucketCount) @@ -130,7 +103,7 @@ func (a *AddrBook) init() { } // OnStart implements Service. -func (a *AddrBook) OnStart() error { +func (a *addrBook) OnStart() error { if err := a.BaseService.OnStart(); err != nil { return err } @@ -145,11 +118,11 @@ func (a *AddrBook) OnStart() error { } // OnStop implements Service. -func (a *AddrBook) OnStop() { +func (a *addrBook) OnStop() { a.BaseService.OnStop() } -func (a *AddrBook) Wait() { +func (a *addrBook) Wait() { a.wg.Wait() } @@ -161,46 +134,40 @@ func (a *AddrBook) AddOurAddress(addr *NetAddress) { a.ourAddrs[addr.String()] = addr } -// OurAddresses returns a list of our addresses. -func (a *AddrBook) OurAddresses() []*NetAddress { - addrs := []*NetAddress{} - for _, addr := range a.ourAddrs { - addrs = append(addrs, addr) - } - return addrs -} +//------------------------------------------------------- -// AddAddress adds the given address as received from the given source. +// AddAddress implements AddrBook - adds the given address as received from the given source. // NOTE: addr must not be nil -func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) error { +func (a *addrBook) AddAddress(addr *NetAddress, src *NetAddress) error { a.mtx.Lock() defer a.mtx.Unlock() return a.addAddress(addr, src) } -// NeedMoreAddrs returns true if there are not have enough addresses in the book. -func (a *AddrBook) NeedMoreAddrs() bool { - return a.Size() < needAddressThreshold -} - -// Size returns the number of addresses in the book. -func (a *AddrBook) Size() int { +// RemoveAddress implements AddrBook - removes the address from the book. +func (a *addrBook) RemoveAddress(addr *NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() - return a.size() + ka := a.addrLookup[addr.ID] + if ka == nil { + return + } + a.Logger.Info("Remove address from book", "addr", ka.Addr, "ID", ka.ID) + a.removeFromAllBuckets(ka) } -func (a *AddrBook) size() int { - return a.nNew + a.nOld +// NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book. +func (a *addrBook) NeedMoreAddrs() bool { + return a.Size() < needAddressThreshold } -// PickAddress picks an address to connect to. +// PickAddress implements AddrBook. It picks an address to connect to. // The address is picked randomly from an old or new bucket according // to the newBias argument, which must be between [0, 100] (or else is truncated to that range) // and determines how biased we are to pick an address from a new bucket. // PickAddress returns nil if the AddrBook is empty or if we try to pick // from an empty bucket. -func (a *AddrBook) PickAddress(newBias int) *NetAddress { +func (a *addrBook) PickAddress(newBias int) *NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -244,9 +211,9 @@ func (a *AddrBook) PickAddress(newBias int) *NetAddress { return nil } -// MarkGood marks the peer as good and moves it into an "old" bucket. -// TODO: call this from somewhere -func (a *AddrBook) MarkGood(addr *NetAddress) { +// MarkGood implements AddrBook - it marks the peer as good and +// moves it into an "old" bucket. +func (a *addrBook) MarkGood(addr *NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -259,8 +226,8 @@ func (a *AddrBook) MarkGood(addr *NetAddress) { } } -// MarkAttempt marks that an attempt was made to connect to the address. -func (a *AddrBook) MarkAttempt(addr *NetAddress) { +// MarkAttempt implements AddrBook - it marks that an attempt was made to connect to the address. +func (a *addrBook) MarkAttempt(addr *NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -270,28 +237,15 @@ func (a *AddrBook) MarkAttempt(addr *NetAddress) { ka.markAttempt() } -// MarkBad currently just ejects the address. In the future, consider -// blacklisting. -func (a *AddrBook) MarkBad(addr *NetAddress) { +// MarkBad implements AddrBook. Currently it just ejects the address. +// TODO: black list for some amount of time +func (a *addrBook) MarkBad(addr *NetAddress) { a.RemoveAddress(addr) } -// RemoveAddress removes the address from the book. -func (a *AddrBook) RemoveAddress(addr *NetAddress) { - a.mtx.Lock() - defer a.mtx.Unlock() - ka := a.addrLookup[addr.ID] - if ka == nil { - return - } - a.Logger.Info("Remove address from book", "addr", ka.Addr, "ID", ka.ID) - a.removeFromAllBuckets(ka) -} - -/* Peer exchange */ - -// GetSelection randomly selects some addresses (old & new). Suitable for peer-exchange protocols. -func (a *AddrBook) GetSelection() []*NetAddress { +// GetSelection implements AddrBook. +// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. +func (a *addrBook) GetSelection() []*NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -336,18 +290,6 @@ func (a *AddrBook) ListOfKnownAddresses() []*knownAddress { return addrs } -func (ka *knownAddress) copy() *knownAddress { - return &knownAddress{ - Addr: ka.Addr, - Src: ka.Src, - Attempts: ka.Attempts, - LastAttempt: ka.LastAttempt, - LastSuccess: ka.LastSuccess, - BucketType: ka.BucketType, - Buckets: ka.Buckets, - } -} - /* Loading & Saving */ type addrBookJSON struct { @@ -357,81 +299,24 @@ type addrBookJSON struct { func (a *AddrBook) saveToFile(filePath string) { a.Logger.Info("Saving AddrBook to file", "size", a.Size()) - - a.mtx.Lock() - defer a.mtx.Unlock() - // Compile Addrs - addrs := []*knownAddress{} - for _, ka := range a.addrLookup { - addrs = append(addrs, ka) - } - - aJSON := &addrBookJSON{ - Key: a.key, - Addrs: addrs, - } - - jsonBytes, err := json.MarshalIndent(aJSON, "", "\t") - if err != nil { - a.Logger.Error("Failed to save AddrBook to file", "err", err) - return - } - err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644) - if err != nil { - a.Logger.Error("Failed to save AddrBook to file", "file", filePath, "err", err) - } } -// Returns false if file does not exist. -// cmn.Panics if file is corrupt. -func (a *AddrBook) loadFromFile(filePath string) bool { - // If doesn't exist, do nothing. - _, err := os.Stat(filePath) - if os.IsNotExist(err) { - return false - } - - // Load addrBookJSON{} - r, err := os.Open(filePath) - if err != nil { - cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err)) - } - defer r.Close() // nolint: errcheck - aJSON := &addrBookJSON{} - dec := json.NewDecoder(r) - err = dec.Decode(aJSON) - if err != nil { - cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err)) - } +//------------------------------------------------ - // Restore all the fields... - // Restore the key - a.key = aJSON.Key - // Restore .bucketsNew & .bucketsOld - for _, ka := range aJSON.Addrs { - for _, bucketIndex := range ka.Buckets { - bucket := a.getBucket(ka.BucketType, bucketIndex) - bucket[ka.Addr.String()] = ka - } - a.addrLookup[ka.ID()] = ka - if ka.BucketType == bucketTypeNew { - a.nNew++ - } else { - a.nOld++ - } - } - return true +// Size returns the number of addresses in the book. +func (a *addrBook) Size() int { + a.mtx.Lock() + defer a.mtx.Unlock() + return a.size() } -// Save saves the book. -func (a *AddrBook) Save() { - a.Logger.Info("Saving AddrBook to file", "size", a.Size()) - a.saveToFile(a.filePath) +func (a *addrBook) size() int { + return a.nNew + a.nOld } -/* Private methods */ +//---------------------------------------------------------- -func (a *AddrBook) saveRoutine() { +func (a *addrBook) saveRoutine() { defer a.wg.Done() saveFileTicker := time.NewTicker(dumpAddressInterval) @@ -449,7 +334,7 @@ out: a.Logger.Info("Address handler done") } -func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress { +func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress { switch bucketType { case bucketTypeNew: return a.bucketsNew[bucketIdx] @@ -463,7 +348,7 @@ func (a *AddrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAd // Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full. // NOTE: currently it always returns true. -func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { +func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { // Sanity check if ka.isOld() { a.Logger.Error(cmn.Fmt("Cannot add address already in old bucket to a new bucket: %v", ka)) @@ -497,7 +382,7 @@ func (a *AddrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { } // Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full. -func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool { +func (a *addrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool { // Sanity check if ka.isNew() { a.Logger.Error(cmn.Fmt("Cannot add new address to old bucket: %v", ka)) @@ -533,7 +418,7 @@ func (a *AddrBook) addToOldBucket(ka *knownAddress, bucketIdx int) bool { return true } -func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) { +func (a *addrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx int) { if ka.BucketType != bucketType { a.Logger.Error(cmn.Fmt("Bucket type mismatch: %v", ka)) return @@ -550,7 +435,7 @@ func (a *AddrBook) removeFromBucket(ka *knownAddress, bucketType byte, bucketIdx } } -func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) { +func (a *addrBook) removeFromAllBuckets(ka *knownAddress) { for _, bucketIdx := range ka.Buckets { bucket := a.getBucket(ka.BucketType, bucketIdx) delete(bucket, ka.Addr.String()) @@ -564,7 +449,7 @@ func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) { delete(a.addrLookup, ka.ID()) } -func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { +func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { bucket := a.getBucket(bucketType, bucketIdx) var oldest *knownAddress for _, ka := range bucket { @@ -575,7 +460,7 @@ func (a *AddrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { return oldest } -func (a *AddrBook) addAddress(addr, src *NetAddress) error { +func (a *addrBook) addAddress(addr, src *NetAddress) error { if a.routabilityStrict && !addr.Routable() { return fmt.Errorf("Cannot add non-routable address %v", addr) } @@ -613,7 +498,7 @@ func (a *AddrBook) addAddress(addr, src *NetAddress) error { // Make space in the new buckets by expiring the really bad entries. // If no bad entries are available we remove the oldest. -func (a *AddrBook) expireNew(bucketIdx int) { +func (a *addrBook) expireNew(bucketIdx int) { for addrStr, ka := range a.bucketsNew[bucketIdx] { // If an entry is bad, throw it away if ka.isBad() { @@ -631,7 +516,7 @@ func (a *AddrBook) expireNew(bucketIdx int) { // Promotes an address from new to old. // TODO: Move to old probabilistically. // The better a node is, the less likely it should be evicted from an old bucket. -func (a *AddrBook) moveToOld(ka *knownAddress) { +func (a *addrBook) moveToOld(ka *knownAddress) { // Sanity check if ka.isOld() { a.Logger.Error(cmn.Fmt("Cannot promote address that is already old %v", ka)) @@ -676,7 +561,7 @@ func (a *AddrBook) moveToOld(ka *knownAddress) { // doublesha256( key + sourcegroup + // int64(doublesha256(key + group + sourcegroup))%bucket_per_group ) % num_new_buckets -func (a *AddrBook) calcNewBucket(addr, src *NetAddress) int { +func (a *addrBook) calcNewBucket(addr, src *NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(a.groupKey(addr))...) @@ -697,7 +582,7 @@ func (a *AddrBook) calcNewBucket(addr, src *NetAddress) int { // doublesha256( key + group + // int64(doublesha256(key + addr))%buckets_per_group ) % num_old_buckets -func (a *AddrBook) calcOldBucket(addr *NetAddress) int { +func (a *addrBook) calcOldBucket(addr *NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(addr.String())...) @@ -719,7 +604,7 @@ func (a *AddrBook) calcOldBucket(addr *NetAddress) int { // This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string // "local" for a local address and the string "unroutable" for an unroutable // address. -func (a *AddrBook) groupKey(na *NetAddress) string { +func (a *addrBook) groupKey(na *NetAddress) string { if a.routabilityStrict && na.Local() { return "local" } diff --git a/p2p/addrbook_test.go b/p2p/addrbook/addrbook_test.go similarity index 99% rename from p2p/addrbook_test.go rename to p2p/addrbook/addrbook_test.go index 00051ae1f..ff8d239d0 100644 --- a/p2p/addrbook_test.go +++ b/p2p/addrbook/addrbook_test.go @@ -1,4 +1,4 @@ -package p2p +package addrbook import ( "encoding/hex" diff --git a/p2p/addrbook/file.go b/p2p/addrbook/file.go new file mode 100644 index 000000000..956ac56c1 --- /dev/null +++ b/p2p/addrbook/file.go @@ -0,0 +1,83 @@ +package addrbook + +import ( + "encoding/json" + "os" + + cmn "github.com/tendermint/tmlibs/common" +) + +/* Loading & Saving */ + +type addrBookJSON struct { + Key string + Addrs []*knownAddress +} + +func (a *addrBook) saveToFile(filePath string) { + a.Logger.Info("Saving AddrBook to file", "size", a.Size()) + + a.mtx.Lock() + defer a.mtx.Unlock() + // Compile Addrs + addrs := []*knownAddress{} + for _, ka := range a.addrLookup { + addrs = append(addrs, ka) + } + + aJSON := &addrBookJSON{ + Key: a.key, + Addrs: addrs, + } + + jsonBytes, err := json.MarshalIndent(aJSON, "", "\t") + if err != nil { + a.Logger.Error("Failed to save AddrBook to file", "err", err) + return + } + err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644) + if err != nil { + a.Logger.Error("Failed to save AddrBook to file", "file", filePath, "err", err) + } +} + +// Returns false if file does not exist. +// cmn.Panics if file is corrupt. +func (a *addrBook) loadFromFile(filePath string) bool { + // If doesn't exist, do nothing. + _, err := os.Stat(filePath) + if os.IsNotExist(err) { + return false + } + + // Load addrBookJSON{} + r, err := os.Open(filePath) + if err != nil { + cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err)) + } + defer r.Close() // nolint: errcheck + aJSON := &addrBookJSON{} + dec := json.NewDecoder(r) + err = dec.Decode(aJSON) + if err != nil { + cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err)) + } + + // Restore all the fields... + // Restore the key + a.key = aJSON.Key + // Restore .bucketsNew & .bucketsOld + for _, ka := range aJSON.Addrs { + for _, bucketIndex := range ka.Buckets { + bucket := a.getBucket(ka.BucketType, bucketIndex) + bucket[ka.Addr.String()] = ka + } + a.addrLookup[ka.ID()] = ka + if ka.BucketType == bucketTypeNew { + a.nNew++ + } else { + a.nOld++ + } + } + return true +} diff --git a/p2p/addrbook/known_address.go b/p2p/addrbook/known_address.go new file mode 100644 index 000000000..2a879081a --- /dev/null +++ b/p2p/addrbook/known_address.go @@ -0,0 +1,138 @@ +package addrbook + +import "time" + +// knownAddress tracks information about a known network address +// that is used to determine how viable an address is. +type knownAddress struct { + Addr *NetAddress + Src *NetAddress + Attempts int32 + LastAttempt time.Time + LastSuccess time.Time + BucketType byte + Buckets []int +} + +func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress { + return &knownAddress{ + Addr: addr, + Src: src, + Attempts: 0, + LastAttempt: time.Now(), + BucketType: bucketTypeNew, + Buckets: nil, + } +} + +func (ka *knownAddress) ID() ID { + return ka.Addr.ID +} + +func (ka *knownAddress) copy() *knownAddress { + return &knownAddress{ + Addr: ka.Addr, + Src: ka.Src, + Attempts: ka.Attempts, + LastAttempt: ka.LastAttempt, + LastSuccess: ka.LastSuccess, + BucketType: ka.BucketType, + Buckets: ka.Buckets, + } +} + +func (ka *knownAddress) isOld() bool { + return ka.BucketType == bucketTypeOld +} + +func (ka *knownAddress) isNew() bool { + return ka.BucketType == bucketTypeNew +} + +func (ka *knownAddress) markAttempt() { + now := time.Now() + ka.LastAttempt = now + ka.Attempts += 1 +} + +func (ka *knownAddress) markGood() { + now := time.Now() + ka.LastAttempt = now + ka.Attempts = 0 + ka.LastSuccess = now +} + +func (ka *knownAddress) addBucketRef(bucketIdx int) int { + for _, bucket := range ka.Buckets { + if bucket == bucketIdx { + // TODO refactor to return error? + // log.Warn(Fmt("Bucket already exists in ka.Buckets: %v", ka)) + return -1 + } + } + ka.Buckets = append(ka.Buckets, bucketIdx) + return len(ka.Buckets) +} + +func (ka *knownAddress) removeBucketRef(bucketIdx int) int { + buckets := []int{} + for _, bucket := range ka.Buckets { + if bucket != bucketIdx { + buckets = append(buckets, bucket) + } + } + if len(buckets) != len(ka.Buckets)-1 { + // TODO refactor to return error? + // log.Warn(Fmt("bucketIdx not found in ka.Buckets: %v", ka)) + return -1 + } + ka.Buckets = buckets + return len(ka.Buckets) +} + +/* + An address is bad if the address in question is a New address, has not been tried in the last + minute, and meets one of the following criteria: + + 1) It claims to be from the future + 2) It hasn't been seen in over a week + 3) It has failed at least three times and never succeeded + 4) It has failed ten times in the last week + + All addresses that meet these criteria are assumed to be worthless and not + worth keeping hold of. + + XXX: so a good peer needs us to call MarkGood before the conditions above are reached! +*/ +func (ka *knownAddress) isBad() bool { + // Is Old --> good + if ka.BucketType == bucketTypeOld { + return false + } + + // Has been attempted in the last minute --> good + if ka.LastAttempt.Before(time.Now().Add(-1 * time.Minute)) { + return false + } + + // Too old? + // XXX: does this mean if we've kept a connection up for this long we'll disconnect?! + // and shouldn't it be .Before ? + if ka.LastAttempt.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { + return true + } + + // Never succeeded? + if ka.LastSuccess.IsZero() && ka.Attempts >= numRetries { + return true + } + + // Hasn't succeeded in too long? + // XXX: does this mean if we've kept a connection up for this long we'll disconnect?! + if ka.LastSuccess.Before(time.Now().Add(-1*minBadDays*time.Hour*24)) && + ka.Attempts >= maxFailures { + return true + } + + return false +} diff --git a/p2p/addrbook/params.go b/p2p/addrbook/params.go new file mode 100644 index 000000000..f410ed9af --- /dev/null +++ b/p2p/addrbook/params.go @@ -0,0 +1,55 @@ +package addrbook + +import "time" + +const ( + // addresses under which the address manager will claim to need more addresses. + needAddressThreshold = 1000 + + // interval used to dump the address cache to disk for future use. + dumpAddressInterval = time.Minute * 2 + + // max addresses in each old address bucket. + oldBucketSize = 64 + + // buckets we split old addresses over. + oldBucketCount = 64 + + // max addresses in each new address bucket. + newBucketSize = 64 + + // buckets that we spread new addresses over. + newBucketCount = 256 + + // old buckets over which an address group will be spread. + oldBucketsPerGroup = 4 + + // new buckets over which a source address group will be spread. + newBucketsPerGroup = 32 + + // buckets a frequently seen new address may end up in. + maxNewBucketsPerAddress = 4 + + // days before which we assume an address has vanished + // if we have not seen it announced in that long. + numMissingDays = 7 + + // tries without a single success before we assume an address is bad. + numRetries = 3 + + // max failures we will accept without a success before considering an address bad. + maxFailures = 10 // ? + + // days since the last success before we will consider evicting an address. + minBadDays = 7 + + // % of total addresses known returned by GetSelection. + getSelectionPercent = 23 + + // min addresses that must be returned by GetSelection. Useful for bootstrapping. + minGetSelection = 32 + + // max addresses returned by GetSelection + // NOTE: this must match "maxPexMessageSize" + maxGetSelection = 250 +) diff --git a/p2p/pex_reactor.go b/p2p/pex_reactor.go index bd19ab3b5..57665e073 100644 --- a/p2p/pex_reactor.go +++ b/p2p/pex_reactor.go @@ -11,6 +11,8 @@ import ( "github.com/pkg/errors" wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" + + "github.com/tendermint/tendermint/p2p/addrbook" ) const ( @@ -47,7 +49,7 @@ const ( type PEXReactor struct { BaseReactor - book *AddrBook + book *addrbook.AddrBook config *PEXReactorConfig ensurePeersPeriod time.Duration @@ -67,7 +69,7 @@ type PEXReactorConfig struct { } // NewPEXReactor creates new PEX reactor. -func NewPEXReactor(b *AddrBook, config *PEXReactorConfig) *PEXReactor { +func NewPEXReactor(b *addrbook.AddrBook, config *PEXReactorConfig) *PEXReactor { r := &PEXReactor{ book: b, config: config, diff --git a/p2p/switch.go b/p2p/switch.go index 3f026556a..db2e7d980 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -332,7 +332,6 @@ func (sw *Switch) DialPeersAsync(addrBook *AddrBook, peers []string, persistent } addrBook.AddAddress(netAddr, ourAddr) } - addrBook.Save() } // permute the list, dial them in random order. From 5b5cbaa66a4b036abdec06015a71bc1ebf9c059d Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 20 Jan 2018 21:12:04 -0500 Subject: [PATCH 2/8] p2p: use sub dirs --- p2p/base_reactor.go | 9 +- p2p/listener.go | 27 +-- p2p/peer.go | 55 ++--- p2p/peer_set.go | 16 +- p2p/peer_set_test.go | 5 +- p2p/peer_test.go | 18 +- p2p/{addrbook => pex}/addrbook.go | 241 ++++++--------------- p2p/{addrbook => pex}/addrbook_test.go | 15 +- p2p/{addrbook => pex}/file.go | 2 +- p2p/{addrbook => pex}/known_address.go | 16 +- p2p/{addrbook => pex}/params.go | 2 +- p2p/{ => pex}/pex_reactor.go | 46 ++-- p2p/{ => pex}/pex_reactor_test.go | 111 ++++------ p2p/switch.go | 46 ++-- p2p/switch_test.go | 40 ++-- p2p/test_util.go | 48 +++- p2p/{ => tmconn}/conn_go110.go | 4 +- p2p/{ => tmconn}/conn_notgo110.go | 4 +- p2p/{ => tmconn}/connection.go | 16 +- p2p/{ => tmconn}/connection_test.go | 14 +- p2p/{ => tmconn}/secret_connection.go | 2 +- p2p/{ => tmconn}/secret_connection_test.go | 2 +- p2p/types/errors.go | 20 ++ p2p/{ => types}/key.go | 2 +- p2p/{ => types}/key_test.go | 2 +- p2p/{ => types}/netaddress.go | 2 +- p2p/{ => types}/netaddress_test.go | 2 +- p2p/{types.go => types/node_info.go} | 6 +- 28 files changed, 366 insertions(+), 407 deletions(-) rename p2p/{addrbook => pex}/addrbook.go (76%) rename p2p/{addrbook => pex}/addrbook_test.go (93%) rename p2p/{addrbook => pex}/file.go (99%) rename p2p/{addrbook => pex}/known_address.go (92%) rename p2p/{addrbook => pex}/params.go (98%) rename p2p/{ => pex}/pex_reactor.go (92%) rename p2p/{ => pex}/pex_reactor_test.go (76%) rename p2p/{ => tmconn}/conn_go110.go (86%) rename p2p/{ => tmconn}/conn_notgo110.go (93%) rename p2p/{ => tmconn}/connection.go (98%) rename p2p/{ => tmconn}/connection_test.go (97%) rename p2p/{ => tmconn}/secret_connection.go (99%) rename p2p/{ => tmconn}/secret_connection_test.go (99%) create mode 100644 p2p/types/errors.go rename p2p/{ => types}/key.go (99%) rename p2p/{ => types}/key_test.go (98%) rename p2p/{ => types}/netaddress.go (99%) rename p2p/{ => types}/netaddress_test.go (99%) rename p2p/{types.go => types/node_info.go} (97%) diff --git a/p2p/base_reactor.go b/p2p/base_reactor.go index e8107d730..a24a7629b 100644 --- a/p2p/base_reactor.go +++ b/p2p/base_reactor.go @@ -1,12 +1,15 @@ package p2p -import cmn "github.com/tendermint/tmlibs/common" +import ( + "github.com/tendermint/tendermint/p2p/tmconn" + cmn "github.com/tendermint/tmlibs/common" +) type Reactor interface { cmn.Service // Start, Stop SetSwitch(*Switch) - GetChannels() []*ChannelDescriptor + GetChannels() []*tmconn.ChannelDescriptor AddPeer(peer Peer) RemovePeer(peer Peer, reason interface{}) Receive(chID byte, peer Peer, msgBytes []byte) // CONTRACT: msgBytes are not nil @@ -29,7 +32,7 @@ func NewBaseReactor(name string, impl Reactor) *BaseReactor { func (br *BaseReactor) SetSwitch(sw *Switch) { br.Switch = sw } -func (_ *BaseReactor) GetChannels() []*ChannelDescriptor { return nil } +func (_ *BaseReactor) GetChannels() []*tmconn.ChannelDescriptor { return nil } func (_ *BaseReactor) AddPeer(peer Peer) {} func (_ *BaseReactor) RemovePeer(peer Peer, reason interface{}) {} func (_ *BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {} diff --git a/p2p/listener.go b/p2p/listener.go index 884c45ee8..01d718330 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -6,6 +6,7 @@ import ( "strconv" "time" + "github.com/tendermint/tendermint/p2p/types" "github.com/tendermint/tendermint/p2p/upnp" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" @@ -13,8 +14,8 @@ import ( type Listener interface { Connections() <-chan net.Conn - InternalAddress() *NetAddress - ExternalAddress() *NetAddress + InternalAddress() *types.NetAddress + ExternalAddress() *types.NetAddress String() string Stop() error } @@ -24,8 +25,8 @@ type DefaultListener struct { cmn.BaseService listener net.Listener - intAddr *NetAddress - extAddr *NetAddress + intAddr *types.NetAddress + extAddr *types.NetAddress connections chan net.Conn } @@ -71,14 +72,14 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log logger.Info("Local listener", "ip", listenerIP, "port", listenerPort) // Determine internal address... - var intAddr *NetAddress - intAddr, err = NewNetAddressString(lAddr) + var intAddr *types.NetAddress + intAddr, err = types.NewNetAddressString(lAddr) if err != nil { panic(err) } // Determine external address... - var extAddr *NetAddress + var extAddr *types.NetAddress if !skipUPNP { // If the lAddrIP is INADDR_ANY, try UPnP if lAddrIP == "" || lAddrIP == "0.0.0.0" { @@ -151,11 +152,11 @@ func (l *DefaultListener) Connections() <-chan net.Conn { return l.connections } -func (l *DefaultListener) InternalAddress() *NetAddress { +func (l *DefaultListener) InternalAddress() *types.NetAddress { return l.intAddr } -func (l *DefaultListener) ExternalAddress() *NetAddress { +func (l *DefaultListener) ExternalAddress() *types.NetAddress { return l.extAddr } @@ -172,7 +173,7 @@ func (l *DefaultListener) String() string { /* external address helpers */ // UPNP external address discovery & port mapping -func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *NetAddress { +func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *types.NetAddress { logger.Info("Getting UPNP external address") nat, err := upnp.Discover() if err != nil { @@ -198,11 +199,11 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) * } logger.Info("Got UPNP external address", "address", ext) - return NewNetAddressIPPort(ext, uint16(externalPort)) + return types.NewNetAddressIPPort(ext, uint16(externalPort)) } // TODO: use syscalls: see issue #712 -func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *NetAddress { +func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *types.NetAddress { addrs, err := net.InterfaceAddrs() if err != nil { panic(cmn.Fmt("Could not fetch interface addresses: %v", err)) @@ -217,7 +218,7 @@ func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) * if v4 == nil || (!settleForLocal && v4[0] == 127) { continue } // loopback - return NewNetAddressIPPort(ipnet.IP, uint16(port)) + return types.NewNetAddressIPPort(ipnet.IP, uint16(port)) } // try again, but settle for local diff --git a/p2p/peer.go b/p2p/peer.go index 596b92168..17c5861f6 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -11,17 +11,20 @@ import ( wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" ) // Peer is an interface representing a peer connected on a reactor. type Peer interface { cmn.Service - ID() ID // peer's cryptographic ID - IsOutbound() bool // did we dial the peer - IsPersistent() bool // do we redial this peer when we disconnect - NodeInfo() NodeInfo // peer's info - Status() ConnectionStatus + ID() types.ID // peer's cryptographic ID + IsOutbound() bool // did we dial the peer + IsPersistent() bool // do we redial this peer when we disconnect + NodeInfo() types.NodeInfo // peer's info + Status() tmconn.ConnectionStatus Send(byte, interface{}) bool TrySend(byte, interface{}) bool @@ -40,13 +43,13 @@ type peer struct { outbound bool - conn net.Conn // source connection - mconn *MConnection // multiplex connection + conn net.Conn // source connection + mconn *tmconn.MConnection // multiplex connection persistent bool config *PeerConfig - nodeInfo NodeInfo + nodeInfo types.NodeInfo Data *cmn.CMap // User data. } @@ -58,7 +61,7 @@ type PeerConfig struct { HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"` DialTimeout time.Duration `mapstructure:"dial_timeout"` - MConfig *MConnConfig `mapstructure:"connection"` + MConfig *tmconn.MConnConfig `mapstructure:"connection"` Fuzz bool `mapstructure:"fuzz"` // fuzz connection (for testing) FuzzConfig *FuzzConnConfig `mapstructure:"fuzz_config"` @@ -70,13 +73,13 @@ func DefaultPeerConfig() *PeerConfig { AuthEnc: true, HandshakeTimeout: 20, // * time.Second, DialTimeout: 3, // * time.Second, - MConfig: DefaultMConnConfig(), + MConfig: tmconn.DefaultMConnConfig(), Fuzz: false, FuzzConfig: DefaultFuzzConnConfig(), } } -func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, +func newOutboundPeer(addr *types.NetAddress, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig, persistent bool) (*peer, error) { conn, err := dial(addr, config) @@ -96,7 +99,7 @@ func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs [] return peer, nil } -func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, +func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig) (*peer, error) { // TODO: issue PoW challenge @@ -104,7 +107,7 @@ func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*Cha return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config) } -func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, +func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig) (*peer, error) { conn := rawConn @@ -122,7 +125,7 @@ func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[ } var err error - conn, err = MakeSecretConnection(conn, ourNodePrivKey) + conn, err = tmconn.MakeSecretConnection(conn, ourNodePrivKey) if err != nil { return nil, errors.Wrap(err, "Error creating peer") } @@ -171,8 +174,8 @@ func (p *peer) OnStop() { // Implements Peer // ID returns the peer's ID - the hex encoded hash of its pubkey. -func (p *peer) ID() ID { - return PubKeyToID(p.PubKey()) +func (p *peer) ID() types.ID { + return types.PubKeyToID(p.PubKey()) } // IsOutbound returns true if the connection is outbound, false otherwise. @@ -186,12 +189,12 @@ func (p *peer) IsPersistent() bool { } // NodeInfo returns a copy of the peer's NodeInfo. -func (p *peer) NodeInfo() NodeInfo { +func (p *peer) NodeInfo() types.NodeInfo { return p.nodeInfo } // Status returns the peer's ConnectionStatus. -func (p *peer) Status() ConnectionStatus { +func (p *peer) Status() tmconn.ConnectionStatus { return p.mconn.Status() } @@ -236,13 +239,13 @@ func (p *peer) CloseConn() { // HandshakeTimeout performs the Tendermint P2P handshake between a given node and the peer // by exchanging their NodeInfo. It sets the received nodeInfo on the peer. // NOTE: blocking -func (p *peer) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) error { +func (p *peer) HandshakeTimeout(ourNodeInfo types.NodeInfo, timeout time.Duration) error { // Set deadline for handshake so we don't block forever on conn.ReadFull if err := p.conn.SetDeadline(time.Now().Add(timeout)); err != nil { return errors.Wrap(err, "Error setting deadline") } - var peerNodeInfo NodeInfo + var peerNodeInfo types.NodeInfo var err1 error var err2 error cmn.Parallel( @@ -252,7 +255,7 @@ func (p *peer) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) err }, func() { var n int - wire.ReadBinary(&peerNodeInfo, p.conn, maxNodeInfoSize, &n, &err2) + wire.ReadBinary(&peerNodeInfo, p.conn, types.MaxNodeInfoSize(), &n, &err2) p.Logger.Info("Peer handshake", "peerNodeInfo", peerNodeInfo) }) if err1 != nil { @@ -283,7 +286,7 @@ func (p *peer) PubKey() crypto.PubKey { if !p.nodeInfo.PubKey.Empty() { return p.nodeInfo.PubKey } else if p.config.AuthEnc { - return p.conn.(*SecretConnection).RemotePubKey() + return p.conn.(*tmconn.SecretConnection).RemotePubKey() } panic("Attempt to get peer's PubKey before calling Handshake") } @@ -308,7 +311,7 @@ func (p *peer) String() string { //------------------------------------------------------------------ // helper funcs -func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) { +func dial(addr *types.NetAddress, config *PeerConfig) (net.Conn, error) { conn, err := addr.DialTimeout(config.DialTimeout * time.Second) if err != nil { return nil, err @@ -316,8 +319,8 @@ func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) { return conn, nil } -func createMConnection(conn net.Conn, p *peer, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, - onPeerError func(Peer, interface{}), config *MConnConfig) *MConnection { +func createMConnection(conn net.Conn, p *peer, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, + onPeerError func(Peer, interface{}), config *tmconn.MConnConfig) *tmconn.MConnection { onReceive := func(chID byte, msgBytes []byte) { reactor := reactorsByCh[chID] @@ -331,5 +334,5 @@ func createMConnection(conn net.Conn, p *peer, reactorsByCh map[byte]Reactor, ch onPeerError(p, r) } - return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config) + return tmconn.NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config) } diff --git a/p2p/peer_set.go b/p2p/peer_set.go index dc53174a1..7a0680cb7 100644 --- a/p2p/peer_set.go +++ b/p2p/peer_set.go @@ -2,12 +2,14 @@ package p2p import ( "sync" + + "github.com/tendermint/tendermint/p2p/types" ) // IPeerSet has a (immutable) subset of the methods of PeerSet. type IPeerSet interface { - Has(key ID) bool - Get(key ID) Peer + Has(key types.ID) bool + Get(key types.ID) Peer List() []Peer Size() int } @@ -18,7 +20,7 @@ type IPeerSet interface { // Iteration over the peers is super fast and thread-safe. type PeerSet struct { mtx sync.Mutex - lookup map[ID]*peerSetItem + lookup map[types.ID]*peerSetItem list []Peer } @@ -30,7 +32,7 @@ type peerSetItem struct { // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items. func NewPeerSet() *PeerSet { return &PeerSet{ - lookup: make(map[ID]*peerSetItem), + lookup: make(map[types.ID]*peerSetItem), list: make([]Peer, 0, 256), } } @@ -41,7 +43,7 @@ func (ps *PeerSet) Add(peer Peer) error { ps.mtx.Lock() defer ps.mtx.Unlock() if ps.lookup[peer.ID()] != nil { - return ErrSwitchDuplicatePeer + return types.ErrSwitchDuplicatePeer } index := len(ps.list) @@ -54,7 +56,7 @@ func (ps *PeerSet) Add(peer Peer) error { // Has returns true iff the PeerSet contains // the peer referred to by this peerKey. -func (ps *PeerSet) Has(peerKey ID) bool { +func (ps *PeerSet) Has(peerKey types.ID) bool { ps.mtx.Lock() _, ok := ps.lookup[peerKey] ps.mtx.Unlock() @@ -62,7 +64,7 @@ func (ps *PeerSet) Has(peerKey ID) bool { } // Get looks up a peer by the provided peerKey. -func (ps *PeerSet) Get(peerKey ID) Peer { +func (ps *PeerSet) Get(peerKey types.ID) Peer { ps.mtx.Lock() defer ps.mtx.Unlock() item, ok := ps.lookup[peerKey] diff --git a/p2p/peer_set_test.go b/p2p/peer_set_test.go index e906eb8e7..7d7ed1062 100644 --- a/p2p/peer_set_test.go +++ b/p2p/peer_set_test.go @@ -8,13 +8,14 @@ import ( "github.com/stretchr/testify/assert" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tendermint/p2p/types" cmn "github.com/tendermint/tmlibs/common" ) // Returns an empty dummy peer func randPeer() *peer { return &peer{ - nodeInfo: NodeInfo{ + nodeInfo: types.NodeInfo{ ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256), PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), }, @@ -119,7 +120,7 @@ func TestPeerSetAddDuplicate(t *testing.T) { // Our next procedure is to ensure that only one addition // succeeded and that the rest are each ErrSwitchDuplicatePeer. - wantErrCount, gotErrCount := n-1, errsTally[ErrSwitchDuplicatePeer] + wantErrCount, gotErrCount := n-1, errsTally[types.ErrSwitchDuplicatePeer] assert.Equal(t, wantErrCount, gotErrCount, "invalid ErrSwitchDuplicatePeer count") wantNilErrCount, gotNilErrCount := 1, errsTally[nil] diff --git a/p2p/peer_test.go b/p2p/peer_test.go index d99fff5e4..dc13cf9d6 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" ) func TestPeerBasic(t *testing.T) { @@ -80,8 +82,8 @@ func TestPeerSend(t *testing.T) { assert.True(p.Send(0x01, "Asylum")) } -func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) (*peer, error) { - chDescs := []*ChannelDescriptor{ +func createOutboundPeerAndPerformHandshake(addr *types.NetAddress, config *PeerConfig) (*peer, error) { + chDescs := []*tmconn.ChannelDescriptor{ {ID: 0x01, Priority: 1}, } reactorsByCh := map[byte]Reactor{0x01: NewTestReactor(chDescs, true)} @@ -90,7 +92,7 @@ func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) if err != nil { return nil, err } - err = p.HandshakeTimeout(NodeInfo{ + err = p.HandshakeTimeout(types.NodeInfo{ PubKey: pk.PubKey(), Moniker: "host_peer", Network: "testing", @@ -105,11 +107,11 @@ func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) type remotePeer struct { PrivKey crypto.PrivKey Config *PeerConfig - addr *NetAddress + addr *types.NetAddress quit chan struct{} } -func (p *remotePeer) Addr() *NetAddress { +func (p *remotePeer) Addr() *types.NetAddress { return p.addr } @@ -122,7 +124,7 @@ func (p *remotePeer) Start() { if e != nil { golog.Fatalf("net.Listen tcp :0: %+v", e) } - p.addr = NewNetAddress("", l.Addr()) + p.addr = types.NewNetAddress("", l.Addr()) p.quit = make(chan struct{}) go p.accept(l) } @@ -137,11 +139,11 @@ func (p *remotePeer) accept(l net.Listener) { if err != nil { golog.Fatalf("Failed to accept conn: %+v", err) } - peer, err := newInboundPeer(conn, make(map[byte]Reactor), make([]*ChannelDescriptor, 0), func(p Peer, r interface{}) {}, p.PrivKey, p.Config) + peer, err := newInboundPeer(conn, make(map[byte]Reactor), make([]*tmconn.ChannelDescriptor, 0), func(p Peer, r interface{}) {}, p.PrivKey, p.Config) if err != nil { golog.Fatalf("Failed to create a peer: %+v", err) } - err = peer.HandshakeTimeout(NodeInfo{ + err = peer.HandshakeTimeout(types.NodeInfo{ PubKey: p.PrivKey.PubKey(), Moniker: "remote_peer", Network: "testing", diff --git a/p2p/addrbook/addrbook.go b/p2p/pex/addrbook.go similarity index 76% rename from p2p/addrbook/addrbook.go rename to p2p/pex/addrbook.go index 1f317a2eb..93f352117 100644 --- a/p2p/addrbook/addrbook.go +++ b/p2p/pex/addrbook.go @@ -2,7 +2,7 @@ // Originally Copyright (c) 2013-2014 Conformal Systems LLC. // https://github.com/conformal/btcd/blob/master/LICENSE -package addrbook +package pex import ( "crypto/sha256" @@ -16,6 +16,8 @@ import ( crypto "github.com/tendermint/go-crypto" cmn "github.com/tendermint/tmlibs/common" + + "github.com/tendermint/tendermint/p2p/types" ) const ( @@ -29,25 +31,33 @@ const ( type AddrBook interface { cmn.Service + // Add our own addresses so we don't later add ourselves + AddOurAddress(*types.NetAddress) + // Add and remove an address - AddAddress(addr *NetAddress, src *NetAddress) - RemoveAddress(addr *NetAddress) + AddAddress(addr *types.NetAddress, src *types.NetAddress) error + RemoveAddress(addr *types.NetAddress) // Do we need more peers? NeedMoreAddrs() bool // Pick an address to dial - PickAddress(newBias int) *NetAddress + PickAddress(newBias int) *types.NetAddress // Mark address - MarkGood(*NetAddress) - MarkAttempt(*Address) - MarkBad(*NetAddress) + MarkGood(*types.NetAddress) + MarkAttempt(*types.NetAddress) + MarkBad(*types.NetAddress) // Send a selection of addresses to peers - GetSelection() []*NetAddress + GetSelection() []*types.NetAddress + + // TODO: remove + ListOfKnownAddresses() []*knownAddress } +var _ AddrBook = (*addrBook)(nil) + // addrBook - concurrency safe peer address manager. // Implements AddrBook. type addrBook struct { @@ -56,13 +66,13 @@ type addrBook struct { // immutable after creation filePath string routabilityStrict bool - key string + key string // random prefix for bucket placement // accessed concurrently mtx sync.Mutex rand *rand.Rand - ourAddrs map[string]*NetAddress - addrLookup map[ID]*knownAddress // new & old + ourAddrs map[string]*types.NetAddress + addrLookup map[types.ID]*knownAddress // new & old bucketsOld []map[string]*knownAddress bucketsNew []map[string]*knownAddress nOld int @@ -74,10 +84,10 @@ type addrBook struct { // NewAddrBook creates a new address book. // Use Start to begin processing asynchronous address updates. func NewAddrBook(filePath string, routabilityStrict bool) *addrBook { - am := &AddrBook{ - rand: rand.New(rand.NewSource(time.Now().UnixNano())), - ourAddrs: make(map[string]*NetAddress), - addrLookup: make(map[ID]*knownAddress), + am := &addrBook{ + rand: rand.New(rand.NewSource(time.Now().UnixNano())), // TODO: seed from outside + ourAddrs: make(map[string]*types.NetAddress), + addrLookup: make(map[types.ID]*knownAddress), filePath: filePath, routabilityStrict: routabilityStrict, } @@ -126,26 +136,26 @@ func (a *addrBook) Wait() { a.wg.Wait() } -// AddOurAddress adds another one of our addresses. -func (a *AddrBook) AddOurAddress(addr *NetAddress) { +//------------------------------------------------------- + +// AddOurAddress one of our addresses. +func (a *addrBook) AddOurAddress(addr *types.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() a.Logger.Info("Add our address to book", "addr", addr) a.ourAddrs[addr.String()] = addr } -//------------------------------------------------------- - // AddAddress implements AddrBook - adds the given address as received from the given source. // NOTE: addr must not be nil -func (a *addrBook) AddAddress(addr *NetAddress, src *NetAddress) error { +func (a *addrBook) AddAddress(addr *types.NetAddress, src *types.NetAddress) error { a.mtx.Lock() defer a.mtx.Unlock() return a.addAddress(addr, src) } // RemoveAddress implements AddrBook - removes the address from the book. -func (a *addrBook) RemoveAddress(addr *NetAddress) { +func (a *addrBook) RemoveAddress(addr *types.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -167,7 +177,7 @@ func (a *addrBook) NeedMoreAddrs() bool { // and determines how biased we are to pick an address from a new bucket. // PickAddress returns nil if the AddrBook is empty or if we try to pick // from an empty bucket. -func (a *addrBook) PickAddress(newBias int) *NetAddress { +func (a *addrBook) PickAddress(newBias int) *types.NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -213,7 +223,7 @@ func (a *addrBook) PickAddress(newBias int) *NetAddress { // MarkGood implements AddrBook - it marks the peer as good and // moves it into an "old" bucket. -func (a *addrBook) MarkGood(addr *NetAddress) { +func (a *addrBook) MarkGood(addr *types.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -227,7 +237,7 @@ func (a *addrBook) MarkGood(addr *NetAddress) { } // MarkAttempt implements AddrBook - it marks that an attempt was made to connect to the address. -func (a *addrBook) MarkAttempt(addr *NetAddress) { +func (a *addrBook) MarkAttempt(addr *types.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -239,13 +249,13 @@ func (a *addrBook) MarkAttempt(addr *NetAddress) { // MarkBad implements AddrBook. Currently it just ejects the address. // TODO: black list for some amount of time -func (a *addrBook) MarkBad(addr *NetAddress) { +func (a *addrBook) MarkBad(addr *types.NetAddress) { a.RemoveAddress(addr) } // GetSelection implements AddrBook. // It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. -func (a *addrBook) GetSelection() []*NetAddress { +func (a *addrBook) GetSelection() []*types.NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -253,7 +263,7 @@ func (a *addrBook) GetSelection() []*NetAddress { return nil } - allAddr := make([]*NetAddress, a.size()) + allAddr := make([]*types.NetAddress, a.size()) i := 0 for _, ka := range a.addrLookup { allAddr[i] = ka.Addr @@ -279,7 +289,7 @@ func (a *addrBook) GetSelection() []*NetAddress { } // ListOfKnownAddresses returns the new and old addresses. -func (a *AddrBook) ListOfKnownAddresses() []*knownAddress { +func (a *addrBook) ListOfKnownAddresses() []*knownAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -290,17 +300,6 @@ func (a *AddrBook) ListOfKnownAddresses() []*knownAddress { return addrs } -/* Loading & Saving */ - -type addrBookJSON struct { - Key string - Addrs []*knownAddress -} - -func (a *AddrBook) saveToFile(filePath string) { - a.Logger.Info("Saving AddrBook to file", "size", a.Size()) -} - //------------------------------------------------ // Size returns the number of addresses in the book. @@ -334,6 +333,8 @@ out: a.Logger.Info("Address handler done") } +//---------------------------------------------------------- + func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAddress { switch bucketType { case bucketTypeNew: @@ -365,17 +366,18 @@ func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { // Enforce max addresses. if len(bucket) > newBucketSize { - a.Logger.Info("new bucket is full, expiring old ") + a.Logger.Info("new bucket is full, expiring new") a.expireNew(bucketIdx) } // Add to bucket. bucket[addrStr] = ka + // increment nNew if the peer doesnt already exist in a bucket if ka.addBucketRef(bucketIdx) == 1 { a.nNew++ } - // Ensure in addrLookup + // Add it to addrLookup a.addrLookup[ka.ID()] = ka return true @@ -449,6 +451,8 @@ func (a *addrBook) removeFromAllBuckets(ka *knownAddress) { delete(a.addrLookup, ka.ID()) } +//---------------------------------------------------------- + func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { bucket := a.getBucket(bucketType, bucketIdx) var oldest *knownAddress @@ -460,7 +464,9 @@ func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { return oldest } -func (a *addrBook) addAddress(addr, src *NetAddress) error { +// adds the address to a "new" bucket. if its already in one, +// it only adds it probabilistically +func (a *addrBook) addAddress(addr, src *types.NetAddress) error { if a.routabilityStrict && !addr.Routable() { return fmt.Errorf("Cannot add non-routable address %v", addr) } @@ -490,7 +496,10 @@ func (a *addrBook) addAddress(addr, src *NetAddress) error { } bucket := a.calcNewBucket(addr, src) - a.addToNewBucket(ka, bucket) + added := a.addToNewBucket(ka, bucket) + if !added { + a.Logger.Info("Can't add new address, addr book is full", "address", addr, "total", a.size()) + } a.Logger.Info("Added new address", "address", addr, "total", a.size()) return nil @@ -513,9 +522,9 @@ func (a *addrBook) expireNew(bucketIdx int) { a.removeFromBucket(oldest, bucketTypeNew, bucketIdx) } -// Promotes an address from new to old. -// TODO: Move to old probabilistically. -// The better a node is, the less likely it should be evicted from an old bucket. +// Promotes an address from new to old. If the destination bucket is full, +// demote the oldest one to a "new" bucket. +// TODO: Demote more probabilistically? func (a *addrBook) moveToOld(ka *knownAddress) { // Sanity check if ka.isOld() { @@ -559,9 +568,12 @@ func (a *addrBook) moveToOld(ka *knownAddress) { } } +//--------------------------------------------------------------------- +// calculate bucket placements + // doublesha256( key + sourcegroup + // int64(doublesha256(key + group + sourcegroup))%bucket_per_group ) % num_new_buckets -func (a *addrBook) calcNewBucket(addr, src *NetAddress) int { +func (a *addrBook) calcNewBucket(addr, src *types.NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(a.groupKey(addr))...) @@ -582,7 +594,7 @@ func (a *addrBook) calcNewBucket(addr, src *NetAddress) int { // doublesha256( key + group + // int64(doublesha256(key + addr))%buckets_per_group ) % num_old_buckets -func (a *addrBook) calcOldBucket(addr *NetAddress) int { +func (a *addrBook) calcOldBucket(addr *types.NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(addr.String())...) @@ -604,7 +616,7 @@ func (a *addrBook) calcOldBucket(addr *NetAddress) int { // This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string // "local" for a local address and the string "unroutable" for an unroutable // address. -func (a *addrBook) groupKey(na *NetAddress) string { +func (a *addrBook) groupKey(na *types.NetAddress) string { if a.routabilityStrict && na.Local() { return "local" } @@ -649,137 +661,6 @@ func (a *addrBook) groupKey(na *NetAddress) string { return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String() } -//----------------------------------------------------------------------------- - -/* - knownAddress - - tracks information about a known network address that is used - to determine how viable an address is. -*/ -type knownAddress struct { - Addr *NetAddress - Src *NetAddress - Attempts int32 - LastAttempt time.Time - LastSuccess time.Time - BucketType byte - Buckets []int -} - -func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress { - return &knownAddress{ - Addr: addr, - Src: src, - Attempts: 0, - LastAttempt: time.Now(), - BucketType: bucketTypeNew, - Buckets: nil, - } -} - -func (ka *knownAddress) ID() ID { - return ka.Addr.ID -} - -func (ka *knownAddress) isOld() bool { - return ka.BucketType == bucketTypeOld -} - -func (ka *knownAddress) isNew() bool { - return ka.BucketType == bucketTypeNew -} - -func (ka *knownAddress) markAttempt() { - now := time.Now() - ka.LastAttempt = now - ka.Attempts += 1 -} - -func (ka *knownAddress) markGood() { - now := time.Now() - ka.LastAttempt = now - ka.Attempts = 0 - ka.LastSuccess = now -} - -func (ka *knownAddress) addBucketRef(bucketIdx int) int { - for _, bucket := range ka.Buckets { - if bucket == bucketIdx { - // TODO refactor to return error? - // log.Warn(Fmt("Bucket already exists in ka.Buckets: %v", ka)) - return -1 - } - } - ka.Buckets = append(ka.Buckets, bucketIdx) - return len(ka.Buckets) -} - -func (ka *knownAddress) removeBucketRef(bucketIdx int) int { - buckets := []int{} - for _, bucket := range ka.Buckets { - if bucket != bucketIdx { - buckets = append(buckets, bucket) - } - } - if len(buckets) != len(ka.Buckets)-1 { - // TODO refactor to return error? - // log.Warn(Fmt("bucketIdx not found in ka.Buckets: %v", ka)) - return -1 - } - ka.Buckets = buckets - return len(ka.Buckets) -} - -/* - An address is bad if the address in question is a New address, has not been tried in the last - minute, and meets one of the following criteria: - - 1) It claims to be from the future - 2) It hasn't been seen in over a month - 3) It has failed at least three times and never succeeded - 4) It has failed ten times in the last week - - All addresses that meet these criteria are assumed to be worthless and not - worth keeping hold of. - - XXX: so a good peer needs us to call MarkGood before the conditions above are reached! -*/ -func (ka *knownAddress) isBad() bool { - // Is Old --> good - if ka.BucketType == bucketTypeOld { - return false - } - - // Has been attempted in the last minute --> good - if ka.LastAttempt.Before(time.Now().Add(-1 * time.Minute)) { - return false - } - - // Too old? - // XXX: does this mean if we've kept a connection up for this long we'll disconnect?! - // and shouldn't it be .Before ? - if ka.LastAttempt.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { - return true - } - - // Never succeeded? - if ka.LastSuccess.IsZero() && ka.Attempts >= numRetries { - return true - } - - // Hasn't succeeded in too long? - // XXX: does this mean if we've kept a connection up for this long we'll disconnect?! - if ka.LastSuccess.Before(time.Now().Add(-1*minBadDays*time.Hour*24)) && - ka.Attempts >= maxFailures { - return true - } - - return false -} - -//----------------------------------------------------------------------------- - // doubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes. func doubleSha256(b []byte) []byte { hasher := sha256.New() diff --git a/p2p/addrbook/addrbook_test.go b/p2p/pex/addrbook_test.go similarity index 93% rename from p2p/addrbook/addrbook_test.go rename to p2p/pex/addrbook_test.go index ff8d239d0..206e3401f 100644 --- a/p2p/addrbook/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -1,4 +1,4 @@ -package addrbook +package pex import ( "encoding/hex" @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/p2p/types" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" ) @@ -168,8 +169,8 @@ func TestAddrBookHandlesDuplicates(t *testing.T) { } type netAddressPair struct { - addr *NetAddress - src *NetAddress + addr *types.NetAddress + src *types.NetAddress } func randNetAddressPairs(t *testing.T, n int) []netAddressPair { @@ -180,7 +181,7 @@ func randNetAddressPairs(t *testing.T, n int) []netAddressPair { return randAddrs } -func randIPv4Address(t *testing.T) *NetAddress { +func randIPv4Address(t *testing.T) *types.NetAddress { for { ip := fmt.Sprintf("%v.%v.%v.%v", rand.Intn(254)+1, @@ -189,9 +190,9 @@ func randIPv4Address(t *testing.T) *NetAddress { rand.Intn(255), ) port := rand.Intn(65535-1) + 1 - id := ID(hex.EncodeToString(cmn.RandBytes(IDByteLength))) - idAddr := IDAddressString(id, fmt.Sprintf("%v:%v", ip, port)) - addr, err := NewNetAddressString(idAddr) + id := types.ID(hex.EncodeToString(cmn.RandBytes(types.IDByteLength))) + idAddr := types.IDAddressString(id, fmt.Sprintf("%v:%v", ip, port)) + addr, err := types.NewNetAddressString(idAddr) assert.Nil(t, err, "error generating rand network address") if addr.Routable() { return addr diff --git a/p2p/addrbook/file.go b/p2p/pex/file.go similarity index 99% rename from p2p/addrbook/file.go rename to p2p/pex/file.go index 956ac56c1..521fcfcf7 100644 --- a/p2p/addrbook/file.go +++ b/p2p/pex/file.go @@ -1,4 +1,4 @@ -package addrbook +package pex import ( "encoding/json" diff --git a/p2p/addrbook/known_address.go b/p2p/pex/known_address.go similarity index 92% rename from p2p/addrbook/known_address.go rename to p2p/pex/known_address.go index 2a879081a..db6d021f2 100644 --- a/p2p/addrbook/known_address.go +++ b/p2p/pex/known_address.go @@ -1,12 +1,16 @@ -package addrbook +package pex -import "time" +import ( + "time" + + "github.com/tendermint/tendermint/p2p/types" +) // knownAddress tracks information about a known network address // that is used to determine how viable an address is. type knownAddress struct { - Addr *NetAddress - Src *NetAddress + Addr *types.NetAddress + Src *types.NetAddress Attempts int32 LastAttempt time.Time LastSuccess time.Time @@ -14,7 +18,7 @@ type knownAddress struct { Buckets []int } -func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress { +func newKnownAddress(addr *types.NetAddress, src *types.NetAddress) *knownAddress { return &knownAddress{ Addr: addr, Src: src, @@ -25,7 +29,7 @@ func newKnownAddress(addr *NetAddress, src *NetAddress) *knownAddress { } } -func (ka *knownAddress) ID() ID { +func (ka *knownAddress) ID() types.ID { return ka.Addr.ID } diff --git a/p2p/addrbook/params.go b/p2p/pex/params.go similarity index 98% rename from p2p/addrbook/params.go rename to p2p/pex/params.go index f410ed9af..f94e1021c 100644 --- a/p2p/addrbook/params.go +++ b/p2p/pex/params.go @@ -1,4 +1,4 @@ -package addrbook +package pex import "time" diff --git a/p2p/pex_reactor.go b/p2p/pex/pex_reactor.go similarity index 92% rename from p2p/pex_reactor.go rename to p2p/pex/pex_reactor.go index 57665e073..24c9417f7 100644 --- a/p2p/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -1,4 +1,4 @@ -package p2p +package pex import ( "bytes" @@ -12,9 +12,13 @@ import ( wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" - "github.com/tendermint/tendermint/p2p/addrbook" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" ) +type Peer = p2p.Peer + const ( // PexChannel is a channel for PEX messages PexChannel = byte(0x00) @@ -47,9 +51,9 @@ const ( // Only accept pexAddrsMsg from peers we sent a corresponding pexRequestMsg too. // Only accept one pexRequestMsg every ~defaultEnsurePeersPeriod. type PEXReactor struct { - BaseReactor + p2p.BaseReactor - book *addrbook.AddrBook + book AddrBook config *PEXReactorConfig ensurePeersPeriod time.Duration @@ -69,7 +73,7 @@ type PEXReactorConfig struct { } // NewPEXReactor creates new PEX reactor. -func NewPEXReactor(b *addrbook.AddrBook, config *PEXReactorConfig) *PEXReactor { +func NewPEXReactor(b AddrBook, config *PEXReactorConfig) *PEXReactor { r := &PEXReactor{ book: b, config: config, @@ -77,7 +81,7 @@ func NewPEXReactor(b *addrbook.AddrBook, config *PEXReactorConfig) *PEXReactor { requestsSent: cmn.NewCMap(), lastReceivedRequests: cmn.NewCMap(), } - r.BaseReactor = *NewBaseReactor("PEXReactor", r) + r.BaseReactor = *p2p.NewBaseReactor("PEXReactor", r) return r } @@ -113,8 +117,8 @@ func (r *PEXReactor) OnStop() { } // GetChannels implements Reactor -func (r *PEXReactor) GetChannels() []*ChannelDescriptor { - return []*ChannelDescriptor{ +func (r *PEXReactor) GetChannels() []*tmconn.ChannelDescriptor { + return []*tmconn.ChannelDescriptor{ { ID: PexChannel, Priority: 1, @@ -227,7 +231,7 @@ func (r *PEXReactor) RequestAddrs(p Peer) { // ReceiveAddrs adds the given addrs to the addrbook if theres an open // request for this peer and deletes the open request. // If there's no open request for the src peer, it returns an error. -func (r *PEXReactor) ReceiveAddrs(addrs []*NetAddress, src Peer) error { +func (r *PEXReactor) ReceiveAddrs(addrs []*types.NetAddress, src Peer) error { id := string(src.ID()) if !r.requestsSent.Has(id) { @@ -246,7 +250,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*NetAddress, src Peer) error { } // SendAddrs sends addrs to the peer. -func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*NetAddress) { +func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*types.NetAddress) { p.Send(PexChannel, struct{ PexMessage }{&pexAddrsMessage{Addrs: netAddrs}}) } @@ -296,7 +300,7 @@ func (r *PEXReactor) ensurePeers() { // NOTE: range here is [10, 90]. Too high ? newBias := cmn.MinInt(numOutPeers, 8)*10 + 10 - toDial := make(map[ID]*NetAddress) + toDial := make(map[types.ID]*types.NetAddress) // Try maxAttempts times to pick numToDial addresses to dial maxAttempts := numToDial * 3 for i := 0; i < maxAttempts && len(toDial) < numToDial; i++ { @@ -319,10 +323,15 @@ func (r *PEXReactor) ensurePeers() { // Dial picked addresses for _, item := range toDial { - go func(picked *NetAddress) { + go func(picked *types.NetAddress) { _, err := r.Switch.DialPeerWithAddress(picked, false) if err != nil { - r.book.MarkAttempt(picked) + // TODO: detect more "bad peer" scenarios + if _, ok := err.(types.ErrSwitchAuthenticationFailure); ok { + r.book.MarkBad(picked) + } else { + r.book.MarkAttempt(picked) + } } }(item) } @@ -351,7 +360,7 @@ func (r *PEXReactor) checkSeeds() error { if lSeeds == 0 { return nil } - _, errs := NewNetAddressStrings(r.config.Seeds) + _, errs := types.NewNetAddressStrings(r.config.Seeds) for _, err := range errs { if err != nil { return err @@ -366,9 +375,10 @@ func (r *PEXReactor) dialSeeds() { if lSeeds == 0 { return } - seedAddrs, _ := NewNetAddressStrings(r.config.Seeds) + seedAddrs, _ := types.NewNetAddressStrings(r.config.Seeds) - perm := r.Switch.rng.Perm(lSeeds) + perm := rand.Perm(lSeeds) + // perm := r.Switch.rng.Perm(lSeeds) for _, i := range perm { // dial a random seed seedAddr := seedAddrs[i] @@ -410,7 +420,7 @@ func (r *PEXReactor) crawlPeersRoutine() { // network crawling performed during seed/crawler mode. type crawlPeerInfo struct { // The listening address of a potential peer we learned about - Addr *NetAddress + Addr *types.NetAddress // The last time we attempt to reach this address LastAttempt time.Time @@ -534,7 +544,7 @@ func (m *pexRequestMessage) String() string { A message with announced peer addresses. */ type pexAddrsMessage struct { - Addrs []*NetAddress + Addrs []*types.NetAddress } func (m *pexAddrsMessage) String() string { diff --git a/p2p/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go similarity index 76% rename from p2p/pex_reactor_test.go rename to p2p/pex/pex_reactor_test.go index 44fd8b51c..439914ac8 100644 --- a/p2p/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -1,21 +1,35 @@ -package p2p +package pex import ( "fmt" "io/ioutil" - "math/rand" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" +) + +var ( + config *cfg.P2PConfig ) +func init() { + config = cfg.DefaultP2PConfig() + config.PexReactor = true +} + func TestPEXReactorBasic(t *testing.T) { assert, require := assert.New(t), require.New(t) @@ -45,7 +59,7 @@ func TestPEXReactorAddRemovePeer(t *testing.T) { r.SetLogger(log.TestingLogger()) size := book.Size() - peer := createRandomPeer(false) + peer := p2p.CreateRandomPeer(false) r.AddPeer(peer) assert.Equal(size+1, book.Size()) @@ -53,7 +67,7 @@ func TestPEXReactorAddRemovePeer(t *testing.T) { r.RemovePeer(peer, "peer not available") assert.Equal(size+1, book.Size()) - outboundPeer := createRandomPeer(true) + outboundPeer := p2p.CreateRandomPeer(true) r.AddPeer(outboundPeer) assert.Equal(size+1, book.Size(), "outbound peers should not be added to the address book") @@ -64,7 +78,7 @@ func TestPEXReactorAddRemovePeer(t *testing.T) { func TestPEXReactorRunning(t *testing.T) { N := 3 - switches := make([]*Switch, N) + switches := make([]*p2p.Switch, N) dir, err := ioutil.TempDir("", "pex_reactor") require.Nil(t, err) @@ -74,7 +88,7 @@ func TestPEXReactorRunning(t *testing.T) { // create switches for i := 0; i < N; i++ { - switches[i] = makeSwitch(config, i, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { + switches[i] = p2p.MakeSwitch(config, i, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { sw.SetLogger(log.TestingLogger().With("switch", i)) r := NewPEXReactor(book, &PEXReactorConfig{}) @@ -87,9 +101,9 @@ func TestPEXReactorRunning(t *testing.T) { // fill the address book and add listeners for _, s := range switches { - addr, _ := NewNetAddressString(s.NodeInfo().ListenAddr) + addr, _ := types.NewNetAddressString(s.NodeInfo().ListenAddr) book.AddAddress(addr, addr) - s.AddListener(NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger())) + s.AddListener(p2p.NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger())) } // start switches @@ -106,7 +120,7 @@ func TestPEXReactorRunning(t *testing.T) { } } -func assertSomePeersWithTimeout(t *testing.T, switches []*Switch, checkPeriod, timeout time.Duration) { +func assertSomePeersWithTimeout(t *testing.T, switches []*p2p.Switch, checkPeriod, timeout time.Duration) { ticker := time.NewTicker(checkPeriod) remaining := timeout for { @@ -151,13 +165,13 @@ func TestPEXReactorReceive(t *testing.T) { r := NewPEXReactor(book, &PEXReactorConfig{}) r.SetLogger(log.TestingLogger()) - peer := createRandomPeer(false) + peer := p2p.CreateRandomPeer(false) // we have to send a request to receive responses r.RequestAddrs(peer) size := book.Size() - addrs := []*NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*types.NetAddress{peer.NodeInfo().NetAddress()} msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) r.Receive(PexChannel, peer, msg) assert.Equal(size+1, book.Size()) @@ -176,14 +190,14 @@ func TestPEXReactorRequestMessageAbuse(t *testing.T) { book.SetLogger(log.TestingLogger()) r := NewPEXReactor(book, &PEXReactorConfig{}) - sw := makeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { return sw }) + sw := p2p.MakeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw }) sw.SetLogger(log.TestingLogger()) sw.AddReactor("PEX", r) r.SetSwitch(sw) r.SetLogger(log.TestingLogger()) peer := newMockPeer() - sw.peers.Add(peer) + p2p.AddPeerToSwitch(sw, peer) assert.True(sw.Peers().Has(peer.ID())) id := string(peer.ID()) @@ -215,14 +229,14 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { book.SetLogger(log.TestingLogger()) r := NewPEXReactor(book, &PEXReactorConfig{}) - sw := makeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { return sw }) + sw := p2p.MakeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw }) sw.SetLogger(log.TestingLogger()) sw.AddReactor("PEX", r) r.SetSwitch(sw) r.SetLogger(log.TestingLogger()) peer := newMockPeer() - sw.peers.Add(peer) + p2p.AddPeerToSwitch(sw, peer) assert.True(sw.Peers().Has(peer.ID())) id := string(peer.ID()) @@ -232,7 +246,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { assert.True(r.requestsSent.Has(id)) assert.True(sw.Peers().Has(peer.ID())) - addrs := []*NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*types.NetAddress{peer.NodeInfo().NetAddress()} msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) // receive some addrs. should clear the request @@ -254,7 +268,7 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { book.SetLogger(log.TestingLogger()) // 1. create seed - seed := makeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { + seed := p2p.MakeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { sw.SetLogger(log.TestingLogger()) r := NewPEXReactor(book, &PEXReactorConfig{}) @@ -263,13 +277,13 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { sw.AddReactor("pex", r) return sw }) - seed.AddListener(NewDefaultListener("tcp", seed.NodeInfo().ListenAddr, true, log.TestingLogger())) + seed.AddListener(p2p.NewDefaultListener("tcp", seed.NodeInfo().ListenAddr, true, log.TestingLogger())) err = seed.Start() require.Nil(t, err) defer seed.Stop() // 2. create usual peer - sw := makeSwitch(config, 1, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { + sw := p2p.MakeSwitch(config, 1, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { sw.SetLogger(log.TestingLogger()) r := NewPEXReactor(book, &PEXReactorConfig{Seeds: []string{seed.NodeInfo().ListenAddr}}) @@ -283,7 +297,7 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { defer sw.Stop() // 3. check that peer at least connects to seed - assertSomePeersWithTimeout(t, []*Switch{sw}, 10*time.Millisecond, 10*time.Second) + assertSomePeersWithTimeout(t, []*p2p.Switch{sw}, 10*time.Millisecond, 10*time.Second) } func TestPEXReactorCrawlStatus(t *testing.T) { @@ -297,7 +311,7 @@ func TestPEXReactorCrawlStatus(t *testing.T) { pexR := NewPEXReactor(book, &PEXReactorConfig{SeedMode: true}) // Seed/Crawler mode uses data from the Switch - makeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *Switch) *Switch { + p2p.MakeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { pexR.SetLogger(log.TestingLogger()) sw.SetLogger(log.TestingLogger().With("switch", i)) sw.AddReactor("pex", pexR) @@ -305,13 +319,13 @@ func TestPEXReactorCrawlStatus(t *testing.T) { }) // Create a peer, add it to the peer set and the addrbook. - peer := createRandomPeer(false) - pexR.Switch.peers.Add(peer) + peer := p2p.CreateRandomPeer(false) + p2p.AddPeerToSwitch(pexR.Switch, peer) addr1 := peer.NodeInfo().NetAddress() pexR.book.AddAddress(addr1, addr1) // Add a non-connected address to the book. - _, addr2 := createRoutableAddr() + _, addr2 := p2p.CreateRoutableAddr() pexR.book.AddAddress(addr2, addr1) // Get some peerInfos to crawl @@ -323,44 +337,15 @@ func TestPEXReactorCrawlStatus(t *testing.T) { // TODO: test } -func createRoutableAddr() (addr string, netAddr *NetAddress) { - for { - var err error - addr = cmn.Fmt("%X@%v.%v.%v.%v:46656", cmn.RandBytes(20), rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256) - netAddr, err = NewNetAddressString(addr) - if err != nil { - panic(err) - } - if netAddr.Routable() { - break - } - } - return -} - -func createRandomPeer(outbound bool) *peer { - addr, netAddr := createRoutableAddr() - p := &peer{ - nodeInfo: NodeInfo{ - ListenAddr: netAddr.DialString(), - PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), - }, - outbound: outbound, - mconn: &MConnection{}, - } - p.SetLogger(log.TestingLogger().With("peer", addr)) - return p -} - type mockPeer struct { *cmn.BaseService pubKey crypto.PubKey - addr *NetAddress + addr *types.NetAddress outbound, persistent bool } func newMockPeer() mockPeer { - _, netAddr := createRoutableAddr() + _, netAddr := p2p.CreateRoutableAddr() mp := mockPeer{ addr: netAddr, pubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), @@ -370,17 +355,17 @@ func newMockPeer() mockPeer { return mp } -func (mp mockPeer) ID() ID { return PubKeyToID(mp.pubKey) } +func (mp mockPeer) ID() types.ID { return types.PubKeyToID(mp.pubKey) } func (mp mockPeer) IsOutbound() bool { return mp.outbound } func (mp mockPeer) IsPersistent() bool { return mp.persistent } -func (mp mockPeer) NodeInfo() NodeInfo { - return NodeInfo{ +func (mp mockPeer) NodeInfo() types.NodeInfo { + return types.NodeInfo{ PubKey: mp.pubKey, ListenAddr: mp.addr.DialString(), } } -func (mp mockPeer) Status() ConnectionStatus { return ConnectionStatus{} } -func (mp mockPeer) Send(byte, interface{}) bool { return false } -func (mp mockPeer) TrySend(byte, interface{}) bool { return false } -func (mp mockPeer) Set(string, interface{}) {} -func (mp mockPeer) Get(string) interface{} { return nil } +func (mp mockPeer) Status() tmconn.ConnectionStatus { return tmconn.ConnectionStatus{} } +func (mp mockPeer) Send(byte, interface{}) bool { return false } +func (mp mockPeer) TrySend(byte, interface{}) bool { return false } +func (mp mockPeer) Set(string, interface{}) {} +func (mp mockPeer) Get(string) interface{} { return nil } diff --git a/p2p/switch.go b/p2p/switch.go index db2e7d980..ec54478cf 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -11,6 +11,8 @@ import ( crypto "github.com/tendermint/go-crypto" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" cmn "github.com/tendermint/tmlibs/common" ) @@ -30,10 +32,11 @@ const ( reconnectBackOffBaseSeconds = 3 ) -var ( - ErrSwitchDuplicatePeer = errors.New("Duplicate peer") - ErrSwitchConnectToSelf = errors.New("Connect to self") -) +//----------------------------------------------------------------------------- + +type AddrBook interface { + AddAddress(addr *types.NetAddress, src *types.NetAddress) +} //----------------------------------------------------------------------------- @@ -48,12 +51,12 @@ type Switch struct { peerConfig *PeerConfig listeners []Listener reactors map[string]Reactor - chDescs []*ChannelDescriptor + chDescs []*tmconn.ChannelDescriptor reactorsByCh map[byte]Reactor peers *PeerSet dialing *cmn.CMap - nodeInfo NodeInfo // our node info - nodeKey *NodeKey // our node privkey + nodeInfo types.NodeInfo // our node info + nodeKey *types.NodeKey // our node privkey filterConnByAddr func(net.Addr) error filterConnByPubKey func(crypto.PubKey) error @@ -66,7 +69,7 @@ func NewSwitch(config *cfg.P2PConfig) *Switch { config: config, peerConfig: DefaultPeerConfig(), reactors: make(map[string]Reactor), - chDescs: make([]*ChannelDescriptor, 0), + chDescs: make([]*tmconn.ChannelDescriptor, 0), reactorsByCh: make(map[byte]Reactor), peers: NewPeerSet(), dialing: cmn.NewCMap(), @@ -77,10 +80,10 @@ func NewSwitch(config *cfg.P2PConfig) *Switch { sw.rng = rand.New(rand.NewSource(cmn.RandInt64())) // TODO: collapse the peerConfig into the config ? - sw.peerConfig.MConfig.flushThrottle = time.Duration(config.FlushThrottleTimeout) * time.Millisecond + sw.peerConfig.MConfig.FlushThrottle = time.Duration(config.FlushThrottleTimeout) * time.Millisecond sw.peerConfig.MConfig.SendRate = config.SendRate sw.peerConfig.MConfig.RecvRate = config.RecvRate - sw.peerConfig.MConfig.maxMsgPacketPayloadSize = config.MaxMsgPacketPayloadSize + sw.peerConfig.MConfig.MaxMsgPacketPayloadSize = config.MaxMsgPacketPayloadSize sw.BaseService = *cmn.NewBaseService(nil, "P2P Switch", sw) return sw @@ -140,19 +143,19 @@ func (sw *Switch) IsListening() bool { // SetNodeInfo sets the switch's NodeInfo for checking compatibility and handshaking with other nodes. // NOTE: Not goroutine safe. -func (sw *Switch) SetNodeInfo(nodeInfo NodeInfo) { +func (sw *Switch) SetNodeInfo(nodeInfo types.NodeInfo) { sw.nodeInfo = nodeInfo } // NodeInfo returns the switch's NodeInfo. // NOTE: Not goroutine safe. -func (sw *Switch) NodeInfo() NodeInfo { +func (sw *Switch) NodeInfo() types.NodeInfo { return sw.nodeInfo } // SetNodeKey sets the switch's private key for authenticated encryption. // NOTE: Not goroutine safe. -func (sw *Switch) SetNodeKey(nodeKey *NodeKey) { +func (sw *Switch) SetNodeKey(nodeKey *types.NodeKey) { sw.nodeKey = nodeKey } @@ -311,13 +314,13 @@ func (sw *Switch) reconnectToPeer(peer Peer) { // Dialing // IsDialing returns true if the switch is currently dialing the given ID. -func (sw *Switch) IsDialing(id ID) bool { +func (sw *Switch) IsDialing(id types.ID) bool { return sw.dialing.Has(string(id)) } // DialPeersAsync dials a list of peers asynchronously in random order (optionally, making them persistent). -func (sw *Switch) DialPeersAsync(addrBook *AddrBook, peers []string, persistent bool) error { - netAddrs, errs := NewNetAddressStrings(peers) +func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent bool) error { + netAddrs, errs := types.NewNetAddressStrings(peers) for _, err := range errs { sw.Logger.Error("Error in peer's address", "err", err) } @@ -330,6 +333,7 @@ func (sw *Switch) DialPeersAsync(addrBook *AddrBook, peers []string, persistent if netAddr.Same(ourAddr) { continue } + // TODO: move this out of here ? addrBook.AddAddress(netAddr, ourAddr) } } @@ -353,7 +357,7 @@ func (sw *Switch) DialPeersAsync(addrBook *AddrBook, peers []string, persistent // DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects and authenticates successfully. // If `persistent == true`, the switch will always try to reconnect to this peer if the connection ever fails. -func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (Peer, error) { +func (sw *Switch) DialPeerWithAddress(addr *types.NetAddress, persistent bool) (Peer, error) { sw.dialing.Set(string(addr.ID), addr) defer sw.dialing.Delete(string(addr.ID)) return sw.addOutboundPeerWithConfig(addr, sw.peerConfig, persistent) @@ -439,7 +443,7 @@ func (sw *Switch) addInboundPeerWithConfig(conn net.Conn, config *PeerConfig) er // dial the peer; make secret connection; authenticate against the dialed ID; // add the peer. -func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig, persistent bool) (Peer, error) { +func (sw *Switch) addOutboundPeerWithConfig(addr *types.NetAddress, config *PeerConfig, persistent bool) (Peer, error) { sw.Logger.Info("Dialing peer", "address", addr) peer, err := newOutboundPeer(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, config, persistent) if err != nil { @@ -453,7 +457,7 @@ func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig peer.Logger.Info("Dialed peer with unknown ID - unable to authenticate", "addr", addr) } else if addr.ID != peer.ID() { peer.CloseConn() - return nil, fmt.Errorf("Failed to authenticate peer %v. Connected to peer with ID %s", addr, peer.ID()) + return nil, types.ErrSwitchAuthenticationFailure{addr, peer.ID()} } err = sw.addPeer(peer) @@ -474,12 +478,12 @@ func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig func (sw *Switch) addPeer(peer *peer) error { // Avoid self if sw.nodeKey.ID() == peer.ID() { - return ErrSwitchConnectToSelf + return types.ErrSwitchConnectToSelf } // Avoid duplicate if sw.peers.Has(peer.ID()) { - return ErrSwitchDuplicatePeer + return types.ErrSwitchDuplicatePeer } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index a729698e9..ae7e89e77 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -16,6 +16,8 @@ import ( "github.com/tendermint/tmlibs/log" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" ) var ( @@ -28,7 +30,7 @@ func init() { } type PeerMessage struct { - PeerID ID + PeerID types.ID Bytes []byte Counter int } @@ -37,7 +39,7 @@ type TestReactor struct { BaseReactor mtx sync.Mutex - channels []*ChannelDescriptor + channels []*tmconn.ChannelDescriptor peersAdded []Peer peersRemoved []Peer logMessages bool @@ -45,7 +47,7 @@ type TestReactor struct { msgsReceived map[byte][]PeerMessage } -func NewTestReactor(channels []*ChannelDescriptor, logMessages bool) *TestReactor { +func NewTestReactor(channels []*tmconn.ChannelDescriptor, logMessages bool) *TestReactor { tr := &TestReactor{ channels: channels, logMessages: logMessages, @@ -56,7 +58,7 @@ func NewTestReactor(channels []*ChannelDescriptor, logMessages bool) *TestReacto return tr } -func (tr *TestReactor) GetChannels() []*ChannelDescriptor { +func (tr *TestReactor) GetChannels() []*tmconn.ChannelDescriptor { return tr.channels } @@ -92,7 +94,7 @@ func (tr *TestReactor) getMsgs(chID byte) []PeerMessage { // convenience method for creating two switches connected to each other. // XXX: note this uses net.Pipe and not a proper TCP conn -func makeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switch, *Switch) { +func MakeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switch, *Switch) { // Create two switches that will be interconnected. switches := MakeConnectedSwitches(config, 2, initSwitch, Connect2Switches) return switches[0], switches[1] @@ -100,11 +102,11 @@ func makeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switc func initSwitchFunc(i int, sw *Switch) *Switch { // Make two reactors of two channels each - sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{ + sw.AddReactor("foo", NewTestReactor([]*tmconn.ChannelDescriptor{ {ID: byte(0x00), Priority: 10}, {ID: byte(0x01), Priority: 10}, }, true)) - sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{ + sw.AddReactor("bar", NewTestReactor([]*tmconn.ChannelDescriptor{ {ID: byte(0x02), Priority: 10}, {ID: byte(0x03), Priority: 10}, }, true)) @@ -112,7 +114,7 @@ func initSwitchFunc(i int, sw *Switch) *Switch { } func TestSwitches(t *testing.T) { - s1, s2 := makeSwitchPair(t, initSwitchFunc) + s1, s2 := MakeSwitchPair(t, initSwitchFunc) defer s1.Stop() defer s2.Stop() @@ -156,12 +158,12 @@ func assertMsgReceivedWithTimeout(t *testing.T, msg string, channel byte, reacto } func TestConnAddrFilter(t *testing.T) { - s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) - s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + s1 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + s2 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) defer s1.Stop() defer s2.Stop() - c1, c2 := netPipe() + c1, c2 := tmconn.NetPipe() s1.SetAddrFilter(func(addr net.Addr) error { if addr.String() == c1.RemoteAddr().String() { @@ -192,12 +194,12 @@ func assertNoPeersAfterTimeout(t *testing.T, sw *Switch, timeout time.Duration) } func TestConnPubKeyFilter(t *testing.T) { - s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) - s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + s1 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + s2 := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) defer s1.Stop() defer s2.Stop() - c1, c2 := netPipe() + c1, c2 := tmconn.NetPipe() // set pubkey filter s1.SetPubKeyFilter(func(pubkey crypto.PubKey) error { @@ -224,7 +226,7 @@ func TestConnPubKeyFilter(t *testing.T) { func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { assert, require := assert.New(t), require.New(t) - sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + sw := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() if err != nil { t.Error(err) @@ -251,7 +253,7 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { func TestSwitchReconnectsToPersistentPeer(t *testing.T) { assert, require := assert.New(t), require.New(t) - sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) + sw := MakeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() if err != nil { t.Error(err) @@ -302,13 +304,13 @@ func TestSwitchFullConnectivity(t *testing.T) { func BenchmarkSwitches(b *testing.B) { b.StopTimer() - s1, s2 := makeSwitchPair(b, func(i int, sw *Switch) *Switch { + s1, s2 := MakeSwitchPair(b, func(i int, sw *Switch) *Switch { // Make bar reactors of bar channels each - sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{ + sw.AddReactor("foo", NewTestReactor([]*tmconn.ChannelDescriptor{ {ID: byte(0x00), Priority: 10}, {ID: byte(0x01), Priority: 10}, }, false)) - sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{ + sw.AddReactor("bar", NewTestReactor([]*tmconn.ChannelDescriptor{ {ID: byte(0x02), Priority: 10}, {ID: byte(0x03), Priority: 10}, }, false)) diff --git a/p2p/test_util.go b/p2p/test_util.go index dca23a0ea..aad6fb232 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -5,11 +5,47 @@ import ( "net" crypto "github.com/tendermint/go-crypto" - cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" ) +func AddPeerToSwitch(sw *Switch, peer Peer) { + sw.peers.Add(peer) +} + +func CreateRandomPeer(outbound bool) *peer { + addr, netAddr := CreateRoutableAddr() + p := &peer{ + nodeInfo: types.NodeInfo{ + ListenAddr: netAddr.DialString(), + PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), + }, + outbound: outbound, + mconn: &tmconn.MConnection{}, + } + p.SetLogger(log.TestingLogger().With("peer", addr)) + return p +} + +func CreateRoutableAddr() (addr string, netAddr *types.NetAddress) { + for { + var err error + addr = cmn.Fmt("%X@%v.%v.%v.%v:46656", cmn.RandBytes(20), rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256) + netAddr, err = types.NewNetAddressString(addr) + if err != nil { + panic(err) + } + if netAddr.Routable() { + break + } + } + return +} + //------------------------------------------------------------------ // Connects switches via arbitrary net.Conn. Used for testing. @@ -20,7 +56,7 @@ import ( func MakeConnectedSwitches(cfg *cfg.P2PConfig, n int, initSwitch func(int, *Switch) *Switch, connect func([]*Switch, int, int)) []*Switch { switches := make([]*Switch, n) for i := 0; i < n; i++ { - switches[i] = makeSwitch(cfg, i, "testing", "123.123.123", initSwitch) + switches[i] = MakeSwitch(cfg, i, "testing", "123.123.123", initSwitch) } if err := StartSwitches(switches); err != nil { @@ -42,7 +78,7 @@ func MakeConnectedSwitches(cfg *cfg.P2PConfig, n int, initSwitch func(int, *Swit func Connect2Switches(switches []*Switch, i, j int) { switchI := switches[i] switchJ := switches[j] - c1, c2 := netPipe() + c1, c2 := tmconn.NetPipe() doneCh := make(chan struct{}) go func() { err := switchI.addPeerWithConnection(c1) @@ -91,16 +127,16 @@ func StartSwitches(switches []*Switch) error { return nil } -func makeSwitch(cfg *cfg.P2PConfig, i int, network, version string, initSwitch func(int, *Switch) *Switch) *Switch { +func MakeSwitch(cfg *cfg.P2PConfig, i int, network, version string, initSwitch func(int, *Switch) *Switch) *Switch { // new switch, add reactors // TODO: let the config be passed in? - nodeKey := &NodeKey{ + nodeKey := &types.NodeKey{ PrivKey: crypto.GenPrivKeyEd25519().Wrap(), } s := NewSwitch(cfg) s.SetLogger(log.TestingLogger()) s = initSwitch(i, s) - s.SetNodeInfo(NodeInfo{ + s.SetNodeInfo(types.NodeInfo{ PubKey: nodeKey.PubKey(), Moniker: cmn.Fmt("switch%d", i), Network: network, diff --git a/p2p/conn_go110.go b/p2p/tmconn/conn_go110.go similarity index 86% rename from p2p/conn_go110.go rename to p2p/tmconn/conn_go110.go index 2fca7c3df..75e55d855 100644 --- a/p2p/conn_go110.go +++ b/p2p/tmconn/conn_go110.go @@ -1,6 +1,6 @@ // +build go1.10 -package p2p +package tmconn // Go1.10 has a proper net.Conn implementation that // has the SetDeadline method implemented as per @@ -10,6 +10,6 @@ package p2p import "net" -func netPipe() (net.Conn, net.Conn) { +func NetPipe() (net.Conn, net.Conn) { return net.Pipe() } diff --git a/p2p/conn_notgo110.go b/p2p/tmconn/conn_notgo110.go similarity index 93% rename from p2p/conn_notgo110.go rename to p2p/tmconn/conn_notgo110.go index a5c2f7410..bb72d64a1 100644 --- a/p2p/conn_notgo110.go +++ b/p2p/tmconn/conn_notgo110.go @@ -1,6 +1,6 @@ // +build !go1.10 -package p2p +package tmconn import ( "net" @@ -24,7 +24,7 @@ func (p *pipe) SetDeadline(t time.Time) error { return nil } -func netPipe() (net.Conn, net.Conn) { +func NetPipe() (net.Conn, net.Conn) { p1, p2 := net.Pipe() return &pipe{p1}, &pipe{p2} } diff --git a/p2p/connection.go b/p2p/tmconn/connection.go similarity index 98% rename from p2p/connection.go rename to p2p/tmconn/connection.go index dcb660967..92c48c360 100644 --- a/p2p/connection.go +++ b/p2p/tmconn/connection.go @@ -1,4 +1,4 @@ -package p2p +package tmconn import ( "bufio" @@ -97,13 +97,13 @@ type MConnConfig struct { SendRate int64 `mapstructure:"send_rate"` RecvRate int64 `mapstructure:"recv_rate"` - maxMsgPacketPayloadSize int + MaxMsgPacketPayloadSize int - flushThrottle time.Duration + FlushThrottle time.Duration } func (cfg *MConnConfig) maxMsgPacketTotalSize() int { - return cfg.maxMsgPacketPayloadSize + maxMsgPacketOverheadSize + return cfg.MaxMsgPacketPayloadSize + maxMsgPacketOverheadSize } // DefaultMConnConfig returns the default config. @@ -111,8 +111,8 @@ func DefaultMConnConfig() *MConnConfig { return &MConnConfig{ SendRate: defaultSendRate, RecvRate: defaultRecvRate, - maxMsgPacketPayloadSize: defaultMaxMsgPacketPayloadSize, - flushThrottle: defaultFlushThrottle, + MaxMsgPacketPayloadSize: defaultMaxMsgPacketPayloadSize, + FlushThrottle: defaultFlushThrottle, } } @@ -171,7 +171,7 @@ func (c *MConnection) OnStart() error { return err } c.quit = make(chan struct{}) - c.flushTimer = cmn.NewThrottleTimer("flush", c.config.flushThrottle) + c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle) c.pingTimer = cmn.NewRepeatTimer("ping", pingTimeout) c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateStats) go c.sendRoutine() @@ -586,7 +586,7 @@ func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { desc: desc, sendQueue: make(chan []byte, desc.SendQueueCapacity), recving: make([]byte, 0, desc.RecvBufferCapacity), - maxMsgPacketPayloadSize: conn.config.maxMsgPacketPayloadSize, + maxMsgPacketPayloadSize: conn.config.MaxMsgPacketPayloadSize, } } diff --git a/p2p/connection_test.go b/p2p/tmconn/connection_test.go similarity index 97% rename from p2p/connection_test.go rename to p2p/tmconn/connection_test.go index 2a64764ea..65ae017cb 100644 --- a/p2p/connection_test.go +++ b/p2p/tmconn/connection_test.go @@ -1,4 +1,4 @@ -package p2p +package tmconn import ( "net" @@ -31,7 +31,7 @@ func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msg func TestMConnectionSend(t *testing.T) { assert, require := assert.New(t), require.New(t) - server, client := netPipe() + server, client := NetPipe() defer server.Close() // nolint: errcheck defer client.Close() // nolint: errcheck @@ -64,7 +64,7 @@ func TestMConnectionSend(t *testing.T) { func TestMConnectionReceive(t *testing.T) { assert, require := assert.New(t), require.New(t) - server, client := netPipe() + server, client := NetPipe() defer server.Close() // nolint: errcheck defer client.Close() // nolint: errcheck @@ -102,7 +102,7 @@ func TestMConnectionReceive(t *testing.T) { func TestMConnectionStatus(t *testing.T) { assert, require := assert.New(t), require.New(t) - server, client := netPipe() + server, client := NetPipe() defer server.Close() // nolint: errcheck defer client.Close() // nolint: errcheck @@ -119,7 +119,7 @@ func TestMConnectionStatus(t *testing.T) { func TestMConnectionStopsAndReturnsError(t *testing.T) { assert, require := assert.New(t), require.New(t) - server, client := netPipe() + server, client := NetPipe() defer server.Close() // nolint: errcheck defer client.Close() // nolint: errcheck @@ -152,7 +152,7 @@ func TestMConnectionStopsAndReturnsError(t *testing.T) { } func newClientAndServerConnsForReadErrors(require *require.Assertions, chOnErr chan struct{}) (*MConnection, *MConnection) { - server, client := netPipe() + server, client := NetPipe() onReceive := func(chID byte, msgBytes []byte) {} onError := func(r interface{}) {} @@ -283,7 +283,7 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { func TestMConnectionTrySend(t *testing.T) { assert, require := assert.New(t), require.New(t) - server, client := netPipe() + server, client := NetPipe() defer server.Close() defer client.Close() diff --git a/p2p/secret_connection.go b/p2p/tmconn/secret_connection.go similarity index 99% rename from p2p/secret_connection.go rename to p2p/tmconn/secret_connection.go index f022d9c35..e1a3e0506 100644 --- a/p2p/secret_connection.go +++ b/p2p/tmconn/secret_connection.go @@ -4,7 +4,7 @@ // is known ahead of time, and thus we are technically // still vulnerable to MITM. (TODO!) // See docs/sts-final.pdf for more info -package p2p +package tmconn import ( "bytes" diff --git a/p2p/secret_connection_test.go b/p2p/tmconn/secret_connection_test.go similarity index 99% rename from p2p/secret_connection_test.go rename to p2p/tmconn/secret_connection_test.go index 5e0611a87..5ef2c410e 100644 --- a/p2p/secret_connection_test.go +++ b/p2p/tmconn/secret_connection_test.go @@ -1,4 +1,4 @@ -package p2p +package tmconn import ( "io" diff --git a/p2p/types/errors.go b/p2p/types/errors.go new file mode 100644 index 000000000..ead2a8334 --- /dev/null +++ b/p2p/types/errors.go @@ -0,0 +1,20 @@ +package types + +import ( + "errors" + "fmt" +) + +var ( + ErrSwitchDuplicatePeer = errors.New("Duplicate peer") + ErrSwitchConnectToSelf = errors.New("Connect to self") +) + +type ErrSwitchAuthenticationFailure struct { + Dialed *NetAddress + Got ID +} + +func (e ErrSwitchAuthenticationFailure) Error() string { + return fmt.Sprintf("Failed to authenticate peer. Dialed %v, but got peer with ID %s", e.Dialed, e.Got) +} diff --git a/p2p/key.go b/p2p/types/key.go similarity index 99% rename from p2p/key.go rename to p2p/types/key.go index ea0f0b071..4ce5ee500 100644 --- a/p2p/key.go +++ b/p2p/types/key.go @@ -1,4 +1,4 @@ -package p2p +package types import ( "bytes" diff --git a/p2p/key_test.go b/p2p/types/key_test.go similarity index 98% rename from p2p/key_test.go rename to p2p/types/key_test.go index c2e1f3e0e..f18fb3b92 100644 --- a/p2p/key_test.go +++ b/p2p/types/key_test.go @@ -1,4 +1,4 @@ -package p2p +package types import ( "bytes" diff --git a/p2p/netaddress.go b/p2p/types/netaddress.go similarity index 99% rename from p2p/netaddress.go rename to p2p/types/netaddress.go index 333d16e5d..f0b397e8e 100644 --- a/p2p/netaddress.go +++ b/p2p/types/netaddress.go @@ -2,7 +2,7 @@ // Originally Copyright (c) 2013-2014 Conformal Systems LLC. // https://github.com/conformal/btcd/blob/master/LICENSE -package p2p +package types import ( "encoding/hex" diff --git a/p2p/netaddress_test.go b/p2p/types/netaddress_test.go similarity index 99% rename from p2p/netaddress_test.go rename to p2p/types/netaddress_test.go index 6c1930a2f..0119cc3b1 100644 --- a/p2p/netaddress_test.go +++ b/p2p/types/netaddress_test.go @@ -1,4 +1,4 @@ -package p2p +package types import ( "net" diff --git a/p2p/types.go b/p2p/types/node_info.go similarity index 97% rename from p2p/types.go rename to p2p/types/node_info.go index d93adc9b6..10c486852 100644 --- a/p2p/types.go +++ b/p2p/types/node_info.go @@ -1,4 +1,4 @@ -package p2p +package types import ( "fmt" @@ -11,6 +11,10 @@ import ( const maxNodeInfoSize = 10240 // 10Kb +func MaxNodeInfoSize() int { + return maxNodeInfoSize +} + // NodeInfo is the basic node information exchanged // between two peers during the Tendermint P2P handshake. type NodeInfo struct { From 0d7d16005a256e9080fd8df45e0483bfe90d18ed Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 20 Jan 2018 21:44:09 -0500 Subject: [PATCH 3/8] fixes --- node/node.go | 14 ++++++++------ p2p/switch.go | 2 +- p2p/types.go | 12 ++++++++++++ rpc/core/pipe.go | 6 +++--- 4 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 p2p/types.go diff --git a/node/node.go b/node/node.go index 3012ed057..cab8aa805 100644 --- a/node/node.go +++ b/node/node.go @@ -22,7 +22,9 @@ import ( "github.com/tendermint/tendermint/evidence" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/pex" "github.com/tendermint/tendermint/p2p/trust" + p2ptypes "github.com/tendermint/tendermint/p2p/types" "github.com/tendermint/tendermint/proxy" rpccore "github.com/tendermint/tendermint/rpc/core" grpccore "github.com/tendermint/tendermint/rpc/grpc" @@ -97,7 +99,7 @@ type Node struct { // network sw *p2p.Switch // p2p connections - addrBook *p2p.AddrBook // known peers + addrBook pex.AddrBook // known peers trustMetricStore *trust.TrustMetricStore // trust metrics for all peers // services @@ -238,10 +240,10 @@ func NewNode(config *cfg.Config, sw.AddReactor("EVIDENCE", evidenceReactor) // Optionally, start the pex reactor - var addrBook *p2p.AddrBook + var addrBook pex.AddrBook var trustMetricStore *trust.TrustMetricStore if config.P2P.PexReactor { - addrBook = p2p.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) + addrBook = pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) // Get the trust metric history data @@ -256,8 +258,8 @@ func NewNode(config *cfg.Config, if config.P2P.Seeds != "" { seeds = strings.Split(config.P2P.Seeds, ",") } - pexReactor := p2p.NewPEXReactor(addrBook, - &p2p.PEXReactorConfig{Seeds: seeds}) + pexReactor := pex.NewPEXReactor(addrBook, + &pex.PEXReactorConfig{Seeds: seeds}) pexReactor.SetLogger(p2pLogger) sw.AddReactor("PEX", pexReactor) } @@ -374,7 +376,7 @@ func (n *Node) OnStart() error { // Generate node PrivKey // TODO: pass in like priv_val - nodeKey, err := p2p.LoadOrGenNodeKey(n.config.NodeKeyFile()) + nodeKey, err := p2ptypes.LoadOrGenNodeKey(n.config.NodeKeyFile()) if err != nil { return err } diff --git a/p2p/switch.go b/p2p/switch.go index ec54478cf..c9938374c 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -35,7 +35,7 @@ const ( //----------------------------------------------------------------------------- type AddrBook interface { - AddAddress(addr *types.NetAddress, src *types.NetAddress) + AddAddress(addr *types.NetAddress, src *types.NetAddress) error } //----------------------------------------------------------------------------- diff --git a/p2p/types.go b/p2p/types.go new file mode 100644 index 000000000..db7469ec0 --- /dev/null +++ b/p2p/types.go @@ -0,0 +1,12 @@ +package p2p + +import ( + "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/types" +) + +type ID = types.ID +type NodeInfo = types.NodeInfo + +type ChannelDescriptor = tmconn.ChannelDescriptor +type ConnectionStatus = tmconn.ConnectionStatus diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 301977ac3..2edb3f3d1 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -32,7 +32,7 @@ type P2P interface { NumPeers() (outbound, inbound, dialig int) NodeInfo() p2p.NodeInfo IsListening() bool - DialPeersAsync(*p2p.AddrBook, []string, bool) error + DialPeersAsync(p2p.AddrBook, []string, bool) error } //---------------------------------------------- @@ -54,7 +54,7 @@ var ( // objects pubKey crypto.PubKey genDoc *types.GenesisDoc // cache the genesis structure - addrBook *p2p.AddrBook + addrBook p2p.AddrBook txIndexer txindex.TxIndexer consensusReactor *consensus.ConsensusReactor eventBus *types.EventBus // thread safe @@ -94,7 +94,7 @@ func SetGenesisDoc(doc *types.GenesisDoc) { genDoc = doc } -func SetAddrBook(book *p2p.AddrBook) { +func SetAddrBook(book p2p.AddrBook) { addrBook = book } From 44e967184a6f44f9d788015f265e9bb1a926688c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 21 Jan 2018 00:33:53 -0500 Subject: [PATCH 4/8] p2p: tmconn->conn and types->p2p --- node/node.go | 3 +- p2p/base_reactor.go | 6 +-- p2p/{tmconn => conn}/conn_go110.go | 2 +- p2p/{tmconn => conn}/conn_notgo110.go | 2 +- p2p/{tmconn => conn}/connection.go | 2 +- p2p/{tmconn => conn}/connection_test.go | 2 +- p2p/{tmconn => conn}/secret_connection.go | 2 +- .../secret_connection_test.go | 2 +- p2p/{types => }/errors.go | 2 +- p2p/{types => }/key.go | 2 +- p2p/{types => }/key_test.go | 2 +- p2p/listener.go | 27 +++++----- p2p/{types => }/netaddress.go | 2 +- p2p/{types => }/netaddress_test.go | 2 +- p2p/{types => }/node_info.go | 2 +- p2p/peer.go | 29 +++++----- p2p/peer_set.go | 16 +++--- p2p/peer_set_test.go | 5 +- p2p/peer_test.go | 15 +++--- p2p/pex/addrbook.go | 53 +++++++++---------- p2p/pex/addrbook_test.go | 14 ++--- p2p/pex/known_address.go | 10 ++-- p2p/pex/pex_reactor.go | 25 +++++---- p2p/pex/pex_reactor_test.go | 27 +++++----- p2p/switch.go | 33 ++++++------ p2p/switch_test.go | 23 ++++---- p2p/test_util.go | 17 +++--- p2p/types.go | 10 ++-- 28 files changed, 160 insertions(+), 177 deletions(-) rename p2p/{tmconn => conn}/conn_go110.go (96%) rename p2p/{tmconn => conn}/conn_notgo110.go (98%) rename p2p/{tmconn => conn}/connection.go (99%) rename p2p/{tmconn => conn}/connection_test.go (99%) rename p2p/{tmconn => conn}/secret_connection.go (99%) rename p2p/{tmconn => conn}/secret_connection_test.go (99%) rename p2p/{types => }/errors.go (96%) rename p2p/{types => }/key.go (99%) rename p2p/{types => }/key_test.go (98%) rename p2p/{types => }/netaddress.go (99%) rename p2p/{types => }/netaddress_test.go (99%) rename p2p/{types => }/node_info.go (99%) diff --git a/node/node.go b/node/node.go index cab8aa805..bdbf12f82 100644 --- a/node/node.go +++ b/node/node.go @@ -24,7 +24,6 @@ import ( "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p/pex" "github.com/tendermint/tendermint/p2p/trust" - p2ptypes "github.com/tendermint/tendermint/p2p/types" "github.com/tendermint/tendermint/proxy" rpccore "github.com/tendermint/tendermint/rpc/core" grpccore "github.com/tendermint/tendermint/rpc/grpc" @@ -376,7 +375,7 @@ func (n *Node) OnStart() error { // Generate node PrivKey // TODO: pass in like priv_val - nodeKey, err := p2ptypes.LoadOrGenNodeKey(n.config.NodeKeyFile()) + nodeKey, err := p2p.LoadOrGenNodeKey(n.config.NodeKeyFile()) if err != nil { return err } diff --git a/p2p/base_reactor.go b/p2p/base_reactor.go index a24a7629b..20525e675 100644 --- a/p2p/base_reactor.go +++ b/p2p/base_reactor.go @@ -1,7 +1,7 @@ package p2p import ( - "github.com/tendermint/tendermint/p2p/tmconn" + "github.com/tendermint/tendermint/p2p/conn" cmn "github.com/tendermint/tmlibs/common" ) @@ -9,7 +9,7 @@ type Reactor interface { cmn.Service // Start, Stop SetSwitch(*Switch) - GetChannels() []*tmconn.ChannelDescriptor + GetChannels() []*conn.ChannelDescriptor AddPeer(peer Peer) RemovePeer(peer Peer, reason interface{}) Receive(chID byte, peer Peer, msgBytes []byte) // CONTRACT: msgBytes are not nil @@ -32,7 +32,7 @@ func NewBaseReactor(name string, impl Reactor) *BaseReactor { func (br *BaseReactor) SetSwitch(sw *Switch) { br.Switch = sw } -func (_ *BaseReactor) GetChannels() []*tmconn.ChannelDescriptor { return nil } +func (_ *BaseReactor) GetChannels() []*conn.ChannelDescriptor { return nil } func (_ *BaseReactor) AddPeer(peer Peer) {} func (_ *BaseReactor) RemovePeer(peer Peer, reason interface{}) {} func (_ *BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {} diff --git a/p2p/tmconn/conn_go110.go b/p2p/conn/conn_go110.go similarity index 96% rename from p2p/tmconn/conn_go110.go rename to p2p/conn/conn_go110.go index 75e55d855..682188101 100644 --- a/p2p/tmconn/conn_go110.go +++ b/p2p/conn/conn_go110.go @@ -1,6 +1,6 @@ // +build go1.10 -package tmconn +package conn // Go1.10 has a proper net.Conn implementation that // has the SetDeadline method implemented as per diff --git a/p2p/tmconn/conn_notgo110.go b/p2p/conn/conn_notgo110.go similarity index 98% rename from p2p/tmconn/conn_notgo110.go rename to p2p/conn/conn_notgo110.go index bb72d64a1..ed642eb54 100644 --- a/p2p/tmconn/conn_notgo110.go +++ b/p2p/conn/conn_notgo110.go @@ -1,6 +1,6 @@ // +build !go1.10 -package tmconn +package conn import ( "net" diff --git a/p2p/tmconn/connection.go b/p2p/conn/connection.go similarity index 99% rename from p2p/tmconn/connection.go rename to p2p/conn/connection.go index 92c48c360..71b2a13d0 100644 --- a/p2p/tmconn/connection.go +++ b/p2p/conn/connection.go @@ -1,4 +1,4 @@ -package tmconn +package conn import ( "bufio" diff --git a/p2p/tmconn/connection_test.go b/p2p/conn/connection_test.go similarity index 99% rename from p2p/tmconn/connection_test.go rename to p2p/conn/connection_test.go index 65ae017cb..9c8eccbe4 100644 --- a/p2p/tmconn/connection_test.go +++ b/p2p/conn/connection_test.go @@ -1,4 +1,4 @@ -package tmconn +package conn import ( "net" diff --git a/p2p/tmconn/secret_connection.go b/p2p/conn/secret_connection.go similarity index 99% rename from p2p/tmconn/secret_connection.go rename to p2p/conn/secret_connection.go index e1a3e0506..aa6db05bb 100644 --- a/p2p/tmconn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -4,7 +4,7 @@ // is known ahead of time, and thus we are technically // still vulnerable to MITM. (TODO!) // See docs/sts-final.pdf for more info -package tmconn +package conn import ( "bytes" diff --git a/p2p/tmconn/secret_connection_test.go b/p2p/conn/secret_connection_test.go similarity index 99% rename from p2p/tmconn/secret_connection_test.go rename to p2p/conn/secret_connection_test.go index 5ef2c410e..8af9cdeb5 100644 --- a/p2p/tmconn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -1,4 +1,4 @@ -package tmconn +package conn import ( "io" diff --git a/p2p/types/errors.go b/p2p/errors.go similarity index 96% rename from p2p/types/errors.go rename to p2p/errors.go index ead2a8334..cb6a7051a 100644 --- a/p2p/types/errors.go +++ b/p2p/errors.go @@ -1,4 +1,4 @@ -package types +package p2p import ( "errors" diff --git a/p2p/types/key.go b/p2p/key.go similarity index 99% rename from p2p/types/key.go rename to p2p/key.go index 4ce5ee500..ea0f0b071 100644 --- a/p2p/types/key.go +++ b/p2p/key.go @@ -1,4 +1,4 @@ -package types +package p2p import ( "bytes" diff --git a/p2p/types/key_test.go b/p2p/key_test.go similarity index 98% rename from p2p/types/key_test.go rename to p2p/key_test.go index f18fb3b92..c2e1f3e0e 100644 --- a/p2p/types/key_test.go +++ b/p2p/key_test.go @@ -1,4 +1,4 @@ -package types +package p2p import ( "bytes" diff --git a/p2p/listener.go b/p2p/listener.go index 01d718330..884c45ee8 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -6,7 +6,6 @@ import ( "strconv" "time" - "github.com/tendermint/tendermint/p2p/types" "github.com/tendermint/tendermint/p2p/upnp" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" @@ -14,8 +13,8 @@ import ( type Listener interface { Connections() <-chan net.Conn - InternalAddress() *types.NetAddress - ExternalAddress() *types.NetAddress + InternalAddress() *NetAddress + ExternalAddress() *NetAddress String() string Stop() error } @@ -25,8 +24,8 @@ type DefaultListener struct { cmn.BaseService listener net.Listener - intAddr *types.NetAddress - extAddr *types.NetAddress + intAddr *NetAddress + extAddr *NetAddress connections chan net.Conn } @@ -72,14 +71,14 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log logger.Info("Local listener", "ip", listenerIP, "port", listenerPort) // Determine internal address... - var intAddr *types.NetAddress - intAddr, err = types.NewNetAddressString(lAddr) + var intAddr *NetAddress + intAddr, err = NewNetAddressString(lAddr) if err != nil { panic(err) } // Determine external address... - var extAddr *types.NetAddress + var extAddr *NetAddress if !skipUPNP { // If the lAddrIP is INADDR_ANY, try UPnP if lAddrIP == "" || lAddrIP == "0.0.0.0" { @@ -152,11 +151,11 @@ func (l *DefaultListener) Connections() <-chan net.Conn { return l.connections } -func (l *DefaultListener) InternalAddress() *types.NetAddress { +func (l *DefaultListener) InternalAddress() *NetAddress { return l.intAddr } -func (l *DefaultListener) ExternalAddress() *types.NetAddress { +func (l *DefaultListener) ExternalAddress() *NetAddress { return l.extAddr } @@ -173,7 +172,7 @@ func (l *DefaultListener) String() string { /* external address helpers */ // UPNP external address discovery & port mapping -func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *types.NetAddress { +func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) *NetAddress { logger.Info("Getting UPNP external address") nat, err := upnp.Discover() if err != nil { @@ -199,11 +198,11 @@ func getUPNPExternalAddress(externalPort, internalPort int, logger log.Logger) * } logger.Info("Got UPNP external address", "address", ext) - return types.NewNetAddressIPPort(ext, uint16(externalPort)) + return NewNetAddressIPPort(ext, uint16(externalPort)) } // TODO: use syscalls: see issue #712 -func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *types.NetAddress { +func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) *NetAddress { addrs, err := net.InterfaceAddrs() if err != nil { panic(cmn.Fmt("Could not fetch interface addresses: %v", err)) @@ -218,7 +217,7 @@ func getNaiveExternalAddress(port int, settleForLocal bool, logger log.Logger) * if v4 == nil || (!settleForLocal && v4[0] == 127) { continue } // loopback - return types.NewNetAddressIPPort(ipnet.IP, uint16(port)) + return NewNetAddressIPPort(ipnet.IP, uint16(port)) } // try again, but settle for local diff --git a/p2p/types/netaddress.go b/p2p/netaddress.go similarity index 99% rename from p2p/types/netaddress.go rename to p2p/netaddress.go index f0b397e8e..333d16e5d 100644 --- a/p2p/types/netaddress.go +++ b/p2p/netaddress.go @@ -2,7 +2,7 @@ // Originally Copyright (c) 2013-2014 Conformal Systems LLC. // https://github.com/conformal/btcd/blob/master/LICENSE -package types +package p2p import ( "encoding/hex" diff --git a/p2p/types/netaddress_test.go b/p2p/netaddress_test.go similarity index 99% rename from p2p/types/netaddress_test.go rename to p2p/netaddress_test.go index 0119cc3b1..6c1930a2f 100644 --- a/p2p/types/netaddress_test.go +++ b/p2p/netaddress_test.go @@ -1,4 +1,4 @@ -package types +package p2p import ( "net" diff --git a/p2p/types/node_info.go b/p2p/node_info.go similarity index 99% rename from p2p/types/node_info.go rename to p2p/node_info.go index 10c486852..552c464d9 100644 --- a/p2p/types/node_info.go +++ b/p2p/node_info.go @@ -1,4 +1,4 @@ -package types +package p2p import ( "fmt" diff --git a/p2p/peer.go b/p2p/peer.go index 17c5861f6..60f9dceba 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -12,18 +12,17 @@ import ( cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + tmconn "github.com/tendermint/tendermint/p2p/conn" ) // Peer is an interface representing a peer connected on a reactor. type Peer interface { cmn.Service - ID() types.ID // peer's cryptographic ID - IsOutbound() bool // did we dial the peer - IsPersistent() bool // do we redial this peer when we disconnect - NodeInfo() types.NodeInfo // peer's info + ID() ID // peer's cryptographic ID + IsOutbound() bool // did we dial the peer + IsPersistent() bool // do we redial this peer when we disconnect + NodeInfo() NodeInfo // peer's info Status() tmconn.ConnectionStatus Send(byte, interface{}) bool @@ -49,7 +48,7 @@ type peer struct { persistent bool config *PeerConfig - nodeInfo types.NodeInfo + nodeInfo NodeInfo Data *cmn.CMap // User data. } @@ -79,7 +78,7 @@ func DefaultPeerConfig() *PeerConfig { } } -func newOutboundPeer(addr *types.NetAddress, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, +func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*tmconn.ChannelDescriptor, onPeerError func(Peer, interface{}), ourNodePrivKey crypto.PrivKey, config *PeerConfig, persistent bool) (*peer, error) { conn, err := dial(addr, config) @@ -174,8 +173,8 @@ func (p *peer) OnStop() { // Implements Peer // ID returns the peer's ID - the hex encoded hash of its pubkey. -func (p *peer) ID() types.ID { - return types.PubKeyToID(p.PubKey()) +func (p *peer) ID() ID { + return PubKeyToID(p.PubKey()) } // IsOutbound returns true if the connection is outbound, false otherwise. @@ -189,7 +188,7 @@ func (p *peer) IsPersistent() bool { } // NodeInfo returns a copy of the peer's NodeInfo. -func (p *peer) NodeInfo() types.NodeInfo { +func (p *peer) NodeInfo() NodeInfo { return p.nodeInfo } @@ -239,13 +238,13 @@ func (p *peer) CloseConn() { // HandshakeTimeout performs the Tendermint P2P handshake between a given node and the peer // by exchanging their NodeInfo. It sets the received nodeInfo on the peer. // NOTE: blocking -func (p *peer) HandshakeTimeout(ourNodeInfo types.NodeInfo, timeout time.Duration) error { +func (p *peer) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) error { // Set deadline for handshake so we don't block forever on conn.ReadFull if err := p.conn.SetDeadline(time.Now().Add(timeout)); err != nil { return errors.Wrap(err, "Error setting deadline") } - var peerNodeInfo types.NodeInfo + var peerNodeInfo NodeInfo var err1 error var err2 error cmn.Parallel( @@ -255,7 +254,7 @@ func (p *peer) HandshakeTimeout(ourNodeInfo types.NodeInfo, timeout time.Duratio }, func() { var n int - wire.ReadBinary(&peerNodeInfo, p.conn, types.MaxNodeInfoSize(), &n, &err2) + wire.ReadBinary(&peerNodeInfo, p.conn, MaxNodeInfoSize(), &n, &err2) p.Logger.Info("Peer handshake", "peerNodeInfo", peerNodeInfo) }) if err1 != nil { @@ -311,7 +310,7 @@ func (p *peer) String() string { //------------------------------------------------------------------ // helper funcs -func dial(addr *types.NetAddress, config *PeerConfig) (net.Conn, error) { +func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) { conn, err := addr.DialTimeout(config.DialTimeout * time.Second) if err != nil { return nil, err diff --git a/p2p/peer_set.go b/p2p/peer_set.go index 7a0680cb7..dc53174a1 100644 --- a/p2p/peer_set.go +++ b/p2p/peer_set.go @@ -2,14 +2,12 @@ package p2p import ( "sync" - - "github.com/tendermint/tendermint/p2p/types" ) // IPeerSet has a (immutable) subset of the methods of PeerSet. type IPeerSet interface { - Has(key types.ID) bool - Get(key types.ID) Peer + Has(key ID) bool + Get(key ID) Peer List() []Peer Size() int } @@ -20,7 +18,7 @@ type IPeerSet interface { // Iteration over the peers is super fast and thread-safe. type PeerSet struct { mtx sync.Mutex - lookup map[types.ID]*peerSetItem + lookup map[ID]*peerSetItem list []Peer } @@ -32,7 +30,7 @@ type peerSetItem struct { // NewPeerSet creates a new peerSet with a list of initial capacity of 256 items. func NewPeerSet() *PeerSet { return &PeerSet{ - lookup: make(map[types.ID]*peerSetItem), + lookup: make(map[ID]*peerSetItem), list: make([]Peer, 0, 256), } } @@ -43,7 +41,7 @@ func (ps *PeerSet) Add(peer Peer) error { ps.mtx.Lock() defer ps.mtx.Unlock() if ps.lookup[peer.ID()] != nil { - return types.ErrSwitchDuplicatePeer + return ErrSwitchDuplicatePeer } index := len(ps.list) @@ -56,7 +54,7 @@ func (ps *PeerSet) Add(peer Peer) error { // Has returns true iff the PeerSet contains // the peer referred to by this peerKey. -func (ps *PeerSet) Has(peerKey types.ID) bool { +func (ps *PeerSet) Has(peerKey ID) bool { ps.mtx.Lock() _, ok := ps.lookup[peerKey] ps.mtx.Unlock() @@ -64,7 +62,7 @@ func (ps *PeerSet) Has(peerKey types.ID) bool { } // Get looks up a peer by the provided peerKey. -func (ps *PeerSet) Get(peerKey types.ID) Peer { +func (ps *PeerSet) Get(peerKey ID) Peer { ps.mtx.Lock() defer ps.mtx.Unlock() item, ok := ps.lookup[peerKey] diff --git a/p2p/peer_set_test.go b/p2p/peer_set_test.go index 7d7ed1062..e906eb8e7 100644 --- a/p2p/peer_set_test.go +++ b/p2p/peer_set_test.go @@ -8,14 +8,13 @@ import ( "github.com/stretchr/testify/assert" crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/tendermint/p2p/types" cmn "github.com/tendermint/tmlibs/common" ) // Returns an empty dummy peer func randPeer() *peer { return &peer{ - nodeInfo: types.NodeInfo{ + nodeInfo: NodeInfo{ ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256), PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), }, @@ -120,7 +119,7 @@ func TestPeerSetAddDuplicate(t *testing.T) { // Our next procedure is to ensure that only one addition // succeeded and that the rest are each ErrSwitchDuplicatePeer. - wantErrCount, gotErrCount := n-1, errsTally[types.ErrSwitchDuplicatePeer] + wantErrCount, gotErrCount := n-1, errsTally[ErrSwitchDuplicatePeer] assert.Equal(t, wantErrCount, gotErrCount, "invalid ErrSwitchDuplicatePeer count") wantNilErrCount, gotNilErrCount := 1, errsTally[nil] diff --git a/p2p/peer_test.go b/p2p/peer_test.go index dc13cf9d6..978775c8e 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -10,8 +10,7 @@ import ( "github.com/stretchr/testify/require" crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + tmconn "github.com/tendermint/tendermint/p2p/conn" ) func TestPeerBasic(t *testing.T) { @@ -82,7 +81,7 @@ func TestPeerSend(t *testing.T) { assert.True(p.Send(0x01, "Asylum")) } -func createOutboundPeerAndPerformHandshake(addr *types.NetAddress, config *PeerConfig) (*peer, error) { +func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig) (*peer, error) { chDescs := []*tmconn.ChannelDescriptor{ {ID: 0x01, Priority: 1}, } @@ -92,7 +91,7 @@ func createOutboundPeerAndPerformHandshake(addr *types.NetAddress, config *PeerC if err != nil { return nil, err } - err = p.HandshakeTimeout(types.NodeInfo{ + err = p.HandshakeTimeout(NodeInfo{ PubKey: pk.PubKey(), Moniker: "host_peer", Network: "testing", @@ -107,11 +106,11 @@ func createOutboundPeerAndPerformHandshake(addr *types.NetAddress, config *PeerC type remotePeer struct { PrivKey crypto.PrivKey Config *PeerConfig - addr *types.NetAddress + addr *NetAddress quit chan struct{} } -func (p *remotePeer) Addr() *types.NetAddress { +func (p *remotePeer) Addr() *NetAddress { return p.addr } @@ -124,7 +123,7 @@ func (p *remotePeer) Start() { if e != nil { golog.Fatalf("net.Listen tcp :0: %+v", e) } - p.addr = types.NewNetAddress("", l.Addr()) + p.addr = NewNetAddress("", l.Addr()) p.quit = make(chan struct{}) go p.accept(l) } @@ -143,7 +142,7 @@ func (p *remotePeer) accept(l net.Listener) { if err != nil { golog.Fatalf("Failed to create a peer: %+v", err) } - err = peer.HandshakeTimeout(types.NodeInfo{ + err = peer.HandshakeTimeout(NodeInfo{ PubKey: p.PrivKey.PubKey(), Moniker: "remote_peer", Network: "testing", diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 93f352117..b7f60682d 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -15,9 +15,8 @@ import ( "time" crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tendermint/p2p" cmn "github.com/tendermint/tmlibs/common" - - "github.com/tendermint/tendermint/p2p/types" ) const ( @@ -32,25 +31,25 @@ type AddrBook interface { cmn.Service // Add our own addresses so we don't later add ourselves - AddOurAddress(*types.NetAddress) + AddOurAddress(*p2p.NetAddress) // Add and remove an address - AddAddress(addr *types.NetAddress, src *types.NetAddress) error - RemoveAddress(addr *types.NetAddress) + AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error + RemoveAddress(addr *p2p.NetAddress) // Do we need more peers? NeedMoreAddrs() bool // Pick an address to dial - PickAddress(newBias int) *types.NetAddress + PickAddress(newBias int) *p2p.NetAddress // Mark address - MarkGood(*types.NetAddress) - MarkAttempt(*types.NetAddress) - MarkBad(*types.NetAddress) + MarkGood(*p2p.NetAddress) + MarkAttempt(*p2p.NetAddress) + MarkBad(*p2p.NetAddress) // Send a selection of addresses to peers - GetSelection() []*types.NetAddress + GetSelection() []*p2p.NetAddress // TODO: remove ListOfKnownAddresses() []*knownAddress @@ -71,8 +70,8 @@ type addrBook struct { // accessed concurrently mtx sync.Mutex rand *rand.Rand - ourAddrs map[string]*types.NetAddress - addrLookup map[types.ID]*knownAddress // new & old + ourAddrs map[string]*p2p.NetAddress + addrLookup map[p2p.ID]*knownAddress // new & old bucketsOld []map[string]*knownAddress bucketsNew []map[string]*knownAddress nOld int @@ -86,8 +85,8 @@ type addrBook struct { func NewAddrBook(filePath string, routabilityStrict bool) *addrBook { am := &addrBook{ rand: rand.New(rand.NewSource(time.Now().UnixNano())), // TODO: seed from outside - ourAddrs: make(map[string]*types.NetAddress), - addrLookup: make(map[types.ID]*knownAddress), + ourAddrs: make(map[string]*p2p.NetAddress), + addrLookup: make(map[p2p.ID]*knownAddress), filePath: filePath, routabilityStrict: routabilityStrict, } @@ -139,7 +138,7 @@ func (a *addrBook) Wait() { //------------------------------------------------------- // AddOurAddress one of our addresses. -func (a *addrBook) AddOurAddress(addr *types.NetAddress) { +func (a *addrBook) AddOurAddress(addr *p2p.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() a.Logger.Info("Add our address to book", "addr", addr) @@ -148,14 +147,14 @@ func (a *addrBook) AddOurAddress(addr *types.NetAddress) { // AddAddress implements AddrBook - adds the given address as received from the given source. // NOTE: addr must not be nil -func (a *addrBook) AddAddress(addr *types.NetAddress, src *types.NetAddress) error { +func (a *addrBook) AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error { a.mtx.Lock() defer a.mtx.Unlock() return a.addAddress(addr, src) } // RemoveAddress implements AddrBook - removes the address from the book. -func (a *addrBook) RemoveAddress(addr *types.NetAddress) { +func (a *addrBook) RemoveAddress(addr *p2p.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -177,7 +176,7 @@ func (a *addrBook) NeedMoreAddrs() bool { // and determines how biased we are to pick an address from a new bucket. // PickAddress returns nil if the AddrBook is empty or if we try to pick // from an empty bucket. -func (a *addrBook) PickAddress(newBias int) *types.NetAddress { +func (a *addrBook) PickAddress(newBias int) *p2p.NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -223,7 +222,7 @@ func (a *addrBook) PickAddress(newBias int) *types.NetAddress { // MarkGood implements AddrBook - it marks the peer as good and // moves it into an "old" bucket. -func (a *addrBook) MarkGood(addr *types.NetAddress) { +func (a *addrBook) MarkGood(addr *p2p.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -237,7 +236,7 @@ func (a *addrBook) MarkGood(addr *types.NetAddress) { } // MarkAttempt implements AddrBook - it marks that an attempt was made to connect to the address. -func (a *addrBook) MarkAttempt(addr *types.NetAddress) { +func (a *addrBook) MarkAttempt(addr *p2p.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() ka := a.addrLookup[addr.ID] @@ -249,13 +248,13 @@ func (a *addrBook) MarkAttempt(addr *types.NetAddress) { // MarkBad implements AddrBook. Currently it just ejects the address. // TODO: black list for some amount of time -func (a *addrBook) MarkBad(addr *types.NetAddress) { +func (a *addrBook) MarkBad(addr *p2p.NetAddress) { a.RemoveAddress(addr) } // GetSelection implements AddrBook. // It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. -func (a *addrBook) GetSelection() []*types.NetAddress { +func (a *addrBook) GetSelection() []*p2p.NetAddress { a.mtx.Lock() defer a.mtx.Unlock() @@ -263,7 +262,7 @@ func (a *addrBook) GetSelection() []*types.NetAddress { return nil } - allAddr := make([]*types.NetAddress, a.size()) + allAddr := make([]*p2p.NetAddress, a.size()) i := 0 for _, ka := range a.addrLookup { allAddr[i] = ka.Addr @@ -466,7 +465,7 @@ func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { // adds the address to a "new" bucket. if its already in one, // it only adds it probabilistically -func (a *addrBook) addAddress(addr, src *types.NetAddress) error { +func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { if a.routabilityStrict && !addr.Routable() { return fmt.Errorf("Cannot add non-routable address %v", addr) } @@ -573,7 +572,7 @@ func (a *addrBook) moveToOld(ka *knownAddress) { // doublesha256( key + sourcegroup + // int64(doublesha256(key + group + sourcegroup))%bucket_per_group ) % num_new_buckets -func (a *addrBook) calcNewBucket(addr, src *types.NetAddress) int { +func (a *addrBook) calcNewBucket(addr, src *p2p.NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(a.groupKey(addr))...) @@ -594,7 +593,7 @@ func (a *addrBook) calcNewBucket(addr, src *types.NetAddress) int { // doublesha256( key + group + // int64(doublesha256(key + addr))%buckets_per_group ) % num_old_buckets -func (a *addrBook) calcOldBucket(addr *types.NetAddress) int { +func (a *addrBook) calcOldBucket(addr *p2p.NetAddress) int { data1 := []byte{} data1 = append(data1, []byte(a.key)...) data1 = append(data1, []byte(addr.String())...) @@ -616,7 +615,7 @@ func (a *addrBook) calcOldBucket(addr *types.NetAddress) int { // This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string // "local" for a local address and the string "unroutable" for an unroutable // address. -func (a *addrBook) groupKey(na *types.NetAddress) string { +func (a *addrBook) groupKey(na *p2p.NetAddress) string { if a.routabilityStrict && na.Local() { return "local" } diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 206e3401f..166d31847 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p" cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tmlibs/log" ) @@ -169,8 +169,8 @@ func TestAddrBookHandlesDuplicates(t *testing.T) { } type netAddressPair struct { - addr *types.NetAddress - src *types.NetAddress + addr *p2p.NetAddress + src *p2p.NetAddress } func randNetAddressPairs(t *testing.T, n int) []netAddressPair { @@ -181,7 +181,7 @@ func randNetAddressPairs(t *testing.T, n int) []netAddressPair { return randAddrs } -func randIPv4Address(t *testing.T) *types.NetAddress { +func randIPv4Address(t *testing.T) *p2p.NetAddress { for { ip := fmt.Sprintf("%v.%v.%v.%v", rand.Intn(254)+1, @@ -190,9 +190,9 @@ func randIPv4Address(t *testing.T) *types.NetAddress { rand.Intn(255), ) port := rand.Intn(65535-1) + 1 - id := types.ID(hex.EncodeToString(cmn.RandBytes(types.IDByteLength))) - idAddr := types.IDAddressString(id, fmt.Sprintf("%v:%v", ip, port)) - addr, err := types.NewNetAddressString(idAddr) + 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 diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index db6d021f2..e26fdb7bd 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -3,14 +3,14 @@ package pex import ( "time" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p" ) // knownAddress tracks information about a known network address // that is used to determine how viable an address is. type knownAddress struct { - Addr *types.NetAddress - Src *types.NetAddress + Addr *p2p.NetAddress + Src *p2p.NetAddress Attempts int32 LastAttempt time.Time LastSuccess time.Time @@ -18,7 +18,7 @@ type knownAddress struct { Buckets []int } -func newKnownAddress(addr *types.NetAddress, src *types.NetAddress) *knownAddress { +func newKnownAddress(addr *p2p.NetAddress, src *p2p.NetAddress) *knownAddress { return &knownAddress{ Addr: addr, Src: src, @@ -29,7 +29,7 @@ func newKnownAddress(addr *types.NetAddress, src *types.NetAddress) *knownAddres } } -func (ka *knownAddress) ID() types.ID { +func (ka *knownAddress) ID() p2p.ID { return ka.Addr.ID } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 24c9417f7..53075a1d6 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -13,8 +13,7 @@ import ( cmn "github.com/tendermint/tmlibs/common" "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" ) type Peer = p2p.Peer @@ -117,8 +116,8 @@ func (r *PEXReactor) OnStop() { } // GetChannels implements Reactor -func (r *PEXReactor) GetChannels() []*tmconn.ChannelDescriptor { - return []*tmconn.ChannelDescriptor{ +func (r *PEXReactor) GetChannels() []*conn.ChannelDescriptor { + return []*conn.ChannelDescriptor{ { ID: PexChannel, Priority: 1, @@ -231,7 +230,7 @@ func (r *PEXReactor) RequestAddrs(p Peer) { // ReceiveAddrs adds the given addrs to the addrbook if theres an open // request for this peer and deletes the open request. // If there's no open request for the src peer, it returns an error. -func (r *PEXReactor) ReceiveAddrs(addrs []*types.NetAddress, src Peer) error { +func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { id := string(src.ID()) if !r.requestsSent.Has(id) { @@ -250,7 +249,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*types.NetAddress, src Peer) error { } // SendAddrs sends addrs to the peer. -func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*types.NetAddress) { +func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*p2p.NetAddress) { p.Send(PexChannel, struct{ PexMessage }{&pexAddrsMessage{Addrs: netAddrs}}) } @@ -300,7 +299,7 @@ func (r *PEXReactor) ensurePeers() { // NOTE: range here is [10, 90]. Too high ? newBias := cmn.MinInt(numOutPeers, 8)*10 + 10 - toDial := make(map[types.ID]*types.NetAddress) + toDial := make(map[p2p.ID]*p2p.NetAddress) // Try maxAttempts times to pick numToDial addresses to dial maxAttempts := numToDial * 3 for i := 0; i < maxAttempts && len(toDial) < numToDial; i++ { @@ -323,11 +322,11 @@ func (r *PEXReactor) ensurePeers() { // Dial picked addresses for _, item := range toDial { - go func(picked *types.NetAddress) { + go func(picked *p2p.NetAddress) { _, err := r.Switch.DialPeerWithAddress(picked, false) if err != nil { // TODO: detect more "bad peer" scenarios - if _, ok := err.(types.ErrSwitchAuthenticationFailure); ok { + if _, ok := err.(p2p.ErrSwitchAuthenticationFailure); ok { r.book.MarkBad(picked) } else { r.book.MarkAttempt(picked) @@ -360,7 +359,7 @@ func (r *PEXReactor) checkSeeds() error { if lSeeds == 0 { return nil } - _, errs := types.NewNetAddressStrings(r.config.Seeds) + _, errs := p2p.NewNetAddressStrings(r.config.Seeds) for _, err := range errs { if err != nil { return err @@ -375,7 +374,7 @@ func (r *PEXReactor) dialSeeds() { if lSeeds == 0 { return } - seedAddrs, _ := types.NewNetAddressStrings(r.config.Seeds) + seedAddrs, _ := p2p.NewNetAddressStrings(r.config.Seeds) perm := rand.Perm(lSeeds) // perm := r.Switch.rng.Perm(lSeeds) @@ -420,7 +419,7 @@ func (r *PEXReactor) crawlPeersRoutine() { // network crawling performed during seed/crawler mode. type crawlPeerInfo struct { // The listening address of a potential peer we learned about - Addr *types.NetAddress + Addr *p2p.NetAddress // The last time we attempt to reach this address LastAttempt time.Time @@ -544,7 +543,7 @@ func (m *pexRequestMessage) String() string { A message with announced peer addresses. */ type pexAddrsMessage struct { - Addrs []*types.NetAddress + Addrs []*p2p.NetAddress } func (m *pexAddrsMessage) String() string { diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 439914ac8..c52e45b47 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -17,8 +17,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" ) var ( @@ -101,7 +100,7 @@ func TestPEXReactorRunning(t *testing.T) { // fill the address book and add listeners for _, s := range switches { - addr, _ := types.NewNetAddressString(s.NodeInfo().ListenAddr) + addr, _ := p2p.NewNetAddressString(s.NodeInfo().ListenAddr) book.AddAddress(addr, addr) s.AddListener(p2p.NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger())) } @@ -171,7 +170,7 @@ func TestPEXReactorReceive(t *testing.T) { r.RequestAddrs(peer) size := book.Size() - addrs := []*types.NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) r.Receive(PexChannel, peer, msg) assert.Equal(size+1, book.Size()) @@ -246,7 +245,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { assert.True(r.requestsSent.Has(id)) assert.True(sw.Peers().Has(peer.ID())) - addrs := []*types.NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} msg := wire.BinaryBytes(struct{ PexMessage }{&pexAddrsMessage{Addrs: addrs}}) // receive some addrs. should clear the request @@ -340,7 +339,7 @@ func TestPEXReactorCrawlStatus(t *testing.T) { type mockPeer struct { *cmn.BaseService pubKey crypto.PubKey - addr *types.NetAddress + addr *p2p.NetAddress outbound, persistent bool } @@ -355,17 +354,17 @@ func newMockPeer() mockPeer { return mp } -func (mp mockPeer) ID() types.ID { return types.PubKeyToID(mp.pubKey) } +func (mp mockPeer) ID() p2p.ID { return p2p.PubKeyToID(mp.pubKey) } func (mp mockPeer) IsOutbound() bool { return mp.outbound } func (mp mockPeer) IsPersistent() bool { return mp.persistent } -func (mp mockPeer) NodeInfo() types.NodeInfo { - return types.NodeInfo{ +func (mp mockPeer) NodeInfo() p2p.NodeInfo { + return p2p.NodeInfo{ PubKey: mp.pubKey, ListenAddr: mp.addr.DialString(), } } -func (mp mockPeer) Status() tmconn.ConnectionStatus { return tmconn.ConnectionStatus{} } -func (mp mockPeer) Send(byte, interface{}) bool { return false } -func (mp mockPeer) TrySend(byte, interface{}) bool { return false } -func (mp mockPeer) Set(string, interface{}) {} -func (mp mockPeer) Get(string) interface{} { return nil } +func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } +func (mp mockPeer) Send(byte, interface{}) bool { return false } +func (mp mockPeer) TrySend(byte, interface{}) bool { return false } +func (mp mockPeer) Set(string, interface{}) {} +func (mp mockPeer) Get(string) interface{} { return nil } diff --git a/p2p/switch.go b/p2p/switch.go index c9938374c..f29d1b273 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -11,8 +11,7 @@ import ( crypto "github.com/tendermint/go-crypto" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" cmn "github.com/tendermint/tmlibs/common" ) @@ -35,7 +34,7 @@ const ( //----------------------------------------------------------------------------- type AddrBook interface { - AddAddress(addr *types.NetAddress, src *types.NetAddress) error + AddAddress(addr *NetAddress, src *NetAddress) error } //----------------------------------------------------------------------------- @@ -51,12 +50,12 @@ type Switch struct { peerConfig *PeerConfig listeners []Listener reactors map[string]Reactor - chDescs []*tmconn.ChannelDescriptor + chDescs []*conn.ChannelDescriptor reactorsByCh map[byte]Reactor peers *PeerSet dialing *cmn.CMap - nodeInfo types.NodeInfo // our node info - nodeKey *types.NodeKey // our node privkey + nodeInfo NodeInfo // our node info + nodeKey *NodeKey // our node privkey filterConnByAddr func(net.Addr) error filterConnByPubKey func(crypto.PubKey) error @@ -69,7 +68,7 @@ func NewSwitch(config *cfg.P2PConfig) *Switch { config: config, peerConfig: DefaultPeerConfig(), reactors: make(map[string]Reactor), - chDescs: make([]*tmconn.ChannelDescriptor, 0), + chDescs: make([]*conn.ChannelDescriptor, 0), reactorsByCh: make(map[byte]Reactor), peers: NewPeerSet(), dialing: cmn.NewCMap(), @@ -143,19 +142,19 @@ func (sw *Switch) IsListening() bool { // SetNodeInfo sets the switch's NodeInfo for checking compatibility and handshaking with other nodes. // NOTE: Not goroutine safe. -func (sw *Switch) SetNodeInfo(nodeInfo types.NodeInfo) { +func (sw *Switch) SetNodeInfo(nodeInfo NodeInfo) { sw.nodeInfo = nodeInfo } // NodeInfo returns the switch's NodeInfo. // NOTE: Not goroutine safe. -func (sw *Switch) NodeInfo() types.NodeInfo { +func (sw *Switch) NodeInfo() NodeInfo { return sw.nodeInfo } // SetNodeKey sets the switch's private key for authenticated encryption. // NOTE: Not goroutine safe. -func (sw *Switch) SetNodeKey(nodeKey *types.NodeKey) { +func (sw *Switch) SetNodeKey(nodeKey *NodeKey) { sw.nodeKey = nodeKey } @@ -314,13 +313,13 @@ func (sw *Switch) reconnectToPeer(peer Peer) { // Dialing // IsDialing returns true if the switch is currently dialing the given ID. -func (sw *Switch) IsDialing(id types.ID) bool { +func (sw *Switch) IsDialing(id ID) bool { return sw.dialing.Has(string(id)) } // DialPeersAsync dials a list of peers asynchronously in random order (optionally, making them persistent). func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent bool) error { - netAddrs, errs := types.NewNetAddressStrings(peers) + netAddrs, errs := NewNetAddressStrings(peers) for _, err := range errs { sw.Logger.Error("Error in peer's address", "err", err) } @@ -357,7 +356,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b // DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects and authenticates successfully. // If `persistent == true`, the switch will always try to reconnect to this peer if the connection ever fails. -func (sw *Switch) DialPeerWithAddress(addr *types.NetAddress, persistent bool) (Peer, error) { +func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) (Peer, error) { sw.dialing.Set(string(addr.ID), addr) defer sw.dialing.Delete(string(addr.ID)) return sw.addOutboundPeerWithConfig(addr, sw.peerConfig, persistent) @@ -443,7 +442,7 @@ func (sw *Switch) addInboundPeerWithConfig(conn net.Conn, config *PeerConfig) er // dial the peer; make secret connection; authenticate against the dialed ID; // add the peer. -func (sw *Switch) addOutboundPeerWithConfig(addr *types.NetAddress, config *PeerConfig, persistent bool) (Peer, error) { +func (sw *Switch) addOutboundPeerWithConfig(addr *NetAddress, config *PeerConfig, persistent bool) (Peer, error) { sw.Logger.Info("Dialing peer", "address", addr) peer, err := newOutboundPeer(addr, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodeKey.PrivKey, config, persistent) if err != nil { @@ -457,7 +456,7 @@ func (sw *Switch) addOutboundPeerWithConfig(addr *types.NetAddress, config *Peer peer.Logger.Info("Dialed peer with unknown ID - unable to authenticate", "addr", addr) } else if addr.ID != peer.ID() { peer.CloseConn() - return nil, types.ErrSwitchAuthenticationFailure{addr, peer.ID()} + return nil, ErrSwitchAuthenticationFailure{addr, peer.ID()} } err = sw.addPeer(peer) @@ -478,12 +477,12 @@ func (sw *Switch) addOutboundPeerWithConfig(addr *types.NetAddress, config *Peer func (sw *Switch) addPeer(peer *peer) error { // Avoid self if sw.nodeKey.ID() == peer.ID() { - return types.ErrSwitchConnectToSelf + return ErrSwitchConnectToSelf } // Avoid duplicate if sw.peers.Has(peer.ID()) { - return types.ErrSwitchDuplicatePeer + return ErrSwitchDuplicatePeer } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index ae7e89e77..75f9640b1 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -16,8 +16,7 @@ import ( "github.com/tendermint/tmlibs/log" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" ) var ( @@ -30,7 +29,7 @@ func init() { } type PeerMessage struct { - PeerID types.ID + PeerID ID Bytes []byte Counter int } @@ -39,7 +38,7 @@ type TestReactor struct { BaseReactor mtx sync.Mutex - channels []*tmconn.ChannelDescriptor + channels []*conn.ChannelDescriptor peersAdded []Peer peersRemoved []Peer logMessages bool @@ -47,7 +46,7 @@ type TestReactor struct { msgsReceived map[byte][]PeerMessage } -func NewTestReactor(channels []*tmconn.ChannelDescriptor, logMessages bool) *TestReactor { +func NewTestReactor(channels []*conn.ChannelDescriptor, logMessages bool) *TestReactor { tr := &TestReactor{ channels: channels, logMessages: logMessages, @@ -58,7 +57,7 @@ func NewTestReactor(channels []*tmconn.ChannelDescriptor, logMessages bool) *Tes return tr } -func (tr *TestReactor) GetChannels() []*tmconn.ChannelDescriptor { +func (tr *TestReactor) GetChannels() []*conn.ChannelDescriptor { return tr.channels } @@ -102,11 +101,11 @@ func MakeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switc func initSwitchFunc(i int, sw *Switch) *Switch { // Make two reactors of two channels each - sw.AddReactor("foo", NewTestReactor([]*tmconn.ChannelDescriptor{ + sw.AddReactor("foo", NewTestReactor([]*conn.ChannelDescriptor{ {ID: byte(0x00), Priority: 10}, {ID: byte(0x01), Priority: 10}, }, true)) - sw.AddReactor("bar", NewTestReactor([]*tmconn.ChannelDescriptor{ + sw.AddReactor("bar", NewTestReactor([]*conn.ChannelDescriptor{ {ID: byte(0x02), Priority: 10}, {ID: byte(0x03), Priority: 10}, }, true)) @@ -163,7 +162,7 @@ func TestConnAddrFilter(t *testing.T) { defer s1.Stop() defer s2.Stop() - c1, c2 := tmconn.NetPipe() + c1, c2 := conn.NetPipe() s1.SetAddrFilter(func(addr net.Addr) error { if addr.String() == c1.RemoteAddr().String() { @@ -199,7 +198,7 @@ func TestConnPubKeyFilter(t *testing.T) { defer s1.Stop() defer s2.Stop() - c1, c2 := tmconn.NetPipe() + c1, c2 := conn.NetPipe() // set pubkey filter s1.SetPubKeyFilter(func(pubkey crypto.PubKey) error { @@ -306,11 +305,11 @@ func BenchmarkSwitches(b *testing.B) { s1, s2 := MakeSwitchPair(b, func(i int, sw *Switch) *Switch { // Make bar reactors of bar channels each - sw.AddReactor("foo", NewTestReactor([]*tmconn.ChannelDescriptor{ + sw.AddReactor("foo", NewTestReactor([]*conn.ChannelDescriptor{ {ID: byte(0x00), Priority: 10}, {ID: byte(0x01), Priority: 10}, }, false)) - sw.AddReactor("bar", NewTestReactor([]*tmconn.ChannelDescriptor{ + sw.AddReactor("bar", NewTestReactor([]*conn.ChannelDescriptor{ {ID: byte(0x02), Priority: 10}, {ID: byte(0x03), Priority: 10}, }, false)) diff --git a/p2p/test_util.go b/p2p/test_util.go index aad6fb232..c4c0fe0b7 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -9,8 +9,7 @@ import ( "github.com/tendermint/tmlibs/log" cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" ) func AddPeerToSwitch(sw *Switch, peer Peer) { @@ -20,22 +19,22 @@ func AddPeerToSwitch(sw *Switch, peer Peer) { func CreateRandomPeer(outbound bool) *peer { addr, netAddr := CreateRoutableAddr() p := &peer{ - nodeInfo: types.NodeInfo{ + nodeInfo: NodeInfo{ ListenAddr: netAddr.DialString(), PubKey: crypto.GenPrivKeyEd25519().Wrap().PubKey(), }, outbound: outbound, - mconn: &tmconn.MConnection{}, + mconn: &conn.MConnection{}, } p.SetLogger(log.TestingLogger().With("peer", addr)) return p } -func CreateRoutableAddr() (addr string, netAddr *types.NetAddress) { +func CreateRoutableAddr() (addr string, netAddr *NetAddress) { for { var err error addr = cmn.Fmt("%X@%v.%v.%v.%v:46656", cmn.RandBytes(20), rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256) - netAddr, err = types.NewNetAddressString(addr) + netAddr, err = NewNetAddressString(addr) if err != nil { panic(err) } @@ -78,7 +77,7 @@ func MakeConnectedSwitches(cfg *cfg.P2PConfig, n int, initSwitch func(int, *Swit func Connect2Switches(switches []*Switch, i, j int) { switchI := switches[i] switchJ := switches[j] - c1, c2 := tmconn.NetPipe() + c1, c2 := conn.NetPipe() doneCh := make(chan struct{}) go func() { err := switchI.addPeerWithConnection(c1) @@ -130,13 +129,13 @@ func StartSwitches(switches []*Switch) error { func MakeSwitch(cfg *cfg.P2PConfig, i int, network, version string, initSwitch func(int, *Switch) *Switch) *Switch { // new switch, add reactors // TODO: let the config be passed in? - nodeKey := &types.NodeKey{ + nodeKey := &NodeKey{ PrivKey: crypto.GenPrivKeyEd25519().Wrap(), } s := NewSwitch(cfg) s.SetLogger(log.TestingLogger()) s = initSwitch(i, s) - s.SetNodeInfo(types.NodeInfo{ + s.SetNodeInfo(NodeInfo{ PubKey: nodeKey.PubKey(), Moniker: cmn.Fmt("switch%d", i), Network: network, diff --git a/p2p/types.go b/p2p/types.go index db7469ec0..b11765bb5 100644 --- a/p2p/types.go +++ b/p2p/types.go @@ -1,12 +1,8 @@ package p2p import ( - "github.com/tendermint/tendermint/p2p/tmconn" - "github.com/tendermint/tendermint/p2p/types" + "github.com/tendermint/tendermint/p2p/conn" ) -type ID = types.ID -type NodeInfo = types.NodeInfo - -type ChannelDescriptor = tmconn.ChannelDescriptor -type ConnectionStatus = tmconn.ConnectionStatus +type ChannelDescriptor = conn.ChannelDescriptor +type ConnectionStatus = conn.ConnectionStatus From 21ce5856b3441dd6d5caca16c7a22c0ab3b27d86 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 23 Jan 2018 21:26:19 -0500 Subject: [PATCH 5/8] p2p: notes about ListenAddr --- p2p/node_info.go | 23 ++++++----------------- p2p/peer.go | 2 -- p2p/switch.go | 2 ++ 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/p2p/node_info.go b/p2p/node_info.go index 552c464d9..72873add8 100644 --- a/p2p/node_info.go +++ b/p2p/node_info.go @@ -2,8 +2,6 @@ package p2p import ( "fmt" - "net" - "strconv" "strings" crypto "github.com/tendermint/go-crypto" @@ -42,7 +40,8 @@ func (info NodeInfo) Validate(pubKey crypto.PubKey) error { return nil } -// CONTRACT: two nodes are compatible if the major/minor versions match and network match +// CompatibleWith checks if two NodeInfo are compatible with eachother. +// CONTRACT: two nodes are compatible if the major/minor versions match and network match. func (info NodeInfo) CompatibleWith(other NodeInfo) error { iMajor, iMinor, _, iErr := splitVersion(info.Version) oMajor, oMinor, _, oErr := splitVersion(other.Version) @@ -79,6 +78,10 @@ func (info NodeInfo) ID() ID { return PubKeyToID(info.PubKey) } +// NetAddress returns a NetAddress derived from the NodeInfo - +// it includes the authenticated peer ID and the self-reported +// ListenAddr. Note that the ListenAddr is not authenticated and +// may not match that address actually dialed if its an outbound peer. func (info NodeInfo) NetAddress() *NetAddress { id := PubKeyToID(info.PubKey) addr := info.ListenAddr @@ -89,20 +92,6 @@ func (info NodeInfo) NetAddress() *NetAddress { return netAddr } -func (info NodeInfo) ListenHost() string { - host, _, _ := net.SplitHostPort(info.ListenAddr) // nolint: errcheck, gas - return host -} - -func (info NodeInfo) ListenPort() int { - _, port, _ := net.SplitHostPort(info.ListenAddr) // nolint: errcheck, gas - port_i, err := strconv.Atoi(port) - if err != nil { - return -1 - } - return port_i -} - func (info NodeInfo) String() string { return fmt.Sprintf("NodeInfo{pk: %v, moniker: %v, network: %v [listen %v], version: %v (%v)}", info.PubKey, info.Moniker, info.Network, info.ListenAddr, info.Version, info.Other) } diff --git a/p2p/peer.go b/p2p/peer.go index 60f9dceba..e427b0d95 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -269,8 +269,6 @@ func (p *peer) HandshakeTimeout(ourNodeInfo NodeInfo, timeout time.Duration) err return errors.Wrap(err, "Error removing deadline") } - // TODO: fix the peerNodeInfo.ListenAddr - p.nodeInfo = peerNodeInfo return nil } diff --git a/p2p/switch.go b/p2p/switch.go index f29d1b273..fb9a6ac25 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -267,6 +267,8 @@ func (sw *Switch) stopAndRemovePeer(peer Peer, reason interface{}) { // If no success after all that, it stops trying, and leaves it // to the PEX/Addrbook to find the peer again func (sw *Switch) reconnectToPeer(peer Peer) { + // NOTE this will connect to the self reported address, + // not necessarily the original we dialed netAddr := peer.NodeInfo().NetAddress() start := time.Now() sw.Logger.Info("Reconnecting to peer", "peer", peer) From 775bb85efbcf29b85d6d553d5b71b160d6e4650e Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 23 Jan 2018 21:30:53 -0500 Subject: [PATCH 6/8] p2p/pex: wait to connect to all peers in reactor test --- p2p/pex/pex_reactor_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index c52e45b47..82dafecd4 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -111,7 +111,7 @@ func TestPEXReactorRunning(t *testing.T) { require.Nil(t, err) } - assertSomePeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second) + assertPeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second, N-1) // stop them for _, s := range switches { @@ -119,7 +119,7 @@ func TestPEXReactorRunning(t *testing.T) { } } -func assertSomePeersWithTimeout(t *testing.T, switches []*p2p.Switch, checkPeriod, timeout time.Duration) { +func assertPeersWithTimeout(t *testing.T, switches []*p2p.Switch, checkPeriod, timeout time.Duration, nPeers int) { ticker := time.NewTicker(checkPeriod) remaining := timeout for { @@ -129,7 +129,7 @@ func assertSomePeersWithTimeout(t *testing.T, switches []*p2p.Switch, checkPerio allGood := true for _, s := range switches { outbound, inbound, _ := s.NumPeers() - if outbound+inbound == 0 { + if outbound+inbound < nPeers { allGood = false } } @@ -296,7 +296,7 @@ func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { defer sw.Stop() // 3. check that peer at least connects to seed - assertSomePeersWithTimeout(t, []*p2p.Switch{sw}, 10*time.Millisecond, 10*time.Second) + assertPeersWithTimeout(t, []*p2p.Switch{sw}, 10*time.Millisecond, 10*time.Second, 1) } func TestPEXReactorCrawlStatus(t *testing.T) { From 85816877c6775dcffa8eb187ead28eada5dcc653 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 23 Jan 2018 22:21:17 -0500 Subject: [PATCH 7/8] config: fix addrbook path to go in config --- config/config.go | 4 +++- p2p/pex/file.go | 4 ++-- p2p/pex/known_address.go | 14 +++++++------- test/p2p/pex/test_addrbook.sh | 8 ++++---- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/config/config.go b/config/config.go index 072606b43..e34d9b959 100644 --- a/config/config.go +++ b/config/config.go @@ -22,11 +22,13 @@ var ( defaultGenesisJSONName = "genesis.json" defaultPrivValName = "priv_validator.json" defaultNodeKeyName = "node_key.json" + defaultAddrBookName = "addrbook.json" defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName) defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName) defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName) defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName) + defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName) ) // Config defines the top level configuration for a Tendermint node @@ -278,7 +280,7 @@ type P2PConfig struct { func DefaultP2PConfig() *P2PConfig { return &P2PConfig{ ListenAddress: "tcp://0.0.0.0:46656", - AddrBook: "addrbook.json", + AddrBook: defaultAddrBookPath, AddrBookStrict: true, MaxNumPeers: 50, FlushThrottleTimeout: 100, diff --git a/p2p/pex/file.go b/p2p/pex/file.go index 521fcfcf7..38142dd9d 100644 --- a/p2p/pex/file.go +++ b/p2p/pex/file.go @@ -10,8 +10,8 @@ import ( /* Loading & Saving */ type addrBookJSON struct { - Key string - Addrs []*knownAddress + Key string `json:"key"` + Addrs []*knownAddress `json:"addrs"` } func (a *addrBook) saveToFile(filePath string) { diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index e26fdb7bd..085eb10fa 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -9,13 +9,13 @@ import ( // knownAddress tracks information about a known network address // that is used to determine how viable an address is. type knownAddress struct { - Addr *p2p.NetAddress - Src *p2p.NetAddress - Attempts int32 - LastAttempt time.Time - LastSuccess time.Time - BucketType byte - Buckets []int + Addr *p2p.NetAddress `json:"addr"` + Src *p2p.NetAddress `json:"src"` + Attempts int32 `json:"attempts"` + LastAttempt time.Time `json:"last_attempt"` + LastSuccess time.Time `json:"last_success"` + BucketType byte `json:"bucket_type"` + Buckets []int `json:"buckets"` } func newKnownAddress(addr *p2p.NetAddress, src *p2p.NetAddress) *knownAddress { diff --git a/test/p2p/pex/test_addrbook.sh b/test/p2p/pex/test_addrbook.sh index 1dd26b172..d54bcf428 100644 --- a/test/p2p/pex/test_addrbook.sh +++ b/test/p2p/pex/test_addrbook.sh @@ -17,18 +17,18 @@ CLIENT_NAME="pex_addrbook_$ID" echo "1. restart peer $ID" docker stop "local_testnet_$ID" # preserve addrbook.json -docker cp "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/addrbook.json" "/tmp/addrbook.json" +docker cp "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json" "/tmp/addrbook.json" set +e #CIRCLE docker rm -vf "local_testnet_$ID" set -e # NOTE that we do not provide persistent_peers bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" -docker cp "/tmp/addrbook.json" "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/addrbook.json" +docker cp "/tmp/addrbook.json" "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json" echo "with the following addrbook:" cat /tmp/addrbook.json # exec doesn't work on circle -# docker exec "local_testnet_$ID" cat "/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/addrbook.json" +# docker exec "local_testnet_$ID" cat "/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json" echo "" # if the client runs forever, it means addrbook wasn't saved or was empty @@ -44,7 +44,7 @@ echo "1. restart peer $ID" docker stop "local_testnet_$ID" set +e #CIRCLE docker rm -vf "local_testnet_$ID" -set -e +set -e # NOTE that we do not provide persistent_peers bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" From 8f3bd3f2095b32f1e8fdac8044995303d9f590d4 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 23 Jan 2018 22:25:39 -0500 Subject: [PATCH 8/8] p2p: addrBook.Save() on DialPeersAsync --- p2p/pex/addrbook.go | 9 +++++++++ p2p/switch.go | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index b7f60682d..3a3be6e44 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -27,6 +27,7 @@ const ( // AddrBook is an address book used for tracking peers // so we can gossip about them to others and select // peers to dial. +// TODO: break this up? type AddrBook interface { cmn.Service @@ -53,6 +54,9 @@ type AddrBook interface { // TODO: remove ListOfKnownAddresses() []*knownAddress + + // Persist to disk + Save() } var _ AddrBook = (*addrBook)(nil) @@ -314,6 +318,11 @@ func (a *addrBook) size() int { //---------------------------------------------------------- +// Save persists the address book to disk. +func (a *addrBook) Save() { + a.saveToFile(a.filePath) // thread safe +} + func (a *addrBook) saveRoutine() { defer a.wg.Done() diff --git a/p2p/switch.go b/p2p/switch.go index fb9a6ac25..7b09279ce 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -35,6 +35,7 @@ const ( type AddrBook interface { AddAddress(addr *NetAddress, src *NetAddress) error + Save() } //----------------------------------------------------------------------------- @@ -337,6 +338,9 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b // TODO: move this out of here ? addrBook.AddAddress(netAddr, ourAddr) } + // Persist some peers to disk right away. + // NOTE: integration tests depend on this + addrBook.Save() } // permute the list, dial them in random order.