github.com/intfoundation/intchain@v0.0.0-20220727031208-4316ad31ca73/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 "context" 33 "fmt" 34 "reflect" 35 "sync" 36 37 "github.com/intfoundation/intchain/p2p" 38 ) 39 40 // error codes used by this protocol scheme 41 const ( 42 ErrMsgTooLong = iota 43 ErrDecode 44 ErrWrite 45 ErrInvalidMsgCode 46 ErrInvalidMsgType 47 ErrHandshake 48 ErrNoHandler 49 ErrHandler 50 ) 51 52 // error description strings associated with the codes 53 var errorToString = map[int]string{ 54 ErrMsgTooLong: "Message too long", 55 ErrDecode: "Invalid message (RLP error)", 56 ErrWrite: "Error sending message", 57 ErrInvalidMsgCode: "Invalid message code", 58 ErrInvalidMsgType: "Invalid message type", 59 ErrHandshake: "Handshake error", 60 ErrNoHandler: "No handler registered error", 61 ErrHandler: "Message handler error", 62 } 63 64 /* 65 Error implements the standard go error interface. 66 Use: 67 68 errorf(code, format, params ...interface{}) 69 70 Prints as: 71 72 <description>: <details> 73 74 where description is given by code in errorToString 75 and details is fmt.Sprintf(format, params...) 76 77 exported field Code can be checked 78 */ 79 type Error struct { 80 Code int 81 message string 82 format string 83 params []interface{} 84 } 85 86 func (e Error) Error() (message string) { 87 if len(e.message) == 0 { 88 name, ok := errorToString[e.Code] 89 if !ok { 90 panic("invalid message code") 91 } 92 e.message = name 93 if e.format != "" { 94 e.message += ": " + fmt.Sprintf(e.format, e.params...) 95 } 96 } 97 return e.message 98 } 99 100 func errorf(code int, format string, params ...interface{}) *Error { 101 return &Error{ 102 Code: code, 103 format: format, 104 params: params, 105 } 106 } 107 108 // Spec is a protocol specification including its name and version as well as 109 // the types of messages which are exchanged 110 type Spec struct { 111 // Name is the name of the protocol, often a three-letter word 112 Name string 113 114 // Version is the version number of the protocol 115 Version uint 116 117 // MaxMsgSize is the maximum accepted length of the message payload 118 MaxMsgSize uint32 119 120 // Messages is a list of message data types which this protocol uses, with 121 // each message type being sent with its array index as the code (so 122 // [&foo{}, &bar{}, &baz{}] would send foo, bar and baz with codes 123 // 0, 1 and 2 respectively) 124 // each message must have a single unique data type 125 Messages []interface{} 126 127 initOnce sync.Once 128 codes map[reflect.Type]uint64 129 types map[uint64]reflect.Type 130 } 131 132 func (s *Spec) init() { 133 s.initOnce.Do(func() { 134 s.codes = make(map[reflect.Type]uint64, len(s.Messages)) 135 s.types = make(map[uint64]reflect.Type, len(s.Messages)) 136 for i, msg := range s.Messages { 137 code := uint64(i) 138 typ := reflect.TypeOf(msg) 139 if typ.Kind() == reflect.Ptr { 140 typ = typ.Elem() 141 } 142 s.codes[typ] = code 143 s.types[code] = typ 144 } 145 }) 146 } 147 148 // Length returns the number of message types in the protocol 149 func (s *Spec) Length() uint64 { 150 return uint64(len(s.Messages)) 151 } 152 153 // GetCode returns the message code of a type, and boolean second argument is 154 // false if the message type is not found 155 func (s *Spec) GetCode(msg interface{}) (uint64, bool) { 156 s.init() 157 typ := reflect.TypeOf(msg) 158 if typ.Kind() == reflect.Ptr { 159 typ = typ.Elem() 160 } 161 code, ok := s.codes[typ] 162 return code, ok 163 } 164 165 // NewMsg construct a new message type given the code 166 func (s *Spec) NewMsg(code uint64) (interface{}, bool) { 167 s.init() 168 typ, ok := s.types[code] 169 if !ok { 170 return nil, false 171 } 172 return reflect.New(typ).Interface(), true 173 } 174 175 // Peer represents a remote peer or protocol instance that is running on a peer connection with 176 // a remote peer 177 type Peer struct { 178 *p2p.Peer // the p2p.Peer object representing the remote 179 rw p2p.MsgReadWriter // p2p.MsgReadWriter to send messages to and read messages from 180 spec *Spec 181 } 182 183 // NewPeer constructs a new peer 184 // this constructor is called by the p2p.Protocol#Run function 185 // the first two arguments are the arguments passed to p2p.Protocol.Run function 186 // the third argument is the Spec describing the protocol 187 func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer { 188 return &Peer{ 189 Peer: p, 190 rw: rw, 191 spec: spec, 192 } 193 } 194 195 // Run starts the forever loop that handles incoming messages 196 // called within the p2p.Protocol#Run function 197 // the handler argument is a function which is called for each message received 198 // from the remote peer, a returned error causes the loop to exit 199 // resulting in disconnection 200 func (p *Peer) Run(handler func(msg interface{}) error) error { 201 for { 202 if err := p.handleIncoming(handler); err != nil { 203 return err 204 } 205 } 206 } 207 208 // Drop disconnects a peer. 209 // TODO: may need to implement protocol drop only? don't want to kick off the peer 210 // if they are useful for other protocols 211 func (p *Peer) Drop(err error) { 212 p.Disconnect(p2p.DiscSubprotocolError) 213 } 214 215 // Send takes a message, encodes it in RLP, finds the right message code and sends the 216 // message off to the peer 217 // this low level call will be wrapped by libraries providing routed or broadcast sends 218 // but often just used to forward and push messages to directly connected peers 219 func (p *Peer) Send(msg interface{}) error { 220 code, found := p.spec.GetCode(msg) 221 if !found { 222 return errorf(ErrInvalidMsgType, "%v", code) 223 } 224 return p2p.Send(p.rw, code, msg) 225 } 226 227 // handleIncoming(code) 228 // is called each cycle of the main forever loop that dispatches incoming messages 229 // if this returns an error the loop returns and the peer is disconnected with the error 230 // this generic handler 231 // * checks message size, 232 // * checks for out-of-range message codes, 233 // * handles decoding with reflection, 234 // * call handlers as callbacks 235 func (p *Peer) handleIncoming(handle func(msg interface{}) error) error { 236 msg, err := p.rw.ReadMsg() 237 if err != nil { 238 return err 239 } 240 // make sure that the payload has been fully consumed 241 defer msg.Discard() 242 243 if msg.Size > p.spec.MaxMsgSize { 244 return errorf(ErrMsgTooLong, "%v > %v", msg.Size, p.spec.MaxMsgSize) 245 } 246 247 val, ok := p.spec.NewMsg(msg.Code) 248 if !ok { 249 return errorf(ErrInvalidMsgCode, "%v", msg.Code) 250 } 251 if err := msg.Decode(val); err != nil { 252 return errorf(ErrDecode, "<= %v: %v", msg, err) 253 } 254 255 // call the registered handler callbacks 256 // a registered callback take the decoded message as argument as an interface 257 // which the handler is supposed to cast to the appropriate type 258 // it is entirely safe not to check the cast in the handler since the handler is 259 // chosen based on the proper type in the first place 260 if err := handle(val); err != nil { 261 return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err) 262 } 263 return nil 264 } 265 266 // Handshake negotiates a handshake on the peer connection 267 // * arguments 268 // * context 269 // * the local handshake to be sent to the remote peer 270 // * funcion to be called on the remote handshake (can be nil) 271 // * expects a remote handshake back of the same type 272 // * the dialing peer needs to send the handshake first and then waits for remote 273 // * the listening peer waits for the remote handshake and then sends it 274 // returns the remote handshake and an error 275 func (p *Peer) Handshake(ctx context.Context, hs interface{}, verify func(interface{}) error) (rhs interface{}, err error) { 276 if _, ok := p.spec.GetCode(hs); !ok { 277 return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs) 278 } 279 errc := make(chan error, 2) 280 handle := func(msg interface{}) error { 281 rhs = msg 282 if verify != nil { 283 return verify(rhs) 284 } 285 return nil 286 } 287 send := func() { errc <- p.Send(hs) } 288 receive := func() { errc <- p.handleIncoming(handle) } 289 290 go func() { 291 if p.Inbound() { 292 receive() 293 send() 294 } else { 295 send() 296 receive() 297 } 298 }() 299 300 for i := 0; i < 2; i++ { 301 select { 302 case err = <-errc: 303 case <-ctx.Done(): 304 err = ctx.Err() 305 } 306 if err != nil { 307 return nil, errorf(ErrHandshake, err.Error()) 308 } 309 } 310 return rhs, nil 311 }