github.com/lukso-network/go-ethereum@v1.8.22/p2p/protocols/protocol.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 /* 18 Package protocols is an extension to p2p. It offers a user friendly simple way to define 19 devp2p subprotocols by abstracting away code standardly shared by protocols. 20 21 * automate assigments of code indexes to messages 22 * automate RLP decoding/encoding based on reflecting 23 * provide the forever loop to read incoming messages 24 * standardise error handling related to communication 25 * standardised handshake negotiation 26 * TODO: automatic generation of wire protocol specification for peers 27 28 */ 29 package protocols 30 31 import ( 32 "bufio" 33 "bytes" 34 "context" 35 "fmt" 36 "io" 37 "reflect" 38 "sync" 39 "time" 40 41 "github.com/ethereum/go-ethereum/log" 42 "github.com/ethereum/go-ethereum/metrics" 43 "github.com/ethereum/go-ethereum/p2p" 44 "github.com/ethereum/go-ethereum/rlp" 45 "github.com/ethereum/go-ethereum/swarm/spancontext" 46 "github.com/ethereum/go-ethereum/swarm/tracing" 47 opentracing "github.com/opentracing/opentracing-go" 48 ) 49 50 // error codes used by this protocol scheme 51 const ( 52 ErrMsgTooLong = iota 53 ErrDecode 54 ErrWrite 55 ErrInvalidMsgCode 56 ErrInvalidMsgType 57 ErrHandshake 58 ErrNoHandler 59 ErrHandler 60 ) 61 62 // error description strings associated with the codes 63 var errorToString = map[int]string{ 64 ErrMsgTooLong: "Message too long", 65 ErrDecode: "Invalid message (RLP error)", 66 ErrWrite: "Error sending message", 67 ErrInvalidMsgCode: "Invalid message code", 68 ErrInvalidMsgType: "Invalid message type", 69 ErrHandshake: "Handshake error", 70 ErrNoHandler: "No handler registered error", 71 ErrHandler: "Message handler error", 72 } 73 74 /* 75 Error implements the standard go error interface. 76 Use: 77 78 errorf(code, format, params ...interface{}) 79 80 Prints as: 81 82 <description>: <details> 83 84 where description is given by code in errorToString 85 and details is fmt.Sprintf(format, params...) 86 87 exported field Code can be checked 88 */ 89 type Error struct { 90 Code int 91 message string 92 format string 93 params []interface{} 94 } 95 96 func (e Error) Error() (message string) { 97 if len(e.message) == 0 { 98 name, ok := errorToString[e.Code] 99 if !ok { 100 panic("invalid message code") 101 } 102 e.message = name 103 if e.format != "" { 104 e.message += ": " + fmt.Sprintf(e.format, e.params...) 105 } 106 } 107 return e.message 108 } 109 110 func errorf(code int, format string, params ...interface{}) *Error { 111 return &Error{ 112 Code: code, 113 format: format, 114 params: params, 115 } 116 } 117 118 // WrappedMsg is used to propagate marshalled context alongside message payloads 119 type WrappedMsg struct { 120 Context []byte 121 Size uint32 122 Payload []byte 123 } 124 125 //For accounting, the design is to allow the Spec to describe which and how its messages are priced 126 //To access this functionality, we provide a Hook interface which will call accounting methods 127 //NOTE: there could be more such (horizontal) hooks in the future 128 type Hook interface { 129 //A hook for sending messages 130 Send(peer *Peer, size uint32, msg interface{}) error 131 //A hook for receiving messages 132 Receive(peer *Peer, size uint32, msg interface{}) error 133 } 134 135 // Spec is a protocol specification including its name and version as well as 136 // the types of messages which are exchanged 137 type Spec struct { 138 // Name is the name of the protocol, often a three-letter word 139 Name string 140 141 // Version is the version number of the protocol 142 Version uint 143 144 // MaxMsgSize is the maximum accepted length of the message payload 145 MaxMsgSize uint32 146 147 // Messages is a list of message data types which this protocol uses, with 148 // each message type being sent with its array index as the code (so 149 // [&foo{}, &bar{}, &baz{}] would send foo, bar and baz with codes 150 // 0, 1 and 2 respectively) 151 // each message must have a single unique data type 152 Messages []interface{} 153 154 //hook for accounting (could be extended to multiple hooks in the future) 155 Hook Hook 156 157 initOnce sync.Once 158 codes map[reflect.Type]uint64 159 types map[uint64]reflect.Type 160 } 161 162 func (s *Spec) init() { 163 s.initOnce.Do(func() { 164 s.codes = make(map[reflect.Type]uint64, len(s.Messages)) 165 s.types = make(map[uint64]reflect.Type, len(s.Messages)) 166 for i, msg := range s.Messages { 167 code := uint64(i) 168 typ := reflect.TypeOf(msg) 169 if typ.Kind() == reflect.Ptr { 170 typ = typ.Elem() 171 } 172 s.codes[typ] = code 173 s.types[code] = typ 174 } 175 }) 176 } 177 178 // Length returns the number of message types in the protocol 179 func (s *Spec) Length() uint64 { 180 return uint64(len(s.Messages)) 181 } 182 183 // GetCode returns the message code of a type, and boolean second argument is 184 // false if the message type is not found 185 func (s *Spec) GetCode(msg interface{}) (uint64, bool) { 186 s.init() 187 typ := reflect.TypeOf(msg) 188 if typ.Kind() == reflect.Ptr { 189 typ = typ.Elem() 190 } 191 code, ok := s.codes[typ] 192 return code, ok 193 } 194 195 // NewMsg construct a new message type given the code 196 func (s *Spec) NewMsg(code uint64) (interface{}, bool) { 197 s.init() 198 typ, ok := s.types[code] 199 if !ok { 200 return nil, false 201 } 202 return reflect.New(typ).Interface(), true 203 } 204 205 // Peer represents a remote peer or protocol instance that is running on a peer connection with 206 // a remote peer 207 type Peer struct { 208 *p2p.Peer // the p2p.Peer object representing the remote 209 rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from 210 spec *Spec 211 } 212 213 // NewPeer constructs a new peer 214 // this constructor is called by the p2p.Protocol#Run function 215 // the first two arguments are the arguments passed to p2p.Protocol.Run function 216 // the third argument is the Spec describing the protocol 217 func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer { 218 return &Peer{ 219 Peer: p, 220 rw: rw, 221 spec: spec, 222 } 223 } 224 225 // Run starts the forever loop that handles incoming messages 226 // called within the p2p.Protocol#Run function 227 // the handler argument is a function which is called for each message received 228 // from the remote peer, a returned error causes the loop to exit 229 // resulting in disconnection 230 func (p *Peer) Run(handler func(ctx context.Context, msg interface{}) error) error { 231 for { 232 if err := p.handleIncoming(handler); err != nil { 233 if err != io.EOF { 234 metrics.GetOrRegisterCounter("peer.handleincoming.error", nil).Inc(1) 235 log.Error("peer.handleIncoming", "err", err) 236 } 237 238 return err 239 } 240 } 241 } 242 243 // Drop disconnects a peer. 244 // TODO: may need to implement protocol drop only? don't want to kick off the peer 245 // if they are useful for other protocols 246 func (p *Peer) Drop(err error) { 247 p.Disconnect(p2p.DiscSubprotocolError) 248 } 249 250 // Send takes a message, encodes it in RLP, finds the right message code and sends the 251 // message off to the peer 252 // this low level call will be wrapped by libraries providing routed or broadcast sends 253 // but often just used to forward and push messages to directly connected peers 254 func (p *Peer) Send(ctx context.Context, msg interface{}) error { 255 defer metrics.GetOrRegisterResettingTimer("peer.send_t", nil).UpdateSince(time.Now()) 256 metrics.GetOrRegisterCounter("peer.send", nil).Inc(1) 257 258 var b bytes.Buffer 259 if tracing.Enabled { 260 writer := bufio.NewWriter(&b) 261 262 tracer := opentracing.GlobalTracer() 263 264 sctx := spancontext.FromContext(ctx) 265 266 if sctx != nil { 267 err := tracer.Inject( 268 sctx, 269 opentracing.Binary, 270 writer) 271 if err != nil { 272 return err 273 } 274 } 275 276 writer.Flush() 277 } 278 279 r, err := rlp.EncodeToBytes(msg) 280 if err != nil { 281 return err 282 } 283 284 wmsg := WrappedMsg{ 285 Context: b.Bytes(), 286 Size: uint32(len(r)), 287 Payload: r, 288 } 289 290 //if the accounting hook is set, call it 291 if p.spec.Hook != nil { 292 err := p.spec.Hook.Send(p, wmsg.Size, msg) 293 if err != nil { 294 p.Drop(err) 295 return err 296 } 297 } 298 299 code, found := p.spec.GetCode(msg) 300 if !found { 301 return errorf(ErrInvalidMsgType, "%v", code) 302 } 303 return p2p.Send(p.rw, code, wmsg) 304 } 305 306 // handleIncoming(code) 307 // is called each cycle of the main forever loop that dispatches incoming messages 308 // if this returns an error the loop returns and the peer is disconnected with the error 309 // this generic handler 310 // * checks message size, 311 // * checks for out-of-range message codes, 312 // * handles decoding with reflection, 313 // * call handlers as callbacks 314 func (p *Peer) handleIncoming(handle func(ctx context.Context, msg interface{}) error) error { 315 msg, err := p.rw.ReadMsg() 316 if err != nil { 317 return err 318 } 319 // make sure that the payload has been fully consumed 320 defer msg.Discard() 321 322 if msg.Size > p.spec.MaxMsgSize { 323 return errorf(ErrMsgTooLong, "%v > %v", msg.Size, p.spec.MaxMsgSize) 324 } 325 326 // unmarshal wrapped msg, which might contain context 327 var wmsg WrappedMsg 328 err = msg.Decode(&wmsg) 329 if err != nil { 330 log.Error(err.Error()) 331 return err 332 } 333 334 ctx := context.Background() 335 336 // if tracing is enabled and the context coming within the request is 337 // not empty, try to unmarshal it 338 if tracing.Enabled && len(wmsg.Context) > 0 { 339 var sctx opentracing.SpanContext 340 341 tracer := opentracing.GlobalTracer() 342 sctx, err = tracer.Extract( 343 opentracing.Binary, 344 bytes.NewReader(wmsg.Context)) 345 if err != nil { 346 log.Error(err.Error()) 347 return err 348 } 349 350 ctx = spancontext.WithContext(ctx, sctx) 351 } 352 353 val, ok := p.spec.NewMsg(msg.Code) 354 if !ok { 355 return errorf(ErrInvalidMsgCode, "%v", msg.Code) 356 } 357 if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil { 358 return errorf(ErrDecode, "<= %v: %v", msg, err) 359 } 360 361 //if the accounting hook is set, call it 362 if p.spec.Hook != nil { 363 err := p.spec.Hook.Receive(p, wmsg.Size, val) 364 if err != nil { 365 return err 366 } 367 } 368 369 // call the registered handler callbacks 370 // a registered callback take the decoded message as argument as an interface 371 // which the handler is supposed to cast to the appropriate type 372 // it is entirely safe not to check the cast in the handler since the handler is 373 // chosen based on the proper type in the first place 374 if err := handle(ctx, val); err != nil { 375 return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err) 376 } 377 return nil 378 } 379 380 // Handshake negotiates a handshake on the peer connection 381 // * arguments 382 // * context 383 // * the local handshake to be sent to the remote peer 384 // * function to be called on the remote handshake (can be nil) 385 // * expects a remote handshake back of the same type 386 // * the dialing peer needs to send the handshake first and then waits for remote 387 // * the listening peer waits for the remote handshake and then sends it 388 // returns the remote handshake and an error 389 func (p *Peer) Handshake(ctx context.Context, hs interface{}, verify func(interface{}) error) (rhs interface{}, err error) { 390 if _, ok := p.spec.GetCode(hs); !ok { 391 return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs) 392 } 393 errc := make(chan error, 2) 394 handle := func(ctx context.Context, msg interface{}) error { 395 rhs = msg 396 if verify != nil { 397 return verify(rhs) 398 } 399 return nil 400 } 401 send := func() { errc <- p.Send(ctx, hs) } 402 receive := func() { errc <- p.handleIncoming(handle) } 403 404 go func() { 405 if p.Inbound() { 406 receive() 407 send() 408 } else { 409 send() 410 receive() 411 } 412 }() 413 414 for i := 0; i < 2; i++ { 415 select { 416 case err = <-errc: 417 case <-ctx.Done(): 418 err = ctx.Err() 419 } 420 if err != nil { 421 return nil, errorf(ErrHandshake, err.Error()) 422 } 423 } 424 return rhs, nil 425 }