github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/protocols/protocol.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:41</date>
    10  //</624450105948246016>
    11  
    12  
    13  /*
    14  包协议是对p2p的扩展,它提供了一种用户友好的简单定义方法
    15  通过抽象协议标准共享的代码来开发子协议。
    16  
    17  *自动将代码索引分配给消息
    18  *基于反射自动RLP解码/编码
    19  *提供永久循环以读取传入消息
    20  *标准化与通信相关的错误处理
    21  *标准化握手谈判
    22  *TODO:自动生成对等机的有线协议规范
    23  
    24  **/
    25  
    26  package protocols
    27  
    28  import (
    29  	"bufio"
    30  	"bytes"
    31  	"context"
    32  	"fmt"
    33  	"io"
    34  	"reflect"
    35  	"sync"
    36  	"time"
    37  
    38  	"github.com/ethereum/go-ethereum/log"
    39  	"github.com/ethereum/go-ethereum/metrics"
    40  	"github.com/ethereum/go-ethereum/p2p"
    41  	"github.com/ethereum/go-ethereum/rlp"
    42  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    43  	"github.com/ethereum/go-ethereum/swarm/tracing"
    44  	opentracing "github.com/opentracing/opentracing-go"
    45  )
    46  
    47  //此协议方案使用的错误代码
    48  const (
    49  	ErrMsgTooLong = iota
    50  	ErrDecode
    51  	ErrWrite
    52  	ErrInvalidMsgCode
    53  	ErrInvalidMsgType
    54  	ErrHandshake
    55  	ErrNoHandler
    56  	ErrHandler
    57  )
    58  
    59  //与代码关联的错误描述字符串
    60  var errorToString = map[int]string{
    61  	ErrMsgTooLong:     "Message too long",
    62  	ErrDecode:         "Invalid message (RLP error)",
    63  	ErrWrite:          "Error sending message",
    64  	ErrInvalidMsgCode: "Invalid message code",
    65  	ErrInvalidMsgType: "Invalid message type",
    66  	ErrHandshake:      "Handshake error",
    67  	ErrNoHandler:      "No handler registered error",
    68  	ErrHandler:        "Message handler error",
    69  }
    70  
    71  /*
    72  错误实现标准Go错误接口。
    73  用途:
    74  
    75    错误F(代码、格式、参数…接口)
    76  
    77  打印为:
    78  
    79   <description>:<details>
    80  
    81  其中由ErrorToString中的代码给出描述
    82  详细信息是fmt.sprintf(格式,参数…)
    83  
    84  可以检查导出的字段代码
    85  **/
    86  
    87  type Error struct {
    88  	Code    int
    89  	message string
    90  	format  string
    91  	params  []interface{}
    92  }
    93  
    94  func (e Error) Error() (message string) {
    95  	if len(e.message) == 0 {
    96  		name, ok := errorToString[e.Code]
    97  		if !ok {
    98  			panic("invalid message code")
    99  		}
   100  		e.message = name
   101  		if e.format != "" {
   102  			e.message += ": " + fmt.Sprintf(e.format, e.params...)
   103  		}
   104  	}
   105  	return e.message
   106  }
   107  
   108  func errorf(code int, format string, params ...interface{}) *Error {
   109  	return &Error{
   110  		Code:   code,
   111  		format: format,
   112  		params: params,
   113  	}
   114  }
   115  
   116  //wrappedmsg用于在消息有效负载旁边传播已封送的上下文
   117  type WrappedMsg struct {
   118  	Context []byte
   119  	Size    uint32
   120  	Payload []byte
   121  }
   122  
   123  //对于会计,设计是允许规范描述消息的定价方式和内容。
   124  //为了访问这个功能,我们提供了一个钩子接口,它将调用会计方法
   125  //注意:将来可能会有更多这样的(水平)钩子
   126  type Hook interface {
   127  //发送信息的钩子
   128  	Send(peer *Peer, size uint32, msg interface{}) error
   129  //接收信息的钩子
   130  	Receive(peer *Peer, size uint32, msg interface{}) error
   131  }
   132  
   133  //规范是一种协议规范,包括其名称和版本以及
   134  //交换的消息类型
   135  type Spec struct {
   136  //名称是协议的名称,通常是三个字母的单词
   137  	Name string
   138  
   139  //version是协议的版本号
   140  	Version uint
   141  
   142  //maxmsgsize是消息有效负载的最大可接受长度
   143  	MaxMsgSize uint32
   144  
   145  //messages是此协议使用的消息数据类型的列表,
   146  //发送的每个消息类型及其数组索引作为代码(因此
   147  //[&foo,&bar,&baz]将发送带有代码的foo、bar和baz
   148  //分别为0、1和2)
   149  //每条消息必须有一个唯一的数据类型
   150  	Messages []interface{}
   151  
   152  //会计挂钩(将来可扩展到多个挂钩)
   153  	Hook Hook
   154  
   155  	initOnce sync.Once
   156  	codes    map[reflect.Type]uint64
   157  	types    map[uint64]reflect.Type
   158  }
   159  
   160  func (s *Spec) init() {
   161  	s.initOnce.Do(func() {
   162  		s.codes = make(map[reflect.Type]uint64, len(s.Messages))
   163  		s.types = make(map[uint64]reflect.Type, len(s.Messages))
   164  		for i, msg := range s.Messages {
   165  			code := uint64(i)
   166  			typ := reflect.TypeOf(msg)
   167  			if typ.Kind() == reflect.Ptr {
   168  				typ = typ.Elem()
   169  			}
   170  			s.codes[typ] = code
   171  			s.types[code] = typ
   172  		}
   173  	})
   174  }
   175  
   176  //length返回协议中的消息类型数
   177  func (s *Spec) Length() uint64 {
   178  	return uint64(len(s.Messages))
   179  }
   180  
   181  //getcode返回一个类型的消息代码,Boolean第二个参数是
   182  //如果未找到消息类型,则为false
   183  func (s *Spec) GetCode(msg interface{}) (uint64, bool) {
   184  	s.init()
   185  	typ := reflect.TypeOf(msg)
   186  	if typ.Kind() == reflect.Ptr {
   187  		typ = typ.Elem()
   188  	}
   189  	code, ok := s.codes[typ]
   190  	return code, ok
   191  }
   192  
   193  //newmsg构造给定代码的新消息类型
   194  func (s *Spec) NewMsg(code uint64) (interface{}, bool) {
   195  	s.init()
   196  	typ, ok := s.types[code]
   197  	if !ok {
   198  		return nil, false
   199  	}
   200  	return reflect.New(typ).Interface(), true
   201  }
   202  
   203  //对等机表示在与的对等连接上运行的远程对等机或协议实例
   204  //远程对等体
   205  type Peer struct {
   206  *p2p.Peer                   //代表远程的p2p.peer对象
   207  rw        p2p.MsgReadWriter //p2p.msgreadwriter,用于向发送消息和从中读取消息
   208  	spec      *Spec
   209  }
   210  
   211  //new peer构造新的peer
   212  //此构造函数由p2p.protocol run函数调用
   213  //前两个参数是传递给p2p.protocol.run函数的参数
   214  //第三个参数是描述协议的规范
   215  func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer {
   216  	return &Peer{
   217  		Peer: p,
   218  		rw:   rw,
   219  		spec: spec,
   220  	}
   221  }
   222  
   223  //运行启动处理传入消息的Forever循环
   224  //在p2p.protocol run函数中调用
   225  //handler参数是为接收到的每个消息调用的函数。
   226  //从远程对等机返回的错误导致循环退出
   227  //导致断开
   228  func (p *Peer) Run(handler func(ctx context.Context, msg interface{}) error) error {
   229  	for {
   230  		if err := p.handleIncoming(handler); err != nil {
   231  			if err != io.EOF {
   232  				metrics.GetOrRegisterCounter("peer.handleincoming.error", nil).Inc(1)
   233  				log.Error("peer.handleIncoming", "err", err)
   234  			}
   235  
   236  			return err
   237  		}
   238  	}
   239  }
   240  
   241  //DROP断开对等机的连接。
   242  //TODO:可能只需要实现协议删除?不想把同伴踢开
   243  //如果它们对其他协议有用
   244  func (p *Peer) Drop(err error) {
   245  	p.Disconnect(p2p.DiscSubprotocolError)
   246  }
   247  
   248  //send接收一条消息,将其编码为rlp,找到正确的消息代码并发送
   249  //向对等端发送消息
   250  //这个低级调用将由提供路由或广播发送的库包装。
   251  //但通常只用于转发和将消息推送到直接连接的对等端
   252  func (p *Peer) Send(ctx context.Context, msg interface{}) error {
   253  	defer metrics.GetOrRegisterResettingTimer("peer.send_t", nil).UpdateSince(time.Now())
   254  	metrics.GetOrRegisterCounter("peer.send", nil).Inc(1)
   255  
   256  	var b bytes.Buffer
   257  	if tracing.Enabled {
   258  		writer := bufio.NewWriter(&b)
   259  
   260  		tracer := opentracing.GlobalTracer()
   261  
   262  		sctx := spancontext.FromContext(ctx)
   263  
   264  		if sctx != nil {
   265  			err := tracer.Inject(
   266  				sctx,
   267  				opentracing.Binary,
   268  				writer)
   269  			if err != nil {
   270  				return err
   271  			}
   272  		}
   273  
   274  		writer.Flush()
   275  	}
   276  
   277  	r, err := rlp.EncodeToBytes(msg)
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	wmsg := WrappedMsg{
   283  		Context: b.Bytes(),
   284  		Size:    uint32(len(r)),
   285  		Payload: r,
   286  	}
   287  
   288  //如果设置了会计挂钩,请调用它
   289  	if p.spec.Hook != nil {
   290  		err := p.spec.Hook.Send(p, wmsg.Size, msg)
   291  		if err != nil {
   292  			p.Drop(err)
   293  			return err
   294  		}
   295  	}
   296  
   297  	code, found := p.spec.GetCode(msg)
   298  	if !found {
   299  		return errorf(ErrInvalidMsgType, "%v", code)
   300  	}
   301  	return p2p.Send(p.rw, code, wmsg)
   302  }
   303  
   304  //手工编码(代码)
   305  //在发送传入消息的主永久循环的每个循环中调用
   306  //如果返回错误,则循环将返回,并且对等端将与错误断开连接。
   307  //此通用处理程序
   308  //*检查邮件大小,
   309  //*检查超出范围的消息代码,
   310  //*处理带反射的解码,
   311  //*作为回调的调用处理程序
   312  func (p *Peer) handleIncoming(handle func(ctx context.Context, msg interface{}) error) error {
   313  	msg, err := p.rw.ReadMsg()
   314  	if err != nil {
   315  		return err
   316  	}
   317  //确保有效载荷已被完全消耗。
   318  	defer msg.Discard()
   319  
   320  	if msg.Size > p.spec.MaxMsgSize {
   321  		return errorf(ErrMsgTooLong, "%v > %v", msg.Size, p.spec.MaxMsgSize)
   322  	}
   323  
   324  //取消标记包装的邮件,其中可能包含上下文
   325  	var wmsg WrappedMsg
   326  	err = msg.Decode(&wmsg)
   327  	if err != nil {
   328  		log.Error(err.Error())
   329  		return err
   330  	}
   331  
   332  	ctx := context.Background()
   333  
   334  //如果启用了跟踪,并且请求中的上下文是
   335  //不是空的,试着解开它
   336  	if tracing.Enabled && len(wmsg.Context) > 0 {
   337  		var sctx opentracing.SpanContext
   338  
   339  		tracer := opentracing.GlobalTracer()
   340  		sctx, err = tracer.Extract(
   341  			opentracing.Binary,
   342  			bytes.NewReader(wmsg.Context))
   343  		if err != nil {
   344  			log.Error(err.Error())
   345  			return err
   346  		}
   347  
   348  		ctx = spancontext.WithContext(ctx, sctx)
   349  	}
   350  
   351  	val, ok := p.spec.NewMsg(msg.Code)
   352  	if !ok {
   353  		return errorf(ErrInvalidMsgCode, "%v", msg.Code)
   354  	}
   355  	if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil {
   356  		return errorf(ErrDecode, "<= %v: %v", msg, err)
   357  	}
   358  
   359  //如果设置了会计挂钩,请调用它
   360  	if p.spec.Hook != nil {
   361  		err := p.spec.Hook.Receive(p, wmsg.Size, val)
   362  		if err != nil {
   363  			return err
   364  		}
   365  	}
   366  
   367  //调用已注册的处理程序回调
   368  //注册的回调将解码后的消息作为参数作为接口
   369  //应该将处理程序强制转换为适当的类型
   370  //不检查处理程序中的强制转换是完全安全的,因为处理程序是
   371  //首先根据正确的类型选择
   372  	if err := handle(ctx, val); err != nil {
   373  		return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err)
   374  	}
   375  	return nil
   376  }
   377  
   378  //握手在对等连接上协商握手
   379  //*参数
   380  //*上下文
   381  //*要发送到远程对等机的本地握手
   382  //*在远程握手时要调用的函数(可以为零)
   383  //*需要相同类型的远程握手
   384  //*拨号对等端需要先发送握手,然后等待远程
   385  //*侦听对等机等待远程握手,然后发送它
   386  //返回远程握手和错误
   387  func (p *Peer) Handshake(ctx context.Context, hs interface{}, verify func(interface{}) error) (rhs interface{}, err error) {
   388  	if _, ok := p.spec.GetCode(hs); !ok {
   389  		return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs)
   390  	}
   391  	errc := make(chan error, 2)
   392  	handle := func(ctx context.Context, msg interface{}) error {
   393  		rhs = msg
   394  		if verify != nil {
   395  			return verify(rhs)
   396  		}
   397  		return nil
   398  	}
   399  	send := func() { errc <- p.Send(ctx, hs) }
   400  	receive := func() { errc <- p.handleIncoming(handle) }
   401  
   402  	go func() {
   403  		if p.Inbound() {
   404  			receive()
   405  			send()
   406  		} else {
   407  			send()
   408  			receive()
   409  		}
   410  	}()
   411  
   412  	for i := 0; i < 2; i++ {
   413  		select {
   414  		case err = <-errc:
   415  		case <-ctx.Done():
   416  			err = ctx.Err()
   417  		}
   418  		if err != nil {
   419  			return nil, errorf(ErrHandshake, err.Error())
   420  		}
   421  	}
   422  	return rhs, nil
   423  }
   424