github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/types/node_info.go (about) 1 package types 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "strconv" 8 "strings" 9 10 "github.com/ari-anchor/sei-tendermint/libs/bytes" 11 tmstrings "github.com/ari-anchor/sei-tendermint/libs/strings" 12 tmp2p "github.com/ari-anchor/sei-tendermint/proto/tendermint/p2p" 13 ) 14 15 const ( 16 maxNodeInfoSize = 10240 // 10KB 17 maxNumChannels = 16 // plenty of room for upgrades, for now 18 ) 19 20 // Max size of the NodeInfo struct 21 func MaxNodeInfoSize() int { 22 return maxNodeInfoSize 23 } 24 25 // ProtocolVersion contains the protocol versions for the software. 26 type ProtocolVersion struct { 27 P2P uint64 `json:"p2p,string"` 28 Block uint64 `json:"block,string"` 29 App uint64 `json:"app,string"` 30 } 31 32 //------------------------------------------------------------- 33 34 // NodeInfo is the basic node information exchanged 35 // between two peers during the Tendermint P2P handshake. 36 type NodeInfo struct { 37 ProtocolVersion ProtocolVersion `json:"protocol_version"` 38 39 // Authenticate 40 NodeID NodeID `json:"id"` // authenticated identifier 41 ListenAddr string `json:"listen_addr"` // accepting incoming 42 43 // Check compatibility. 44 // Channels are HexBytes so easier to read as JSON 45 Network string `json:"network"` // network/chain ID 46 Version string `json:"version"` // major.minor.revision 47 // FIXME: This should be changed to uint16 to be consistent with the updated channel type 48 Channels bytes.HexBytes `json:"channels"` // channels this node knows about 49 50 // ASCIIText fields 51 Moniker string `json:"moniker"` // arbitrary moniker 52 Other NodeInfoOther `json:"other"` // other application specific data 53 } 54 55 // NodeInfoOther is the misc. applcation specific data 56 type NodeInfoOther struct { 57 TxIndex string `json:"tx_index"` 58 RPCAddress string `json:"rpc_address"` 59 } 60 61 // ID returns the node's peer ID. 62 func (info NodeInfo) ID() NodeID { 63 return info.NodeID 64 } 65 66 // Validate checks the self-reported NodeInfo is safe. 67 // It returns an error if there 68 // are too many Channels, if there are any duplicate Channels, 69 // if the ListenAddr is malformed, or if the ListenAddr is a host name 70 // that can not be resolved to some IP. 71 // TODO: constraints for Moniker/Other? Or is that for the UI ? 72 // JAE: It needs to be done on the client, but to prevent ambiguous 73 // unicode characters, maybe it's worth sanitizing it here. 74 // In the future we might want to validate these, once we have a 75 // name-resolution system up. 76 // International clients could then use punycode (or we could use 77 // url-encoding), and we just need to be careful with how we handle that in our 78 // clients. (e.g. off by default). 79 func (info NodeInfo) Validate() error { 80 if _, _, err := ParseAddressString(info.ID().AddressString(info.ListenAddr)); err != nil { 81 return err 82 } 83 84 // Validate Version 85 if len(info.Version) > 0 { 86 if ver, err := tmstrings.ASCIITrim(info.Version); err != nil || ver == "" { 87 return fmt.Errorf("info.Version must be valid ASCII text without tabs, but got, %q [%s]", info.Version, ver) 88 } 89 } 90 91 // Validate Channels - ensure max and check for duplicates. 92 if len(info.Channels) > maxNumChannels { 93 return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels) 94 } 95 channels := make(map[byte]struct{}) 96 for _, ch := range info.Channels { 97 _, ok := channels[ch] 98 if ok { 99 return fmt.Errorf("info.Channels contains duplicate channel id %v", ch) 100 } 101 channels[ch] = struct{}{} 102 } 103 104 if m, err := tmstrings.ASCIITrim(info.Moniker); err != nil || m == "" { 105 return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v", info.Moniker) 106 } 107 108 // Validate Other. 109 other := info.Other 110 txIndex := other.TxIndex 111 switch txIndex { 112 case "", "on", "off": 113 default: 114 return fmt.Errorf("info.Other.TxIndex should be either 'on', 'off', or empty string, got '%v'", txIndex) 115 } 116 // XXX: Should we be more strict about address formats? 117 rpcAddr := other.RPCAddress 118 if len(rpcAddr) > 0 { 119 if a, err := tmstrings.ASCIITrim(rpcAddr); err != nil || a == "" { 120 return fmt.Errorf("info.Other.RPCAddress=%v must be valid ASCII text without tabs", rpcAddr) 121 } 122 } 123 124 return nil 125 } 126 127 // CompatibleWith checks if two NodeInfo are compatible with each other. 128 // CONTRACT: two nodes are compatible if the Block version and network match 129 // and they have at least one channel in common. 130 func (info NodeInfo) CompatibleWith(other NodeInfo) error { 131 if info.ProtocolVersion.Block != other.ProtocolVersion.Block { 132 return fmt.Errorf("peer is on a different Block version. Got %v, expected %v", 133 other.ProtocolVersion.Block, info.ProtocolVersion.Block) 134 } 135 136 // nodes must be on the same network 137 if info.Network != other.Network { 138 return fmt.Errorf("peer is on a different network. Got %v, expected %v", other.Network, info.Network) 139 } 140 141 // if we have no channels, we're just testing 142 if len(info.Channels) == 0 { 143 return nil 144 } 145 146 // for each of our channels, check if they have it 147 found := false 148 OUTER_LOOP: 149 for _, ch1 := range info.Channels { 150 for _, ch2 := range other.Channels { 151 if ch1 == ch2 { 152 found = true 153 break OUTER_LOOP // only need one 154 } 155 } 156 } 157 if !found { 158 return fmt.Errorf("peer has no common channels. Our channels: %v ; Peer channels: %v", info.Channels, other.Channels) 159 } 160 return nil 161 } 162 163 // AddChannel is used by the router when a channel is opened to add it to the node info 164 func (info *NodeInfo) AddChannel(channel uint16) { 165 // check that the channel doesn't already exist 166 for _, ch := range info.Channels { 167 if ch == byte(channel) { 168 return 169 } 170 } 171 172 info.Channels = append(info.Channels, byte(channel)) 173 } 174 175 func (info NodeInfo) Copy() NodeInfo { 176 return NodeInfo{ 177 ProtocolVersion: info.ProtocolVersion, 178 NodeID: info.NodeID, 179 ListenAddr: info.ListenAddr, 180 Network: info.Network, 181 Version: info.Version, 182 Channels: info.Channels, 183 Moniker: info.Moniker, 184 Other: info.Other, 185 } 186 } 187 188 func (info NodeInfo) ToProto() *tmp2p.NodeInfo { 189 190 dni := new(tmp2p.NodeInfo) 191 dni.ProtocolVersion = tmp2p.ProtocolVersion{ 192 P2P: info.ProtocolVersion.P2P, 193 Block: info.ProtocolVersion.Block, 194 App: info.ProtocolVersion.App, 195 } 196 197 dni.NodeID = string(info.NodeID) 198 dni.ListenAddr = info.ListenAddr 199 dni.Network = info.Network 200 dni.Version = info.Version 201 dni.Channels = info.Channels 202 dni.Moniker = info.Moniker 203 dni.Other = tmp2p.NodeInfoOther{ 204 TxIndex: info.Other.TxIndex, 205 RPCAddress: info.Other.RPCAddress, 206 } 207 208 return dni 209 } 210 211 func NodeInfoFromProto(pb *tmp2p.NodeInfo) (NodeInfo, error) { 212 if pb == nil { 213 return NodeInfo{}, errors.New("nil node info") 214 } 215 dni := NodeInfo{ 216 ProtocolVersion: ProtocolVersion{ 217 P2P: pb.ProtocolVersion.P2P, 218 Block: pb.ProtocolVersion.Block, 219 App: pb.ProtocolVersion.App, 220 }, 221 NodeID: NodeID(pb.NodeID), 222 ListenAddr: pb.ListenAddr, 223 Network: pb.Network, 224 Version: pb.Version, 225 Channels: pb.Channels, 226 Moniker: pb.Moniker, 227 Other: NodeInfoOther{ 228 TxIndex: pb.Other.TxIndex, 229 RPCAddress: pb.Other.RPCAddress, 230 }, 231 } 232 233 return dni, nil 234 } 235 236 // ParseAddressString reads an address string, and returns the IP 237 // address and port information, returning an error for any validation 238 // errors. 239 func ParseAddressString(addr string) (net.IP, uint16, error) { 240 addrWithoutProtocol := removeProtocolIfDefined(addr) 241 spl := strings.Split(addrWithoutProtocol, "@") 242 if len(spl) != 2 { 243 return nil, 0, errors.New("invalid address") 244 } 245 246 id, err := NewNodeID(spl[0]) 247 if err != nil { 248 return nil, 0, err 249 } 250 251 if err := id.Validate(); err != nil { 252 return nil, 0, err 253 } 254 255 addrWithoutProtocol = spl[1] 256 257 // get host and port 258 host, portStr, err := net.SplitHostPort(addrWithoutProtocol) 259 if err != nil { 260 return nil, 0, err 261 } 262 if len(host) == 0 { 263 return nil, 0, err 264 } 265 266 ip := net.ParseIP(host) 267 if ip == nil { 268 ips, err := net.LookupIP(host) 269 if err != nil { 270 return nil, 0, err 271 } 272 ip = ips[0] 273 } 274 275 port, err := strconv.ParseUint(portStr, 10, 16) 276 if err != nil { 277 return nil, 0, err 278 } 279 280 return ip, uint16(port), nil 281 } 282 283 func removeProtocolIfDefined(addr string) string { 284 if strings.Contains(addr, "://") { 285 return strings.Split(addr, "://")[1] 286 } 287 return addr 288 289 }