github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/p2p/node_info.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/tendermint/tendermint/libs/bytes"
     8  	tmstrings "github.com/tendermint/tendermint/libs/strings"
     9  	"github.com/tendermint/tendermint/version"
    10  )
    11  
    12  const (
    13  	maxNodeInfoSize = 10240 // 10KB
    14  	maxNumChannels  = 16    // plenty of room for upgrades, for now
    15  )
    16  
    17  // Max size of the NodeInfo struct
    18  func MaxNodeInfoSize() int {
    19  	return maxNodeInfoSize
    20  }
    21  
    22  //-------------------------------------------------------------
    23  
    24  // NodeInfo exposes basic info of a node
    25  // and determines if we're compatible.
    26  type NodeInfo interface {
    27  	ID() ID
    28  	nodeInfoAddress
    29  	nodeInfoTransport
    30  }
    31  
    32  type nodeInfoAddress interface {
    33  	NetAddress() (*NetAddress, error)
    34  }
    35  
    36  // nodeInfoTransport validates a nodeInfo and checks
    37  // our compatibility with it. It's for use in the handshake.
    38  type nodeInfoTransport interface {
    39  	Validate() error
    40  	CompatibleWith(other NodeInfo) error
    41  }
    42  
    43  //-------------------------------------------------------------
    44  
    45  // ProtocolVersion contains the protocol versions for the software.
    46  type ProtocolVersion struct {
    47  	P2P   version.Protocol `json:"p2p"`
    48  	Block version.Protocol `json:"block"`
    49  	App   version.Protocol `json:"app"`
    50  }
    51  
    52  // defaultProtocolVersion populates the Block and P2P versions using
    53  // the global values, but not the App.
    54  var defaultProtocolVersion = NewProtocolVersion(
    55  	version.P2PProtocol,
    56  	version.BlockProtocol,
    57  	0,
    58  )
    59  
    60  // NewProtocolVersion returns a fully populated ProtocolVersion.
    61  func NewProtocolVersion(p2p, block, app version.Protocol) ProtocolVersion {
    62  	return ProtocolVersion{
    63  		P2P:   p2p,
    64  		Block: block,
    65  		App:   app,
    66  	}
    67  }
    68  
    69  //-------------------------------------------------------------
    70  
    71  // Assert DefaultNodeInfo satisfies NodeInfo
    72  var _ NodeInfo = DefaultNodeInfo{}
    73  
    74  // DefaultNodeInfo is the basic node information exchanged
    75  // between two peers during the Tendermint P2P handshake.
    76  type DefaultNodeInfo struct {
    77  	ProtocolVersion ProtocolVersion `json:"protocol_version"`
    78  
    79  	// Authenticate
    80  	// TODO: replace with NetAddress
    81  	DefaultNodeID ID     `json:"id"`          // authenticated identifier
    82  	ListenAddr    string `json:"listen_addr"` // accepting incoming
    83  
    84  	// Check compatibility.
    85  	// Channels are HexBytes so easier to read as JSON
    86  	Network  string         `json:"network"`  // network/chain ID
    87  	Version  string         `json:"version"`  // major.minor.revision
    88  	Channels bytes.HexBytes `json:"channels"` // channels this node knows about
    89  
    90  	// ASCIIText fields
    91  	Moniker string               `json:"moniker"` // arbitrary moniker
    92  	Other   DefaultNodeInfoOther `json:"other"`   // other application specific data
    93  }
    94  
    95  // DefaultNodeInfoOther is the misc. applcation specific data
    96  type DefaultNodeInfoOther struct {
    97  	TxIndex    string `json:"tx_index"`
    98  	RPCAddress string `json:"rpc_address"`
    99  }
   100  
   101  // ID returns the node's peer ID.
   102  func (info DefaultNodeInfo) ID() ID {
   103  	return info.DefaultNodeID
   104  }
   105  
   106  // Validate checks the self-reported DefaultNodeInfo is safe.
   107  // It returns an error if there
   108  // are too many Channels, if there are any duplicate Channels,
   109  // if the ListenAddr is malformed, or if the ListenAddr is a host name
   110  // that can not be resolved to some IP.
   111  // TODO: constraints for Moniker/Other? Or is that for the UI ?
   112  // JAE: It needs to be done on the client, but to prevent ambiguous
   113  // unicode characters, maybe it's worth sanitizing it here.
   114  // In the future we might want to validate these, once we have a
   115  // name-resolution system up.
   116  // International clients could then use punycode (or we could use
   117  // url-encoding), and we just need to be careful with how we handle that in our
   118  // clients. (e.g. off by default).
   119  func (info DefaultNodeInfo) Validate() error {
   120  
   121  	// ID is already validated.
   122  
   123  	// Validate ListenAddr.
   124  	_, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	// Network is validated in CompatibleWith.
   130  
   131  	// Validate Version
   132  	if len(info.Version) > 0 &&
   133  		(!tmstrings.IsASCIIText(info.Version) || tmstrings.ASCIITrim(info.Version) == "") {
   134  
   135  		return fmt.Errorf("info.Version must be valid ASCII text without tabs, but got %v", info.Version)
   136  	}
   137  
   138  	// Validate Channels - ensure max and check for duplicates.
   139  	if len(info.Channels) > maxNumChannels {
   140  		return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels)
   141  	}
   142  	channels := make(map[byte]struct{})
   143  	for _, ch := range info.Channels {
   144  		_, ok := channels[ch]
   145  		if ok {
   146  			return fmt.Errorf("info.Channels contains duplicate channel id %v", ch)
   147  		}
   148  		channels[ch] = struct{}{}
   149  	}
   150  
   151  	// Validate Moniker.
   152  	if !tmstrings.IsASCIIText(info.Moniker) || tmstrings.ASCIITrim(info.Moniker) == "" {
   153  		return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v", info.Moniker)
   154  	}
   155  
   156  	// Validate Other.
   157  	other := info.Other
   158  	txIndex := other.TxIndex
   159  	switch txIndex {
   160  	case "", "on", "off":
   161  	default:
   162  		return fmt.Errorf("info.Other.TxIndex should be either 'on', 'off', or empty string, got '%v'", txIndex)
   163  	}
   164  	// XXX: Should we be more strict about address formats?
   165  	rpcAddr := other.RPCAddress
   166  	if len(rpcAddr) > 0 && (!tmstrings.IsASCIIText(rpcAddr) || tmstrings.ASCIITrim(rpcAddr) == "") {
   167  		return fmt.Errorf("info.Other.RPCAddress=%v must be valid ASCII text without tabs", rpcAddr)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  // CompatibleWith checks if two DefaultNodeInfo are compatible with eachother.
   174  // CONTRACT: two nodes are compatible if the Block version and network match
   175  // and they have at least one channel in common.
   176  func (info DefaultNodeInfo) CompatibleWith(otherInfo NodeInfo) error {
   177  	other, ok := otherInfo.(DefaultNodeInfo)
   178  	if !ok {
   179  		return fmt.Errorf("wrong NodeInfo type. Expected DefaultNodeInfo, got %v", reflect.TypeOf(otherInfo))
   180  	}
   181  
   182  	if info.ProtocolVersion.Block != other.ProtocolVersion.Block {
   183  		return fmt.Errorf("peer is on a different Block version. Got %v, expected %v",
   184  			other.ProtocolVersion.Block, info.ProtocolVersion.Block)
   185  	}
   186  
   187  	// nodes must be on the same network
   188  	if info.Network != other.Network {
   189  		return fmt.Errorf("peer is on a different network. Got %v, expected %v", other.Network, info.Network)
   190  	}
   191  
   192  	// if we have no channels, we're just testing
   193  	if len(info.Channels) == 0 {
   194  		return nil
   195  	}
   196  
   197  	// for each of our channels, check if they have it
   198  	found := false
   199  OUTER_LOOP:
   200  	for _, ch1 := range info.Channels {
   201  		for _, ch2 := range other.Channels {
   202  			if ch1 == ch2 {
   203  				found = true
   204  				break OUTER_LOOP // only need one
   205  			}
   206  		}
   207  	}
   208  	if !found {
   209  		return fmt.Errorf("peer has no common channels. Our channels: %v ; Peer channels: %v", info.Channels, other.Channels)
   210  	}
   211  	return nil
   212  }
   213  
   214  // NetAddress returns a NetAddress derived from the DefaultNodeInfo -
   215  // it includes the authenticated peer ID and the self-reported
   216  // ListenAddr. Note that the ListenAddr is not authenticated and
   217  // may not match that address actually dialed if its an outbound peer.
   218  func (info DefaultNodeInfo) NetAddress() (*NetAddress, error) {
   219  	idAddr := IDAddressString(info.ID(), info.ListenAddr)
   220  	return NewNetAddressString(idAddr)
   221  }
   222  
   223  //-----------------------------------------------------------
   224  // These methods are for Protobuf Compatibility
   225  
   226  // Size returns the size of the amino encoding, in bytes.
   227  func (info *DefaultNodeInfo) Size() int {
   228  	bs, _ := info.Marshal()
   229  	return len(bs)
   230  }
   231  
   232  // Marshal returns the amino encoding.
   233  func (info *DefaultNodeInfo) Marshal() ([]byte, error) {
   234  	return cdc.MarshalBinaryBare(info)
   235  }
   236  
   237  // MarshalTo calls Marshal and copies to the given buffer.
   238  func (info *DefaultNodeInfo) MarshalTo(data []byte) (int, error) {
   239  	bs, err := info.Marshal()
   240  	if err != nil {
   241  		return -1, err
   242  	}
   243  	return copy(data, bs), nil
   244  }
   245  
   246  // Unmarshal deserializes from amino encoded form.
   247  func (info *DefaultNodeInfo) Unmarshal(bs []byte) error {
   248  	return cdc.UnmarshalBinaryBare(bs, info)
   249  }