github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/pss/client/client.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:44</date>
    10  //</624450116379480064>
    11  
    12  
    13  //+建设!Noclipse,!无协议
    14  
    15  package client
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common/hexutil"
    25  	"github.com/ethereum/go-ethereum/p2p"
    26  	"github.com/ethereum/go-ethereum/p2p/enode"
    27  	"github.com/ethereum/go-ethereum/p2p/protocols"
    28  	"github.com/ethereum/go-ethereum/rlp"
    29  	"github.com/ethereum/go-ethereum/rpc"
    30  	"github.com/ethereum/go-ethereum/swarm/log"
    31  	"github.com/ethereum/go-ethereum/swarm/pss"
    32  )
    33  
    34  const (
    35  	handshakeRetryTimeout = 1000
    36  	handshakeRetryCount   = 3
    37  )
    38  
    39  //PSS客户端通过PSS RPC API提供devp2p仿真,
    40  //允许从不同的进程访问PSS方法
    41  type Client struct {
    42  	BaseAddrHex string
    43  
    44  //同龄人
    45  	peerPool map[pss.Topic]map[string]*pssRPCRW
    46  	protos   map[pss.Topic]*p2p.Protocol
    47  
    48  //RPC连接
    49  	rpc  *rpc.Client
    50  	subs []*rpc.ClientSubscription
    51  
    52  //渠道
    53  	topicsC chan []byte
    54  	quitC   chan struct{}
    55  
    56  	poolMu sync.Mutex
    57  }
    58  
    59  //实现p2p.msgreadwriter
    60  type pssRPCRW struct {
    61  	*Client
    62  	topic    string
    63  	msgC     chan []byte
    64  	addr     pss.PssAddress
    65  	pubKeyId string
    66  	lastSeen time.Time
    67  	closed   bool
    68  }
    69  
    70  func (c *Client) newpssRPCRW(pubkeyid string, addr pss.PssAddress, topicobj pss.Topic) (*pssRPCRW, error) {
    71  	topic := topicobj.String()
    72  	err := c.rpc.Call(nil, "pss_setPeerPublicKey", pubkeyid, topic, hexutil.Encode(addr[:]))
    73  	if err != nil {
    74  		return nil, fmt.Errorf("setpeer %s %s: %v", topic, pubkeyid, err)
    75  	}
    76  	return &pssRPCRW{
    77  		Client:   c,
    78  		topic:    topic,
    79  		msgC:     make(chan []byte),
    80  		addr:     addr,
    81  		pubKeyId: pubkeyid,
    82  	}, nil
    83  }
    84  
    85  func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) {
    86  	msg := <-rw.msgC
    87  	log.Trace("pssrpcrw read", "msg", msg)
    88  	pmsg, err := pss.ToP2pMsg(msg)
    89  	if err != nil {
    90  		return p2p.Msg{}, err
    91  	}
    92  
    93  	return pmsg, nil
    94  }
    95  
    96  //如果只剩下一个信息槽
    97  //然后通过握手请求新的
    98  //如果缓冲区为空,握手请求将一直阻塞直到返回
    99  //在此之后,指针将更改为缓冲区中的第一个新键
   100  //如果:
   101  //-任何API调用失败
   102  //-握手重试在没有回复的情况下耗尽,
   103  //-发送失败
   104  func (rw *pssRPCRW) WriteMsg(msg p2p.Msg) error {
   105  	log.Trace("got writemsg pssclient", "msg", msg)
   106  	if rw.closed {
   107  		return fmt.Errorf("connection closed")
   108  	}
   109  	rlpdata := make([]byte, msg.Size)
   110  	msg.Payload.Read(rlpdata)
   111  	pmsg, err := rlp.EncodeToBytes(pss.ProtocolMsg{
   112  		Code:    msg.Code,
   113  		Size:    msg.Size,
   114  		Payload: rlpdata,
   115  	})
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  //拿到钥匙
   121  	var symkeyids []string
   122  	err = rw.Client.rpc.Call(&symkeyids, "pss_getHandshakeKeys", rw.pubKeyId, rw.topic, false, true)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  //检查第一把钥匙的容量
   128  	var symkeycap uint16
   129  	if len(symkeyids) > 0 {
   130  		err = rw.Client.rpc.Call(&symkeycap, "pss_getHandshakeKeyCapacity", symkeyids[0])
   131  		if err != nil {
   132  			return err
   133  		}
   134  	}
   135  
   136  	err = rw.Client.rpc.Call(nil, "pss_sendSym", symkeyids[0], rw.topic, hexutil.Encode(pmsg))
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  //如果这是最后一条有效的消息,则启动新的握手
   142  	if symkeycap == 1 {
   143  		var retries int
   144  		var sync bool
   145  //如果它是唯一剩余的密钥,请确保在有新的密钥可供进一步写入之前不会继续。
   146  		if len(symkeyids) == 1 {
   147  			sync = true
   148  		}
   149  //开始握手
   150  		_, err := rw.handshake(retries, sync, false)
   151  		if err != nil {
   152  			log.Warn("failing", "err", err)
   153  			return err
   154  		}
   155  	}
   156  	return nil
   157  }
   158  
   159  //握手API调用的重试和同步包装
   160  //成功执行后返回第一个新symkeyid
   161  func (rw *pssRPCRW) handshake(retries int, sync bool, flush bool) (string, error) {
   162  
   163  	var symkeyids []string
   164  	var i int
   165  //请求新密钥
   166  //如果密钥缓冲区已耗尽,则将其作为阻塞调用进行,并在放弃之前尝试几次。
   167  	for i = 0; i < 1+retries; i++ {
   168  		log.Debug("handshake attempt pssrpcrw", "pubkeyid", rw.pubKeyId, "topic", rw.topic, "sync", sync)
   169  		err := rw.Client.rpc.Call(&symkeyids, "pss_handshake", rw.pubKeyId, rw.topic, sync, flush)
   170  		if err == nil {
   171  			var keyid string
   172  			if sync {
   173  				keyid = symkeyids[0]
   174  			}
   175  			return keyid, nil
   176  		}
   177  		if i-1+retries > 1 {
   178  			time.Sleep(time.Millisecond * handshakeRetryTimeout)
   179  		}
   180  	}
   181  
   182  	return "", fmt.Errorf("handshake failed after %d attempts", i)
   183  }
   184  
   185  //自定义构造函数
   186  //
   187  //提供对RPC对象的直接访问
   188  func NewClient(rpcurl string) (*Client, error) {
   189  	rpcclient, err := rpc.Dial(rpcurl)
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	client, err := NewClientWithRPC(rpcclient)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	return client, nil
   199  }
   200  
   201  //主要施工单位
   202  //
   203  //“rpc client”参数允许传递内存中的RPC客户端充当远程WebSocket RPC。
   204  func NewClientWithRPC(rpcclient *rpc.Client) (*Client, error) {
   205  	client := newClient()
   206  	client.rpc = rpcclient
   207  	err := client.rpc.Call(&client.BaseAddrHex, "pss_baseAddr")
   208  	if err != nil {
   209  		return nil, fmt.Errorf("cannot get pss node baseaddress: %v", err)
   210  	}
   211  	return client, nil
   212  }
   213  
   214  func newClient() (client *Client) {
   215  	client = &Client{
   216  		quitC:    make(chan struct{}),
   217  		peerPool: make(map[pss.Topic]map[string]*pssRPCRW),
   218  		protos:   make(map[pss.Topic]*p2p.Protocol),
   219  	}
   220  	return
   221  }
   222  
   223  //在PSS连接上安装新的devp2p protcool
   224  //
   225  //协议别名为“PSS主题”
   226  //使用来自p2p/协议包的普通devp2p发送和传入消息处理程序例程
   227  //
   228  //当从客户端尚不知道的对等端接收到传入消息时,
   229  //这个对等对象被实例化,并且协议在它上面运行。
   230  func (c *Client) RunProtocol(ctx context.Context, proto *p2p.Protocol) error {
   231  	topicobj := pss.BytesToTopic([]byte(fmt.Sprintf("%s:%d", proto.Name, proto.Version)))
   232  	topichex := topicobj.String()
   233  	msgC := make(chan pss.APIMsg)
   234  	c.peerPool[topicobj] = make(map[string]*pssRPCRW)
   235  	sub, err := c.rpc.Subscribe(ctx, "pss", msgC, "receive", topichex, false, false)
   236  	if err != nil {
   237  		return fmt.Errorf("pss event subscription failed: %v", err)
   238  	}
   239  	c.subs = append(c.subs, sub)
   240  	err = c.rpc.Call(nil, "pss_addHandshake", topichex)
   241  	if err != nil {
   242  		return fmt.Errorf("pss handshake activation failed: %v", err)
   243  	}
   244  
   245  //发送传入消息
   246  	go func() {
   247  		for {
   248  			select {
   249  			case msg := <-msgC:
   250  //我们这里只允许sym msgs
   251  				if msg.Asymmetric {
   252  					continue
   253  				}
   254  //我们通过了symkeyid
   255  //需要symkey本身解析为对等机的pubkey
   256  				var pubkeyid string
   257  				err = c.rpc.Call(&pubkeyid, "pss_getHandshakePublicKey", msg.Key)
   258  				if err != nil || pubkeyid == "" {
   259  					log.Trace("proto err or no pubkey", "err", err, "symkeyid", msg.Key)
   260  					continue
   261  				}
   262  //如果我们还没有这个协议上的对等方,请创建它
   263  //这或多或少与addpsspeer相同,而不是握手启动
   264  				if c.peerPool[topicobj][pubkeyid] == nil {
   265  					var addrhex string
   266  					err := c.rpc.Call(&addrhex, "pss_getAddress", topichex, false, msg.Key)
   267  					if err != nil {
   268  						log.Trace(err.Error())
   269  						continue
   270  					}
   271  					addrbytes, err := hexutil.Decode(addrhex)
   272  					if err != nil {
   273  						log.Trace(err.Error())
   274  						break
   275  					}
   276  					addr := pss.PssAddress(addrbytes)
   277  					rw, err := c.newpssRPCRW(pubkeyid, addr, topicobj)
   278  					if err != nil {
   279  						break
   280  					}
   281  					c.peerPool[topicobj][pubkeyid] = rw
   282  					p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{})
   283  					go proto.Run(p, c.peerPool[topicobj][pubkeyid])
   284  				}
   285  				go func() {
   286  					c.peerPool[topicobj][pubkeyid].msgC <- msg.Msg
   287  				}()
   288  			case <-c.quitC:
   289  				return
   290  			}
   291  		}
   292  	}()
   293  
   294  	c.protos[topicobj] = proto
   295  	return nil
   296  }
   297  
   298  //始终调用此函数以确保我们干净地退出
   299  func (c *Client) Close() error {
   300  	for _, s := range c.subs {
   301  		s.Unsubscribe()
   302  	}
   303  	return nil
   304  }
   305  
   306  //添加PSS对等(公钥)并在其上运行协议
   307  //
   308  //具有匹配主题的client.runprotocol必须
   309  //在添加对等机之前运行,否则此方法将
   310  //返回一个错误。
   311  //
   312  //密钥必须存在于PSS节点的密钥存储中
   313  //在添加对等机之前。该方法将返回一个错误
   314  //如果不是。
   315  func (c *Client) AddPssPeer(pubkeyid string, addr []byte, spec *protocols.Spec) error {
   316  	topic := pss.ProtocolTopic(spec)
   317  	if c.peerPool[topic] == nil {
   318  		return errors.New("addpeer on unset topic")
   319  	}
   320  	if c.peerPool[topic][pubkeyid] == nil {
   321  		rw, err := c.newpssRPCRW(pubkeyid, addr, topic)
   322  		if err != nil {
   323  			return err
   324  		}
   325  		_, err = rw.handshake(handshakeRetryCount, true, true)
   326  		if err != nil {
   327  			return err
   328  		}
   329  		c.poolMu.Lock()
   330  		c.peerPool[topic][pubkeyid] = rw
   331  		c.poolMu.Unlock()
   332  		p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{})
   333  		go c.protos[topic].Run(p, c.peerPool[topic][pubkeyid])
   334  	}
   335  	return nil
   336  }
   337  
   338  //删除PSS对等
   339  //
   340  //TODO:底层清理
   341  func (c *Client) RemovePssPeer(pubkeyid string, spec *protocols.Spec) {
   342  	log.Debug("closing pss client peer", "pubkey", pubkeyid, "protoname", spec.Name, "protoversion", spec.Version)
   343  	c.poolMu.Lock()
   344  	defer c.poolMu.Unlock()
   345  	topic := pss.ProtocolTopic(spec)
   346  	c.peerPool[topic][pubkeyid].closed = true
   347  	delete(c.peerPool[topic], pubkeyid)
   348  }
   349