github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/peer.go (about) 1 package p2p 2 3 import ( 4 "fmt" 5 "log/slog" 6 "net" 7 8 "github.com/gnolang/gno/tm2/pkg/cmap" 9 connm "github.com/gnolang/gno/tm2/pkg/p2p/conn" 10 "github.com/gnolang/gno/tm2/pkg/service" 11 ) 12 13 // Peer is an interface representing a peer connected on a reactor. 14 type Peer interface { 15 service.Service 16 FlushStop() 17 18 ID() ID // peer's cryptographic ID 19 RemoteIP() net.IP // remote IP of the connection 20 RemoteAddr() net.Addr // remote address of the connection 21 22 IsOutbound() bool // did we dial the peer 23 IsPersistent() bool // do we redial this peer when we disconnect 24 25 CloseConn() error // close original connection 26 27 NodeInfo() NodeInfo // peer's info 28 Status() connm.ConnectionStatus 29 SocketAddr() *NetAddress // actual address of the socket 30 31 Send(byte, []byte) bool 32 TrySend(byte, []byte) bool 33 34 Set(string, interface{}) 35 Get(string) interface{} 36 } 37 38 // ---------------------------------------------------------- 39 40 // peerConn contains the raw connection and its config. 41 type peerConn struct { 42 outbound bool 43 persistent bool 44 conn net.Conn // source connection 45 46 socketAddr *NetAddress 47 48 // cached RemoteIP() 49 ip net.IP 50 } 51 52 func newPeerConn( 53 outbound, persistent bool, 54 conn net.Conn, 55 socketAddr *NetAddress, 56 ) peerConn { 57 return peerConn{ 58 outbound: outbound, 59 persistent: persistent, 60 conn: conn, 61 socketAddr: socketAddr, 62 } 63 } 64 65 // ID only exists for SecretConnection. 66 // NOTE: Will panic if conn is not *SecretConnection. 67 func (pc peerConn) ID() ID { 68 return (pc.conn.(*connm.SecretConnection).RemotePubKey()).Address().ID() 69 } 70 71 // Return the IP from the connection RemoteAddr 72 func (pc peerConn) RemoteIP() net.IP { 73 if pc.ip != nil { 74 return pc.ip 75 } 76 77 host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String()) 78 if err != nil { 79 panic(err) 80 } 81 82 ips, err := net.LookupIP(host) 83 if err != nil { 84 panic(err) 85 } 86 87 pc.ip = ips[0] 88 89 return pc.ip 90 } 91 92 // peer implements Peer. 93 // 94 // Before using a peer, you will need to perform a handshake on connection. 95 type peer struct { 96 service.BaseService 97 98 // raw peerConn and the multiplex connection 99 peerConn 100 mconn *connm.MConnection 101 102 // peer's node info and the channel it knows about 103 // channels = nodeInfo.Channels 104 // cached to avoid copying nodeInfo in hasChannel 105 nodeInfo NodeInfo 106 channels []byte 107 108 // User data 109 Data *cmap.CMap 110 } 111 112 type PeerOption func(*peer) 113 114 func newPeer( 115 pc peerConn, 116 mConfig connm.MConnConfig, 117 nodeInfo NodeInfo, 118 reactorsByCh map[byte]Reactor, 119 chDescs []*connm.ChannelDescriptor, 120 onPeerError func(Peer, interface{}), 121 options ...PeerOption, 122 ) *peer { 123 p := &peer{ 124 peerConn: pc, 125 nodeInfo: nodeInfo, 126 channels: nodeInfo.Channels, // TODO 127 Data: cmap.NewCMap(), 128 } 129 130 p.mconn = createMConnection( 131 pc.conn, 132 p, 133 reactorsByCh, 134 chDescs, 135 onPeerError, 136 mConfig, 137 ) 138 p.BaseService = *service.NewBaseService(nil, "Peer", p) 139 for _, option := range options { 140 option(p) 141 } 142 143 return p 144 } 145 146 // String representation. 147 func (p *peer) String() string { 148 if p.outbound { 149 return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID()) 150 } 151 152 return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID()) 153 } 154 155 // --------------------------------------------------- 156 // Implements service.Service 157 158 // SetLogger implements BaseService. 159 func (p *peer) SetLogger(l *slog.Logger) { 160 p.Logger = l 161 p.mconn.SetLogger(l) 162 } 163 164 // OnStart implements BaseService. 165 func (p *peer) OnStart() error { 166 if err := p.BaseService.OnStart(); err != nil { 167 return err 168 } 169 170 if err := p.mconn.Start(); err != nil { 171 return err 172 } 173 174 return nil 175 } 176 177 // FlushStop mimics OnStop but additionally ensures that all successful 178 // .Send() calls will get flushed before closing the connection. 179 // NOTE: it is not safe to call this method more than once. 180 func (p *peer) FlushStop() { 181 p.BaseService.OnStop() 182 p.mconn.FlushStop() // stop everything and close the conn 183 } 184 185 // OnStop implements BaseService. 186 func (p *peer) OnStop() { 187 p.BaseService.OnStop() 188 p.mconn.Stop() // stop everything and close the conn 189 } 190 191 // --------------------------------------------------- 192 // Implements Peer 193 194 // ID returns the peer's ID - the hex encoded hash of its pubkey. 195 func (p *peer) ID() ID { 196 return p.nodeInfo.NetAddress.ID 197 } 198 199 // IsOutbound returns true if the connection is outbound, false otherwise. 200 func (p *peer) IsOutbound() bool { 201 return p.peerConn.outbound 202 } 203 204 // IsPersistent returns true if the peer is persistent, false otherwise. 205 func (p *peer) IsPersistent() bool { 206 return p.peerConn.persistent 207 } 208 209 // NodeInfo returns a copy of the peer's NodeInfo. 210 func (p *peer) NodeInfo() NodeInfo { 211 return p.nodeInfo 212 } 213 214 // SocketAddr returns the address of the socket. 215 // For outbound peers, it's the address dialed (after DNS resolution). 216 // For inbound peers, it's the address returned by the underlying connection 217 // (not what's reported in the peer's NodeInfo). 218 func (p *peer) SocketAddr() *NetAddress { 219 return p.peerConn.socketAddr 220 } 221 222 // Status returns the peer's ConnectionStatus. 223 func (p *peer) Status() connm.ConnectionStatus { 224 return p.mconn.Status() 225 } 226 227 // Send msg bytes to the channel identified by chID byte. Returns false if the 228 // send queue is full after timeout, specified by MConnection. 229 func (p *peer) Send(chID byte, msgBytes []byte) bool { 230 if !p.IsRunning() { 231 // see Switch#Broadcast, where we fetch the list of peers and loop over 232 // them - while we're looping, one peer may be removed and stopped. 233 return false 234 } else if !p.hasChannel(chID) { 235 return false 236 } 237 res := p.mconn.Send(chID, msgBytes) 238 return res 239 } 240 241 // TrySend msg bytes to the channel identified by chID byte. Immediately returns 242 // false if the send queue is full. 243 func (p *peer) TrySend(chID byte, msgBytes []byte) bool { 244 if !p.IsRunning() { 245 return false 246 } else if !p.hasChannel(chID) { 247 return false 248 } 249 res := p.mconn.TrySend(chID, msgBytes) 250 return res 251 } 252 253 // Get the data for a given key. 254 func (p *peer) Get(key string) interface{} { 255 return p.Data.Get(key) 256 } 257 258 // Set sets the data for the given key. 259 func (p *peer) Set(key string, data interface{}) { 260 p.Data.Set(key, data) 261 } 262 263 // hasChannel returns true if the peer reported 264 // knowing about the given chID. 265 func (p *peer) hasChannel(chID byte) bool { 266 for _, ch := range p.channels { 267 if ch == chID { 268 return true 269 } 270 } 271 // NOTE: probably will want to remove this 272 // but could be helpful while the feature is new 273 p.Logger.Debug( 274 "Unknown channel for peer", 275 "channel", 276 chID, 277 "channels", 278 p.channels, 279 ) 280 return false 281 } 282 283 // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all. 284 func (p *peer) CloseConn() error { 285 return p.peerConn.conn.Close() 286 } 287 288 // --------------------------------------------------- 289 // methods only used for testing 290 // TODO: can we remove these? 291 292 // CloseConn closes the underlying connection 293 func (pc *peerConn) CloseConn() { 294 pc.conn.Close() //nolint: errcheck 295 } 296 297 // RemoteAddr returns peer's remote network address. 298 func (p *peer) RemoteAddr() net.Addr { 299 return p.peerConn.conn.RemoteAddr() 300 } 301 302 // CanSend returns true if the send queue is not full, false otherwise. 303 func (p *peer) CanSend(chID byte) bool { 304 if !p.IsRunning() { 305 return false 306 } 307 return p.mconn.CanSend(chID) 308 } 309 310 // ------------------------------------------------------------------ 311 // helper funcs 312 313 func createMConnection( 314 conn net.Conn, 315 p *peer, 316 reactorsByCh map[byte]Reactor, 317 chDescs []*connm.ChannelDescriptor, 318 onPeerError func(Peer, interface{}), 319 config connm.MConnConfig, 320 ) *connm.MConnection { 321 onReceive := func(chID byte, msgBytes []byte) { 322 reactor := reactorsByCh[chID] 323 if reactor == nil { 324 // Note that its ok to panic here as it's caught in the connm._recover, 325 // which does onPeerError. 326 panic(fmt.Sprintf("Unknown channel %X", chID)) 327 } 328 reactor.Receive(chID, p, msgBytes) 329 } 330 331 onError := func(r interface{}) { 332 onPeerError(p, r) 333 } 334 335 return connm.NewMConnectionWithConfig( 336 conn, 337 chDescs, 338 onReceive, 339 onError, 340 config, 341 ) 342 }