github.com/codingfuture/orig-energi3@v0.8.4/p2p/protocols/protocol.go (about)

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