github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/node_info.go (about) 1 package p2p 2 3 import ( 4 "fmt" 5 6 "github.com/gnolang/gno/tm2/pkg/bft/state/eventstore" 7 "github.com/gnolang/gno/tm2/pkg/strings" 8 "github.com/gnolang/gno/tm2/pkg/versionset" 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 is the basic node information exchanged 24 // between two peers during the Tendermint P2P handshake. 25 type NodeInfo struct { 26 // Set of protocol versions 27 VersionSet versionset.VersionSet `json:"version_set"` 28 29 // Authenticate 30 NetAddress *NetAddress `json:"net_address"` 31 32 // Check compatibility. 33 // Channels are HexBytes so easier to read as JSON 34 Network string `json:"network"` // network/chain ID 35 Software string `json:"software"` // name of immediate software 36 Version string `json:"version"` // software major.minor.revision 37 Channels []byte `json:"channels"` // channels this node knows about 38 39 // ASCIIText fields 40 Moniker string `json:"moniker"` // arbitrary moniker 41 Other NodeInfoOther `json:"other"` // other application specific data 42 } 43 44 // NodeInfoOther is the misc. application specific data 45 type NodeInfoOther struct { 46 TxIndex string `json:"tx_index"` 47 RPCAddress string `json:"rpc_address"` 48 } 49 50 // Validate checks the self-reported NodeInfo is safe. 51 // It returns an error if there 52 // are too many Channels, if there are any duplicate Channels, 53 // if the ListenAddr is malformed, or if the ListenAddr is a host name 54 // that can not be resolved to some IP. 55 // TODO: constraints for Moniker/Other? Or is that for the UI ? 56 // JAE: It needs to be done on the client, but to prevent ambiguous 57 // unicode characters, maybe it's worth sanitizing it here. 58 // In the future we might want to validate these, once we have a 59 // name-resolution system up. 60 // International clients could then use punycode (or we could use 61 // url-encoding), and we just need to be careful with how we handle that in our 62 // clients. (e.g. off by default). 63 func (info NodeInfo) Validate() error { 64 // ID is already validated. TODO validate 65 66 // Validate ListenAddr. 67 if info.NetAddress == nil { 68 return fmt.Errorf("info.NetAddress cannot be nil") 69 } 70 if err := info.NetAddress.ValidateLocal(); err != nil { 71 return err 72 } 73 74 // Network is validated in CompatibleWith. 75 76 // Validate Version 77 if len(info.Version) > 0 && 78 (!strings.IsASCIIText(info.Version) || strings.ASCIITrim(info.Version) == "") { 79 return fmt.Errorf("info.Version must be valid ASCII text without tabs, but got %v", info.Version) 80 } 81 82 // Validate Channels - ensure max and check for duplicates. 83 if len(info.Channels) > maxNumChannels { 84 return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels) 85 } 86 channels := make(map[byte]struct{}) 87 for _, ch := range info.Channels { 88 _, ok := channels[ch] 89 if ok { 90 return fmt.Errorf("info.Channels contains duplicate channel id %v", ch) 91 } 92 channels[ch] = struct{}{} 93 } 94 95 // Validate Moniker. 96 if !strings.IsASCIIText(info.Moniker) || strings.ASCIITrim(info.Moniker) == "" { 97 return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v", info.Moniker) 98 } 99 100 // Validate Other. 101 other := info.Other 102 txIndex := other.TxIndex 103 switch txIndex { 104 case "", eventstore.StatusOn, eventstore.StatusOff: 105 default: 106 return fmt.Errorf("info.Other.TxIndex should be either 'on', 'off', or empty string, got '%v'", txIndex) 107 } 108 // XXX: Should we be more strict about address formats? 109 rpcAddr := other.RPCAddress 110 if len(rpcAddr) > 0 && (!strings.IsASCIIText(rpcAddr) || strings.ASCIITrim(rpcAddr) == "") { 111 return fmt.Errorf("info.Other.RPCAddress=%v must be valid ASCII text without tabs", rpcAddr) 112 } 113 114 return nil 115 } 116 117 func (info NodeInfo) ID() ID { 118 return info.NetAddress.ID 119 } 120 121 // CompatibleWith checks if two NodeInfo are compatible with eachother. 122 // CONTRACT: two nodes are compatible if the Block version and network match 123 // and they have at least one channel in common. 124 func (info NodeInfo) CompatibleWith(other NodeInfo) error { 125 // check protocol versions 126 _, err := info.VersionSet.CompatibleWith(other.VersionSet) 127 if err != nil { 128 return err 129 } 130 131 // nodes must be on the same network 132 if info.Network != other.Network { 133 return fmt.Errorf("Peer is on a different network. Got %v, expected %v", other.Network, info.Network) 134 } 135 136 // if we have no channels, we're just testing 137 if len(info.Channels) == 0 { 138 return nil 139 } 140 141 // for each of our channels, check if they have it 142 found := false 143 OUTER_LOOP: 144 for _, ch1 := range info.Channels { 145 for _, ch2 := range other.Channels { 146 if ch1 == ch2 { 147 found = true 148 break OUTER_LOOP // only need one 149 } 150 } 151 } 152 if !found { 153 return fmt.Errorf("Peer has no common channels. Our channels: %v ; Peer channels: %v", info.Channels, other.Channels) 154 } 155 return nil 156 }