Browse Source

p2p, types: remove legacy NetAddress type (#7084)

pull/7093/head
Sam Kleinman 3 years ago
committed by GitHub
parent
commit
3646b635d3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 168 additions and 1067 deletions
  1. +12
    -11
      internal/consensus/state_test.go
  2. +5
    -5
      internal/p2p/errors.go
  3. +0
    -69
      internal/p2p/mock/peer.go
  4. +0
    -319
      internal/p2p/mocks/peer.go
  5. +0
    -11
      internal/p2p/netaddress.go
  6. +0
    -78
      internal/p2p/pex/errors.go
  7. +0
    -2
      internal/p2p/router.go
  8. +3
    -3
      internal/p2p/router_test.go
  9. +0
    -32
      internal/p2p/test_util.go
  10. +9
    -4
      internal/p2p/transport.go
  11. +2
    -2
      node/node.go
  12. +0
    -329
      types/netaddress.go
  13. +0
    -183
      types/netaddress_test.go
  14. +1
    -2
      types/node_id.go
  15. +59
    -17
      types/node_info.go
  16. +77
    -0
      types/node_info_test.go

+ 12
- 11
internal/consensus/state_test.go View File

@ -13,7 +13,6 @@ import (
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/crypto/tmhash"
cstypes "github.com/tendermint/tendermint/internal/consensus/types"
p2pmock "github.com/tendermint/tendermint/internal/p2p/mock"
"github.com/tendermint/tendermint/libs/log"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
tmrand "github.com/tendermint/tendermint/libs/rand"
@ -1864,7 +1863,8 @@ func TestStateOutputsBlockPartsStats(t *testing.T) {
// create dummy peer
cs, _ := randState(config, 1)
peer := p2pmock.NewPeer(nil)
peerID, err := types.NewNodeID("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
require.NoError(t, err)
// 1) new block part
parts := types.NewPartSetFromData(tmrand.Bytes(100), 10)
@ -1875,26 +1875,26 @@ func TestStateOutputsBlockPartsStats(t *testing.T) {
}
cs.ProposalBlockParts = types.NewPartSetFromHeader(parts.Header())
cs.handleMsg(msgInfo{msg, peer.ID()})
cs.handleMsg(msgInfo{msg, peerID})
statsMessage := <-cs.statsMsgQueue
require.Equal(t, msg, statsMessage.Msg, "")
require.Equal(t, peer.ID(), statsMessage.PeerID, "")
require.Equal(t, peerID, statsMessage.PeerID, "")
// sending the same part from different peer
cs.handleMsg(msgInfo{msg, "peer2"})
// sending the part with the same height, but different round
msg.Round = 1
cs.handleMsg(msgInfo{msg, peer.ID()})
cs.handleMsg(msgInfo{msg, peerID})
// sending the part from the smaller height
msg.Height = 0
cs.handleMsg(msgInfo{msg, peer.ID()})
cs.handleMsg(msgInfo{msg, peerID})
// sending the part from the bigger height
msg.Height = 3
cs.handleMsg(msgInfo{msg, peer.ID()})
cs.handleMsg(msgInfo{msg, peerID})
select {
case <-cs.statsMsgQueue:
@ -1909,18 +1909,19 @@ func TestStateOutputVoteStats(t *testing.T) {
cs, vss := randState(config, 2)
// create dummy peer
peer := p2pmock.NewPeer(nil)
peerID, err := types.NewNodeID("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
require.NoError(t, err)
randBytes := tmrand.Bytes(tmhash.Size)
vote := signVote(vss[1], config, tmproto.PrecommitType, randBytes, types.PartSetHeader{})
voteMessage := &VoteMessage{vote}
cs.handleMsg(msgInfo{voteMessage, peer.ID()})
cs.handleMsg(msgInfo{voteMessage, peerID})
statsMessage := <-cs.statsMsgQueue
require.Equal(t, voteMessage, statsMessage.Msg, "")
require.Equal(t, peer.ID(), statsMessage.PeerID, "")
require.Equal(t, peerID, statsMessage.PeerID, "")
// sending the same part from different peer
cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2"})
@ -1929,7 +1930,7 @@ func TestStateOutputVoteStats(t *testing.T) {
incrementHeight(vss[1])
vote = signVote(vss[1], config, tmproto.PrecommitType, randBytes, types.PartSetHeader{})
cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()})
cs.handleMsg(msgInfo{&VoteMessage{vote}, peerID})
select {
case <-cs.statsMsgQueue:


+ 5
- 5
internal/p2p/errors.go View File

@ -17,7 +17,7 @@ func (e ErrFilterTimeout) Error() string {
// ErrRejected indicates that a Peer was rejected carrying additional
// information as to the reason.
type ErrRejected struct {
addr NetAddress
addr NodeAddress
conn net.Conn
err error
id types.NodeID
@ -30,7 +30,7 @@ type ErrRejected struct {
}
// Addr returns the NetAddress for the rejected Peer.
func (e ErrRejected) Addr() NetAddress {
func (e ErrRejected) Addr() NodeAddress {
return e.addr
}
@ -120,15 +120,15 @@ func (e ErrSwitchDuplicatePeerIP) Error() string {
// ErrSwitchConnectToSelf to be raised when trying to connect to itself.
type ErrSwitchConnectToSelf struct {
Addr *NetAddress
Addr *NodeAddress
}
func (e ErrSwitchConnectToSelf) Error() string {
return fmt.Sprintf("connect to self: %v", e.Addr)
return fmt.Sprintf("connect to self: %s", e.Addr)
}
type ErrSwitchAuthenticationFailure struct {
Dialed *NetAddress
Dialed *NodeAddress
Got types.NodeID
}


+ 0
- 69
internal/p2p/mock/peer.go View File

@ -1,69 +0,0 @@
package mock
import (
"net"
"github.com/tendermint/tendermint/internal/p2p"
"github.com/tendermint/tendermint/libs/service"
"github.com/tendermint/tendermint/types"
)
type Peer struct {
*service.BaseService
ip net.IP
id types.NodeID
addr *p2p.NetAddress
kv map[string]interface{}
Outbound, Persistent bool
}
// NewPeer creates and starts a new mock peer. If the ip
// is nil, random routable address is used.
func NewPeer(ip net.IP) *Peer {
var netAddr *p2p.NetAddress
if ip == nil {
_, netAddr = p2p.CreateRoutableAddr()
} else {
netAddr = types.NewNetAddressIPPort(ip, 26656)
}
nodeKey := types.GenNodeKey()
netAddr.ID = nodeKey.ID
mp := &Peer{
ip: ip,
id: nodeKey.ID,
addr: netAddr,
kv: make(map[string]interface{}),
}
mp.BaseService = service.NewBaseService(nil, "MockPeer", mp)
if err := mp.Start(); err != nil {
panic(err)
}
return mp
}
func (mp *Peer) FlushStop() { mp.Stop() } //nolint:errcheck //ignore error
func (mp *Peer) TrySend(chID byte, msgBytes []byte) bool { return true }
func (mp *Peer) Send(chID byte, msgBytes []byte) bool { return true }
func (mp *Peer) NodeInfo() types.NodeInfo {
return types.NodeInfo{
NodeID: mp.addr.ID,
ListenAddr: mp.addr.DialString(),
}
}
func (mp *Peer) ID() types.NodeID { return mp.id }
func (mp *Peer) IsOutbound() bool { return mp.Outbound }
func (mp *Peer) IsPersistent() bool { return mp.Persistent }
func (mp *Peer) Get(key string) interface{} {
if value, ok := mp.kv[key]; ok {
return value
}
return nil
}
func (mp *Peer) Set(key string, value interface{}) {
mp.kv[key] = value
}
func (mp *Peer) RemoteIP() net.IP { return mp.ip }
func (mp *Peer) SocketAddr() *p2p.NetAddress { return mp.addr }
func (mp *Peer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} }
func (mp *Peer) CloseConn() error { return nil }

+ 0
- 319
internal/p2p/mocks/peer.go View File

@ -1,319 +0,0 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
log "github.com/tendermint/tendermint/libs/log"
mock "github.com/stretchr/testify/mock"
net "net"
types "github.com/tendermint/tendermint/types"
)
// Peer is an autogenerated mock type for the Peer type
type Peer struct {
mock.Mock
}
// CloseConn provides a mock function with given fields:
func (_m *Peer) CloseConn() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// FlushStop provides a mock function with given fields:
func (_m *Peer) FlushStop() {
_m.Called()
}
// Get provides a mock function with given fields: _a0
func (_m *Peer) Get(_a0 string) interface{} {
ret := _m.Called(_a0)
var r0 interface{}
if rf, ok := ret.Get(0).(func(string) interface{}); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(interface{})
}
}
return r0
}
// ID provides a mock function with given fields:
func (_m *Peer) ID() types.NodeID {
ret := _m.Called()
var r0 types.NodeID
if rf, ok := ret.Get(0).(func() types.NodeID); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(types.NodeID)
}
return r0
}
// IsOutbound provides a mock function with given fields:
func (_m *Peer) IsOutbound() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsPersistent provides a mock function with given fields:
func (_m *Peer) IsPersistent() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// IsRunning provides a mock function with given fields:
func (_m *Peer) IsRunning() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// NodeInfo provides a mock function with given fields:
func (_m *Peer) NodeInfo() types.NodeInfo {
ret := _m.Called()
var r0 types.NodeInfo
if rf, ok := ret.Get(0).(func() types.NodeInfo); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(types.NodeInfo)
}
return r0
}
// OnReset provides a mock function with given fields:
func (_m *Peer) OnReset() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// OnStart provides a mock function with given fields:
func (_m *Peer) OnStart() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// OnStop provides a mock function with given fields:
func (_m *Peer) OnStop() {
_m.Called()
}
// Quit provides a mock function with given fields:
func (_m *Peer) Quit() <-chan struct{} {
ret := _m.Called()
var r0 <-chan struct{}
if rf, ok := ret.Get(0).(func() <-chan struct{}); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(<-chan struct{})
}
}
return r0
}
// RemoteAddr provides a mock function with given fields:
func (_m *Peer) RemoteAddr() net.Addr {
ret := _m.Called()
var r0 net.Addr
if rf, ok := ret.Get(0).(func() net.Addr); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(net.Addr)
}
}
return r0
}
// RemoteIP provides a mock function with given fields:
func (_m *Peer) RemoteIP() net.IP {
ret := _m.Called()
var r0 net.IP
if rf, ok := ret.Get(0).(func() net.IP); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(net.IP)
}
}
return r0
}
// Reset provides a mock function with given fields:
func (_m *Peer) Reset() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Send provides a mock function with given fields: _a0, _a1
func (_m *Peer) Send(_a0 byte, _a1 []byte) bool {
ret := _m.Called(_a0, _a1)
var r0 bool
if rf, ok := ret.Get(0).(func(byte, []byte) bool); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Set provides a mock function with given fields: _a0, _a1
func (_m *Peer) Set(_a0 string, _a1 interface{}) {
_m.Called(_a0, _a1)
}
// SetLogger provides a mock function with given fields: _a0
func (_m *Peer) SetLogger(_a0 log.Logger) {
_m.Called(_a0)
}
// SocketAddr provides a mock function with given fields:
func (_m *Peer) SocketAddr() *types.NetAddress {
ret := _m.Called()
var r0 *types.NetAddress
if rf, ok := ret.Get(0).(func() *types.NetAddress); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.NetAddress)
}
}
return r0
}
// Start provides a mock function with given fields:
func (_m *Peer) Start() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Stop provides a mock function with given fields:
func (_m *Peer) Stop() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// String provides a mock function with given fields:
func (_m *Peer) String() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
// TrySend provides a mock function with given fields: _a0, _a1
func (_m *Peer) TrySend(_a0 byte, _a1 []byte) bool {
ret := _m.Called(_a0, _a1)
var r0 bool
if rf, ok := ret.Get(0).(func(byte, []byte) bool); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// Wait provides a mock function with given fields:
func (_m *Peer) Wait() {
_m.Called()
}

+ 0
- 11
internal/p2p/netaddress.go View File

@ -1,11 +0,0 @@
// Modified for Tendermint
// Originally Copyright (c) 2013-2014 Conformal Systems LLC.
// https://github.com/conformal/btcd/blob/master/LICENSE
package p2p
import (
"github.com/tendermint/tendermint/types"
)
type NetAddress = types.NetAddress

+ 0
- 78
internal/p2p/pex/errors.go View File

@ -1,78 +0,0 @@
package pex
import (
"errors"
"fmt"
"github.com/tendermint/tendermint/internal/p2p"
)
type ErrAddrBookNonRoutable struct {
Addr *p2p.NetAddress
}
func (err ErrAddrBookNonRoutable) Error() string {
return fmt.Sprintf("Cannot add non-routable address %v", err.Addr)
}
type ErrAddrBookSelf struct {
Addr *p2p.NetAddress
}
func (err ErrAddrBookSelf) Error() string {
return fmt.Sprintf("Cannot add ourselves with address %v", err.Addr)
}
type ErrAddrBookPrivate struct {
Addr *p2p.NetAddress
}
func (err ErrAddrBookPrivate) Error() string {
return fmt.Sprintf("Cannot add private peer with address %v", err.Addr)
}
func (err ErrAddrBookPrivate) PrivateAddr() bool {
return true
}
type ErrAddrBookPrivateSrc struct {
Src *p2p.NetAddress
}
func (err ErrAddrBookPrivateSrc) Error() string {
return fmt.Sprintf("Cannot add peer coming from private peer with address %v", err.Src)
}
func (err ErrAddrBookPrivateSrc) PrivateAddr() bool {
return true
}
type ErrAddrBookNilAddr struct {
Addr *p2p.NetAddress
Src *p2p.NetAddress
}
func (err ErrAddrBookNilAddr) Error() string {
return fmt.Sprintf("Cannot add a nil address. Got (addr, src) = (%v, %v)", err.Addr, err.Src)
}
type ErrAddrBookInvalidAddr struct {
Addr *p2p.NetAddress
AddrErr error
}
func (err ErrAddrBookInvalidAddr) Error() string {
return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr)
}
// ErrAddressBanned is thrown when the address has been banned and therefore cannot be used
type ErrAddressBanned struct {
Addr *p2p.NetAddress
}
func (err ErrAddressBanned) Error() string {
return fmt.Sprintf("Address: %v is currently banned", err.Addr)
}
// ErrUnsolicitedList is thrown when a peer provides a list of addresses that have not been asked for.
var ErrUnsolicitedList = errors.New("unsolicited pexAddrsMessage")

+ 0
- 2
internal/p2p/router.go View File

@ -1012,13 +1012,11 @@ func (r *Router) NodeInfo() types.NodeInfo {
// OnStart implements service.Service.
func (r *Router) OnStart() error {
netAddr, _ := r.nodeInfo.NetAddress()
r.Logger.Info(
"starting router",
"node_id", r.nodeInfo.NodeID,
"channels", r.nodeInfo.Channels,
"listen_addr", r.nodeInfo.ListenAddr,
"net_addr", netAddr,
)
go r.dialPeers()


+ 3
- 3
internal/p2p/router_test.go View File

@ -367,7 +367,7 @@ func TestRouter_AcceptPeers(t *testing.T) {
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept").Once().Return(mockConnection, nil)
mockTransport.On("Accept").Once().Return(nil, io.EOF)
mockTransport.On("Accept").Maybe().Return(nil, io.EOF)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@ -755,7 +755,7 @@ func TestRouter_EvictPeers(t *testing.T) {
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept").Once().Return(mockConnection, nil)
mockTransport.On("Accept").Once().Return(nil, io.EOF)
mockTransport.On("Accept").Maybe().Return(nil, io.EOF)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
@ -869,7 +869,7 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) {
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
mockTransport.On("Close").Return(nil)
mockTransport.On("Accept").Once().Return(mockConnection, nil)
mockTransport.On("Accept").Once().Return(nil, io.EOF)
mockTransport.On("Accept").Maybe().Return(nil, io.EOF)
// Set up and start the router.
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})


+ 0
- 32
internal/p2p/test_util.go View File

@ -1,32 +0,0 @@
package p2p
import (
"fmt"
mrand "math/rand"
tmrand "github.com/tendermint/tendermint/libs/rand"
"github.com/tendermint/tendermint/types"
)
//------------------------------------------------
// nolint:gosec // G404: Use of weak random number generator
func CreateRoutableAddr() (addr string, netAddr *NetAddress) {
for {
var err error
addr = fmt.Sprintf("%X@%v.%v.%v.%v:26656",
tmrand.Bytes(20),
mrand.Int()%256,
mrand.Int()%256,
mrand.Int()%256,
mrand.Int()%256)
netAddr, err = types.NewNetAddressString(addr)
if err != nil {
panic(err)
}
if netAddr.Routable() {
break
}
}
return
}

+ 9
- 4
internal/p2p/transport.go View File

@ -122,12 +122,17 @@ type Endpoint struct {
}
// NewEndpoint constructs an Endpoint from a types.NetAddress structure.
func NewEndpoint(na *types.NetAddress) Endpoint {
func NewEndpoint(addr string) (Endpoint, error) {
ip, port, err := types.ParseAddressString(addr)
if err != nil {
return Endpoint{}, err
}
return Endpoint{
Protocol: MConnProtocol,
IP: na.IP,
Port: na.Port,
}
IP: ip,
Port: port,
}, nil
}
// NodeAddress converts the endpoint into a NodeAddress for the given node ID.


+ 2
- 2
node/node.go View File

@ -520,11 +520,11 @@ func (n *nodeImpl) OnStart() error {
}
// Start the transport.
addr, err := types.NewNetAddressString(n.nodeKey.ID.AddressString(n.config.P2P.ListenAddress))
ep, err := p2p.NewEndpoint(n.nodeKey.ID.AddressString(n.config.P2P.ListenAddress))
if err != nil {
return err
}
if err := n.transport.Listen(p2p.NewEndpoint(addr)); err != nil {
if err := n.transport.Listen(ep); err != nil {
return err
}


+ 0
- 329
types/netaddress.go View File

@ -1,329 +0,0 @@
// Modified for Tendermint
// Originally Copyright (c) 2013-2014 Conformal Systems LLC.
// https://github.com/conformal/btcd/blob/master/LICENSE
package types
import (
"errors"
"flag"
"fmt"
"net"
"strconv"
"strings"
"time"
)
// EmptyNetAddress defines the string representation of an empty NetAddress
const EmptyNetAddress = "<nil-NetAddress>"
// NetAddress defines information about a peer on the network
// including its ID, IP address, and port.
type NetAddress struct {
ID NodeID `json:"id"`
IP net.IP `json:"ip"`
Port uint16 `json:"port"`
}
// NewNetAddress returns a new NetAddress using the provided TCP
// address. When testing, other net.Addr (except TCP) will result in
// using 0.0.0.0:0. When normal run, other net.Addr (except TCP) will
// panic. Panics if ID is invalid.
// TODO: socks proxies?
func NewNetAddress(id NodeID, addr net.Addr) *NetAddress {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
if flag.Lookup("test.v") == nil { // normal run
panic(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr))
} else { // in testing
netAddr := NewNetAddressIPPort(net.IP("127.0.0.1"), 0)
netAddr.ID = id
return netAddr
}
}
if err := id.Validate(); err != nil {
panic(fmt.Sprintf("Invalid ID %v: %v (addr: %v)", id, err, addr))
}
ip := tcpAddr.IP
port := uint16(tcpAddr.Port)
na := NewNetAddressIPPort(ip, port)
na.ID = id
return na
}
// NewNetAddressIPPort returns a new NetAddress using the provided IP
// and port number.
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
return &NetAddress{
IP: ip,
Port: port,
}
}
// NewNetAddressString returns a new NetAddress using the provided address in
// the form of "ID@IP:Port".
// Also resolves the host if host is not an IP.
// Errors are of type ErrNetAddressXxx where Xxx is in (NoID, Invalid, Lookup)
func NewNetAddressString(addr string) (*NetAddress, error) {
addrWithoutProtocol := removeProtocolIfDefined(addr)
spl := strings.Split(addrWithoutProtocol, "@")
if len(spl) != 2 {
return nil, ErrNetAddressNoID{addr}
}
id, err := NewNodeID(spl[0])
if err != nil {
return nil, ErrNetAddressInvalid{addrWithoutProtocol, err}
}
if err := id.Validate(); err != nil {
return nil, ErrNetAddressInvalid{addrWithoutProtocol, err}
}
addrWithoutProtocol = spl[1]
// get host and port
host, portStr, err := net.SplitHostPort(addrWithoutProtocol)
if err != nil {
return nil, ErrNetAddressInvalid{addrWithoutProtocol, err}
}
if len(host) == 0 {
return nil, ErrNetAddressInvalid{
addrWithoutProtocol,
errors.New("host is empty")}
}
ip := net.ParseIP(host)
if ip == nil {
ips, err := net.LookupIP(host)
if err != nil {
return nil, ErrNetAddressLookup{host, err}
}
ip = ips[0]
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, ErrNetAddressInvalid{portStr, err}
}
na := NewNetAddressIPPort(ip, uint16(port))
na.ID = id
return na, nil
}
// Equals reports whether na and other are the same addresses,
// including their ID, IP, and Port.
func (na *NetAddress) Equals(other interface{}) bool {
if o, ok := other.(*NetAddress); ok {
return na.String() == o.String()
}
return false
}
// Same returns true is na has the same non-empty ID or DialString as other.
func (na *NetAddress) Same(other interface{}) bool {
if o, ok := other.(*NetAddress); ok {
if na.DialString() == o.DialString() {
return true
}
if na.ID != "" && na.ID == o.ID {
return true
}
}
return false
}
// String representation: <ID>@<IP>:<PORT>
func (na *NetAddress) String() string {
if na == nil {
return EmptyNetAddress
}
addrStr := na.DialString()
if na.ID != "" {
addrStr = na.ID.AddressString(addrStr)
}
return addrStr
}
func (na *NetAddress) DialString() string {
if na == nil {
return "<nil-NetAddress>"
}
return net.JoinHostPort(
na.IP.String(),
strconv.FormatUint(uint64(na.Port), 10),
)
}
// Dial calls net.Dial on the address.
func (na *NetAddress) Dial() (net.Conn, error) {
conn, err := net.Dial("tcp", na.DialString())
if err != nil {
return nil, err
}
return conn, nil
}
// DialTimeout calls net.DialTimeout on the address.
func (na *NetAddress) DialTimeout(timeout time.Duration) (net.Conn, error) {
conn, err := net.DialTimeout("tcp", na.DialString(), timeout)
if err != nil {
return nil, err
}
return conn, nil
}
// Routable returns true if the address is routable.
func (na *NetAddress) Routable() bool {
if err := na.Valid(); err != nil {
return false
}
// TODO(oga) bitcoind doesn't include RFC3849 here, but should we?
return !(na.RFC1918() || na.RFC3927() || na.RFC4862() ||
na.RFC4193() || na.RFC4843() || na.Local())
}
// For IPv4 these are either a 0 or all bits set address. For IPv6 a zero
// address or one that matches the RFC3849 documentation address format.
func (na *NetAddress) Valid() error {
if err := na.ID.Validate(); err != nil {
return fmt.Errorf("invalid ID: %w", err)
}
if na.IP == nil {
return errors.New("no IP")
}
if na.IP.IsUnspecified() || na.RFC3849() || na.IP.Equal(net.IPv4bcast) {
return errors.New("invalid IP")
}
return nil
}
// Local returns true if it is a local address.
func (na *NetAddress) Local() bool {
return na.IP.IsLoopback() || zero4.Contains(na.IP)
}
// ReachabilityTo checks whenever o can be reached from na.
func (na *NetAddress) ReachabilityTo(o *NetAddress) int {
const (
Unreachable = 0
Default = iota
Teredo
Ipv6Weak
Ipv4
Ipv6Strong
)
switch {
case !na.Routable():
return Unreachable
case na.RFC4380():
switch {
case !o.Routable():
return Default
case o.RFC4380():
return Teredo
case o.IP.To4() != nil:
return Ipv4
default: // ipv6
return Ipv6Weak
}
case na.IP.To4() != nil:
if o.Routable() && o.IP.To4() != nil {
return Ipv4
}
return Default
default: /* ipv6 */
var tunneled bool
// Is our v6 is tunneled?
if o.RFC3964() || o.RFC6052() || o.RFC6145() {
tunneled = true
}
switch {
case !o.Routable():
return Default
case o.RFC4380():
return Teredo
case o.IP.To4() != nil:
return Ipv4
case tunneled:
// only prioritize ipv6 if we aren't tunneling it.
return Ipv6Weak
}
return Ipv6Strong
}
}
// RFC1918: IPv4 Private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
// RFC3849: IPv6 Documentation address (2001:0DB8::/32)
// RFC3927: IPv4 Autoconfig (169.254.0.0/16)
// RFC3964: IPv6 6to4 (2002::/16)
// RFC4193: IPv6 unique local (FC00::/7)
// RFC4380: IPv6 Teredo tunneling (2001::/32)
// RFC4843: IPv6 ORCHID: (2001:10::/28)
// RFC4862: IPv6 Autoconfig (FE80::/64)
// RFC6052: IPv6 well known prefix (64:FF9B::/96)
// RFC6145: IPv6 IPv4 translated address ::FFFF:0:0:0/96
var rfc1918_10 = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)}
var rfc1918_192 = net.IPNet{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)}
var rfc1918_172 = net.IPNet{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)}
var rfc3849 = net.IPNet{IP: net.ParseIP("2001:0DB8::"), Mask: net.CIDRMask(32, 128)}
var rfc3927 = net.IPNet{IP: net.ParseIP("169.254.0.0"), Mask: net.CIDRMask(16, 32)}
var rfc3964 = net.IPNet{IP: net.ParseIP("2002::"), Mask: net.CIDRMask(16, 128)}
var rfc4193 = net.IPNet{IP: net.ParseIP("FC00::"), Mask: net.CIDRMask(7, 128)}
var rfc4380 = net.IPNet{IP: net.ParseIP("2001::"), Mask: net.CIDRMask(32, 128)}
var rfc4843 = net.IPNet{IP: net.ParseIP("2001:10::"), Mask: net.CIDRMask(28, 128)}
var rfc4862 = net.IPNet{IP: net.ParseIP("FE80::"), Mask: net.CIDRMask(64, 128)}
var rfc6052 = net.IPNet{IP: net.ParseIP("64:FF9B::"), Mask: net.CIDRMask(96, 128)}
var rfc6145 = net.IPNet{IP: net.ParseIP("::FFFF:0:0:0"), Mask: net.CIDRMask(96, 128)}
var zero4 = net.IPNet{IP: net.ParseIP("0.0.0.0"), Mask: net.CIDRMask(8, 32)}
var (
// onionCatNet defines the IPv6 address block used to support Tor.
// bitcoind encodes a .onion address as a 16 byte number by decoding the
// address prior to the .onion (i.e. the key hash) base32 into a ten
// byte number. It then stores the first 6 bytes of the address as
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43.
//
// This is the same range used by OnionCat, which is part part of the
// RFC4193 unique local IPv6 range.
//
// In summary the format is:
// { magic 6 bytes, 10 bytes base32 decode of key hash }
onionCatNet = ipNet("fd87:d87e:eb43::", 48, 128)
)
func (na *NetAddress) RFC1918() bool {
return rfc1918_10.Contains(na.IP) ||
rfc1918_192.Contains(na.IP) ||
rfc1918_172.Contains(na.IP)
}
func (na *NetAddress) RFC3849() bool { return rfc3849.Contains(na.IP) }
func (na *NetAddress) RFC3927() bool { return rfc3927.Contains(na.IP) }
func (na *NetAddress) RFC3964() bool { return rfc3964.Contains(na.IP) }
func (na *NetAddress) RFC4193() bool { return rfc4193.Contains(na.IP) }
func (na *NetAddress) RFC4380() bool { return rfc4380.Contains(na.IP) }
func (na *NetAddress) RFC4843() bool { return rfc4843.Contains(na.IP) }
func (na *NetAddress) RFC4862() bool { return rfc4862.Contains(na.IP) }
func (na *NetAddress) RFC6052() bool { return rfc6052.Contains(na.IP) }
func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) }
func (na *NetAddress) OnionCatTor() bool { return onionCatNet.Contains(na.IP) }
func removeProtocolIfDefined(addr string) string {
if strings.Contains(addr, "://") {
return strings.Split(addr, "://")[1]
}
return addr
}
// ipNet returns a net.IPNet struct given the passed IP address string, number
// of one bits to include at the start of the mask, and the total number of bits
// for the mask.
func ipNet(ip string, ones, bits int) net.IPNet {
return net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(ones, bits)}
}

+ 0
- 183
types/netaddress_test.go View File

@ -1,183 +0,0 @@
package types
import (
"net"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNetAddress_String(t *testing.T) {
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
require.Nil(t, err)
netAddr := NewNetAddress("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", tcpAddr)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = netAddr.String()
}()
}
wg.Wait()
s := netAddr.String()
require.Equal(t, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", s)
}
func TestNewNetAddress(t *testing.T) {
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
require.Nil(t, err)
assert.Panics(t, func() {
NewNetAddress("", tcpAddr)
})
addr := NewNetAddress("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", tcpAddr)
assert.Equal(t, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", addr.String())
assert.NotPanics(t, func() {
NewNetAddress("", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
}, "Calling NewNetAddress with UDPAddr should not panic in testing")
}
func TestNewNetAddressString(t *testing.T) {
testCases := []struct {
name string
addr string
expected string
correct bool
}{
{"no node id and no protocol", "127.0.0.1:8080", "", false},
{"no node id w/ tcp input", "tcp://127.0.0.1:8080", "", false},
{"no node id w/ udp input", "udp://127.0.0.1:8080", "", false},
{
"no protocol",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{
"tcp input",
"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{
"udp input",
"udp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{"malformed tcp input", "tcp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{"malformed udp input", "udp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
// {"127.0.0:8080", false},
{"invalid host", "notahost", "", false},
{"invalid port", "127.0.0.1:notapath", "", false},
{"invalid host w/ port", "notahost:8080", "", false},
{"just a port", "8082", "", false},
{"non-existent port", "127.0.0:8080000", "", false},
{"too short nodeId", "deadbeef@127.0.0.1:8080", "", false},
{"too short, not hex nodeId", "this-isnot-hex@127.0.0.1:8080", "", false},
{"not hex nodeId", "xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{"too short nodeId w/tcp", "tcp://deadbeef@127.0.0.1:8080", "", false},
{"too short notHex nodeId w/tcp", "tcp://this-isnot-hex@127.0.0.1:8080", "", false},
{"notHex nodeId w/tcp", "tcp://xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{
"correct nodeId w/tcp",
"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{"no node id", "tcp://@127.0.0.1:8080", "", false},
{"no node id or IP", "tcp://@", "", false},
{"tcp no host, w/ port", "tcp://:26656", "", false},
{"empty", "", "", false},
{"node id delimiter 1", "@", "", false},
{"node id delimiter 2", " @", "", false},
{"node id delimiter 3", " @ ", "", false},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
addr, err := NewNetAddressString(tc.addr)
if tc.correct {
if assert.Nil(t, err, tc.addr) {
assert.Equal(t, tc.expected, addr.String())
}
} else {
assert.NotNil(t, err, tc.addr)
}
})
}
}
func TestNewNetAddressIPPort(t *testing.T) {
addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)
assert.Equal(t, "127.0.0.1:8080", addr.String())
}
func TestNetAddressProperties(t *testing.T) {
// TODO add more test cases
testCases := []struct {
addr string
valid bool
local bool
routable bool
}{
{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true, true, false},
{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", true, false, true},
}
for _, tc := range testCases {
addr, err := NewNetAddressString(tc.addr)
require.Nil(t, err)
err = addr.Valid()
if tc.valid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
assert.Equal(t, tc.local, addr.Local())
assert.Equal(t, tc.routable, addr.Routable())
}
}
func TestNetAddressReachabilityTo(t *testing.T) {
// TODO add more test cases
testCases := []struct {
addr string
other string
reachability int
}{
{
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8081",
0,
},
{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", 1},
}
for _, tc := range testCases {
addr, err := NewNetAddressString(tc.addr)
require.Nil(t, err)
other, err := NewNetAddressString(tc.other)
require.Nil(t, err)
assert.Equal(t, tc.reachability, addr.ReachabilityTo(other))
}
}

+ 1
- 2
types/node_id.go View File

@ -31,8 +31,7 @@ func NewNodeID(nodeID string) (NodeID, error) {
// IDAddressString returns id@hostPort. It strips the leading
// protocol from protocolHostPort if it exists.
func (id NodeID) AddressString(protocolHostPort string) string {
hostPort := removeProtocolIfDefined(protocolHostPort)
return fmt.Sprintf("%s@%s", id, hostPort)
return fmt.Sprintf("%s@%s", id, removeProtocolIfDefined(protocolHostPort))
}
// NodeIDFromPubKey creates a node ID from a given PubKey address.


+ 59
- 17
types/node_info.go View File

@ -3,6 +3,9 @@ package types
import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/tendermint/tendermint/libs/bytes"
tmstrings "github.com/tendermint/tendermint/libs/strings"
@ -74,17 +77,10 @@ func (info NodeInfo) ID() NodeID {
// url-encoding), and we just need to be careful with how we handle that in our
// clients. (e.g. off by default).
func (info NodeInfo) Validate() error {
// ID is already validated.
// Validate ListenAddr.
_, err := NewNetAddressString(info.ID().AddressString(info.ListenAddr))
if err != nil {
if _, _, err := ParseAddressString(info.ID().AddressString(info.ListenAddr)); err != nil {
return err
}
// Network is validated in CompatibleWith.
// Validate Version
if len(info.Version) > 0 &&
(!tmstrings.IsASCIIText(info.Version) || tmstrings.ASCIITrim(info.Version) == "") {
@ -163,15 +159,6 @@ OUTER_LOOP:
return nil
}
// 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, error) {
idAddr := info.ID().AddressString(info.ListenAddr)
return NewNetAddressString(idAddr)
}
// AddChannel is used by the router when a channel is opened to add it to the node info
func (info *NodeInfo) AddChannel(channel uint16) {
// check that the channel doesn't already exist
@ -244,3 +231,58 @@ func NodeInfoFromProto(pb *tmp2p.NodeInfo) (NodeInfo, error) {
return dni, nil
}
// ParseAddressString reads an address string, and returns the IP
// address and port information, returning an error for any validation
// errors.
func ParseAddressString(addr string) (net.IP, uint16, error) {
addrWithoutProtocol := removeProtocolIfDefined(addr)
spl := strings.Split(addrWithoutProtocol, "@")
if len(spl) != 2 {
return nil, 0, errors.New("invalid address")
}
id, err := NewNodeID(spl[0])
if err != nil {
return nil, 0, err
}
if err := id.Validate(); err != nil {
return nil, 0, err
}
addrWithoutProtocol = spl[1]
// get host and port
host, portStr, err := net.SplitHostPort(addrWithoutProtocol)
if err != nil {
return nil, 0, err
}
if len(host) == 0 {
return nil, 0, err
}
ip := net.ParseIP(host)
if ip == nil {
ips, err := net.LookupIP(host)
if err != nil {
return nil, 0, err
}
ip = ips[0]
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, 0, err
}
return ip, uint16(port), nil
}
func removeProtocolIfDefined(addr string) string {
if strings.Contains(addr, "://") {
return strings.Split(addr, "://")[1]
}
return addr
}

+ 77
- 0
types/node_info_test.go View File

@ -173,3 +173,80 @@ func TestNodeInfoAddChannel(t *testing.T) {
nodeInfo.AddChannel(2)
require.Contains(t, nodeInfo.Channels, byte(0x02))
}
func TestParseAddressString(t *testing.T) {
testCases := []struct {
name string
addr string
expected string
correct bool
}{
{"no node id and no protocol", "127.0.0.1:8080", "", false},
{"no node id w/ tcp input", "tcp://127.0.0.1:8080", "", false},
{"no node id w/ udp input", "udp://127.0.0.1:8080", "", false},
{
"no protocol",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{
"tcp input",
"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{
"udp input",
"udp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{"malformed tcp input", "tcp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{"malformed udp input", "udp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
// {"127.0.0:8080", false},
{"invalid host", "notahost", "", false},
{"invalid port", "127.0.0.1:notapath", "", false},
{"invalid host w/ port", "notahost:8080", "", false},
{"just a port", "8082", "", false},
{"non-existent port", "127.0.0:8080000", "", false},
{"too short nodeId", "deadbeef@127.0.0.1:8080", "", false},
{"too short, not hex nodeId", "this-isnot-hex@127.0.0.1:8080", "", false},
{"not hex nodeId", "xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{"too short nodeId w/tcp", "tcp://deadbeef@127.0.0.1:8080", "", false},
{"too short notHex nodeId w/tcp", "tcp://this-isnot-hex@127.0.0.1:8080", "", false},
{"notHex nodeId w/tcp", "tcp://xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
{
"correct nodeId w/tcp",
"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
true,
},
{"no node id", "tcp://@127.0.0.1:8080", "", false},
{"no node id or IP", "tcp://@", "", false},
{"tcp no host, w/ port", "tcp://:26656", "", false},
{"empty", "", "", false},
{"node id delimiter 1", "@", "", false},
{"node id delimiter 2", " @", "", false},
{"node id delimiter 3", " @ ", "", false},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
addr, port, err := ParseAddressString(tc.addr)
if tc.correct {
require.Nil(t, err, tc.addr)
assert.Contains(t, tc.expected, addr.String())
assert.Contains(t, tc.expected, fmt.Sprint(port))
} else {
assert.Error(t, err, "%v", tc.addr)
}
})
}
}

Loading…
Cancel
Save