github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/node_info.go (about)

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