github.com/dotlike13/wemix30_go@v1.8.23/swarm/pss/client/client.go (about)

     1  // Copyright 2018 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  // +build !noclient,!noprotocol
    18  
    19  package client
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"fmt"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/p2p"
    30  	"github.com/ethereum/go-ethereum/p2p/enode"
    31  	"github.com/ethereum/go-ethereum/p2p/protocols"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  	"github.com/ethereum/go-ethereum/rpc"
    34  	"github.com/ethereum/go-ethereum/swarm/log"
    35  	"github.com/ethereum/go-ethereum/swarm/pss"
    36  )
    37  
    38  const (
    39  	handshakeRetryTimeout = 1000
    40  	handshakeRetryCount   = 3
    41  )
    42  
    43  // The pss client provides devp2p emulation over pss RPC API,
    44  // giving access to pss methods from a different process
    45  type Client struct {
    46  	BaseAddrHex string
    47  
    48  	// peers
    49  	peerPool map[pss.Topic]map[string]*pssRPCRW
    50  	protos   map[pss.Topic]*p2p.Protocol
    51  
    52  	// rpc connections
    53  	rpc  *rpc.Client
    54  	subs []*rpc.ClientSubscription
    55  
    56  	// channels
    57  	topicsC chan []byte
    58  	quitC   chan struct{}
    59  
    60  	poolMu sync.Mutex
    61  }
    62  
    63  // implements p2p.MsgReadWriter
    64  type pssRPCRW struct {
    65  	*Client
    66  	topic    string
    67  	msgC     chan []byte
    68  	addr     pss.PssAddress
    69  	pubKeyId string
    70  	lastSeen time.Time
    71  	closed   bool
    72  }
    73  
    74  func (c *Client) newpssRPCRW(pubkeyid string, addr pss.PssAddress, topicobj pss.Topic) (*pssRPCRW, error) {
    75  	topic := topicobj.String()
    76  	err := c.rpc.Call(nil, "pss_setPeerPublicKey", pubkeyid, topic, hexutil.Encode(addr[:]))
    77  	if err != nil {
    78  		return nil, fmt.Errorf("setpeer %s %s: %v", topic, pubkeyid, err)
    79  	}
    80  	return &pssRPCRW{
    81  		Client:   c,
    82  		topic:    topic,
    83  		msgC:     make(chan []byte),
    84  		addr:     addr,
    85  		pubKeyId: pubkeyid,
    86  	}, nil
    87  }
    88  
    89  func (rw *pssRPCRW) ReadMsg() (p2p.Msg, error) {
    90  	msg := <-rw.msgC
    91  	log.Trace("pssrpcrw read", "msg", msg)
    92  	pmsg, err := pss.ToP2pMsg(msg)
    93  	if err != nil {
    94  		return p2p.Msg{}, err
    95  	}
    96  
    97  	return pmsg, nil
    98  }
    99  
   100  // If only one message slot left
   101  // then new is requested through handshake
   102  // if buffer is empty, handshake request blocks until return
   103  // after which pointer is changed to first new key in buffer
   104  // will fail if:
   105  // - any api calls fail
   106  // - handshake retries are exhausted without reply,
   107  // - send fails
   108  func (rw *pssRPCRW) WriteMsg(msg p2p.Msg) error {
   109  	log.Trace("got writemsg pssclient", "msg", msg)
   110  	if rw.closed {
   111  		return fmt.Errorf("connection closed")
   112  	}
   113  	rlpdata := make([]byte, msg.Size)
   114  	msg.Payload.Read(rlpdata)
   115  	pmsg, err := rlp.EncodeToBytes(pss.ProtocolMsg{
   116  		Code:    msg.Code,
   117  		Size:    msg.Size,
   118  		Payload: rlpdata,
   119  	})
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	// Get the keys we have
   125  	var symkeyids []string
   126  	err = rw.Client.rpc.Call(&symkeyids, "pss_getHandshakeKeys", rw.pubKeyId, rw.topic, false, true)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	// Check the capacity of the first key
   132  	var symkeycap uint16
   133  	if len(symkeyids) > 0 {
   134  		err = rw.Client.rpc.Call(&symkeycap, "pss_getHandshakeKeyCapacity", symkeyids[0])
   135  		if err != nil {
   136  			return err
   137  		}
   138  	}
   139  
   140  	err = rw.Client.rpc.Call(nil, "pss_sendSym", symkeyids[0], rw.topic, hexutil.Encode(pmsg))
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	// If this is the last message it is valid for, initiate new handshake
   146  	if symkeycap == 1 {
   147  		var retries int
   148  		var sync bool
   149  		// if it's the only remaining key, make sure we don't continue until we have new ones for further writes
   150  		if len(symkeyids) == 1 {
   151  			sync = true
   152  		}
   153  		// initiate handshake
   154  		_, err := rw.handshake(retries, sync, false)
   155  		if err != nil {
   156  			log.Warn("failing", "err", err)
   157  			return err
   158  		}
   159  	}
   160  	return nil
   161  }
   162  
   163  // retry and synchronicity wrapper for handshake api call
   164  // returns first new symkeyid upon successful execution
   165  func (rw *pssRPCRW) handshake(retries int, sync bool, flush bool) (string, error) {
   166  
   167  	var symkeyids []string
   168  	var i int
   169  	// request new keys
   170  	// if the key buffer was depleted, make this as a blocking call and try several times before giving up
   171  	for i = 0; i < 1+retries; i++ {
   172  		log.Debug("handshake attempt pssrpcrw", "pubkeyid", rw.pubKeyId, "topic", rw.topic, "sync", sync)
   173  		err := rw.Client.rpc.Call(&symkeyids, "pss_handshake", rw.pubKeyId, rw.topic, sync, flush)
   174  		if err == nil {
   175  			var keyid string
   176  			if sync {
   177  				keyid = symkeyids[0]
   178  			}
   179  			return keyid, nil
   180  		}
   181  		if i-1+retries > 1 {
   182  			time.Sleep(time.Millisecond * handshakeRetryTimeout)
   183  		}
   184  	}
   185  
   186  	return "", fmt.Errorf("handshake failed after %d attempts", i)
   187  }
   188  
   189  // Custom constructor
   190  //
   191  // Provides direct access to the rpc object
   192  func NewClient(rpcurl string) (*Client, error) {
   193  	rpcclient, err := rpc.Dial(rpcurl)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	client, err := NewClientWithRPC(rpcclient)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	return client, nil
   203  }
   204  
   205  // Main constructor
   206  //
   207  // The 'rpcclient' parameter allows passing a in-memory rpc client to act as the remote websocket RPC.
   208  func NewClientWithRPC(rpcclient *rpc.Client) (*Client, error) {
   209  	client := newClient()
   210  	client.rpc = rpcclient
   211  	err := client.rpc.Call(&client.BaseAddrHex, "pss_baseAddr")
   212  	if err != nil {
   213  		return nil, fmt.Errorf("cannot get pss node baseaddress: %v", err)
   214  	}
   215  	return client, nil
   216  }
   217  
   218  func newClient() (client *Client) {
   219  	client = &Client{
   220  		quitC:    make(chan struct{}),
   221  		peerPool: make(map[pss.Topic]map[string]*pssRPCRW),
   222  		protos:   make(map[pss.Topic]*p2p.Protocol),
   223  	}
   224  	return
   225  }
   226  
   227  // Mounts a new devp2p protcool on the pss connection
   228  //
   229  // the protocol is aliased as a "pss topic"
   230  // uses normal devp2p send and incoming message handler routines from the p2p/protocols package
   231  //
   232  // when an incoming message is received from a peer that is not yet known to the client,
   233  // this peer object is instantiated, and the protocol is run on it.
   234  func (c *Client) RunProtocol(ctx context.Context, proto *p2p.Protocol) error {
   235  	topicobj := pss.BytesToTopic([]byte(fmt.Sprintf("%s:%d", proto.Name, proto.Version)))
   236  	topichex := topicobj.String()
   237  	msgC := make(chan pss.APIMsg)
   238  	c.peerPool[topicobj] = make(map[string]*pssRPCRW)
   239  	sub, err := c.rpc.Subscribe(ctx, "pss", msgC, "receive", topichex, false, false)
   240  	if err != nil {
   241  		return fmt.Errorf("pss event subscription failed: %v", err)
   242  	}
   243  	c.subs = append(c.subs, sub)
   244  	err = c.rpc.Call(nil, "pss_addHandshake", topichex)
   245  	if err != nil {
   246  		return fmt.Errorf("pss handshake activation failed: %v", err)
   247  	}
   248  
   249  	// dispatch incoming messages
   250  	go func() {
   251  		for {
   252  			select {
   253  			case msg := <-msgC:
   254  				// we only allow sym msgs here
   255  				if msg.Asymmetric {
   256  					continue
   257  				}
   258  				// we get passed the symkeyid
   259  				// need the symkey itself to resolve to peer's pubkey
   260  				var pubkeyid string
   261  				err = c.rpc.Call(&pubkeyid, "pss_getHandshakePublicKey", msg.Key)
   262  				if err != nil || pubkeyid == "" {
   263  					log.Trace("proto err or no pubkey", "err", err, "symkeyid", msg.Key)
   264  					continue
   265  				}
   266  				// if we don't have the peer on this protocol already, create it
   267  				// this is more or less the same as AddPssPeer, less the handshake initiation
   268  				if c.peerPool[topicobj][pubkeyid] == nil {
   269  					var addrhex string
   270  					err := c.rpc.Call(&addrhex, "pss_getAddress", topichex, false, msg.Key)
   271  					if err != nil {
   272  						log.Trace(err.Error())
   273  						continue
   274  					}
   275  					addrbytes, err := hexutil.Decode(addrhex)
   276  					if err != nil {
   277  						log.Trace(err.Error())
   278  						break
   279  					}
   280  					addr := pss.PssAddress(addrbytes)
   281  					rw, err := c.newpssRPCRW(pubkeyid, addr, topicobj)
   282  					if err != nil {
   283  						break
   284  					}
   285  					c.peerPool[topicobj][pubkeyid] = rw
   286  					p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{})
   287  					go proto.Run(p, c.peerPool[topicobj][pubkeyid])
   288  				}
   289  				go func() {
   290  					c.peerPool[topicobj][pubkeyid].msgC <- msg.Msg
   291  				}()
   292  			case <-c.quitC:
   293  				return
   294  			}
   295  		}
   296  	}()
   297  
   298  	c.protos[topicobj] = proto
   299  	return nil
   300  }
   301  
   302  // Always call this to ensure that we exit cleanly
   303  func (c *Client) Close() error {
   304  	for _, s := range c.subs {
   305  		s.Unsubscribe()
   306  	}
   307  	return nil
   308  }
   309  
   310  // Add a pss peer (public key) and run the protocol on it
   311  //
   312  // client.RunProtocol with matching topic must have been
   313  // run prior to adding the peer, or this method will
   314  // return an error.
   315  //
   316  // The key must exist in the key store of the pss node
   317  // before the peer is added. The method will return an error
   318  // if it is not.
   319  func (c *Client) AddPssPeer(pubkeyid string, addr []byte, spec *protocols.Spec) error {
   320  	topic := pss.ProtocolTopic(spec)
   321  	if c.peerPool[topic] == nil {
   322  		return errors.New("addpeer on unset topic")
   323  	}
   324  	if c.peerPool[topic][pubkeyid] == nil {
   325  		rw, err := c.newpssRPCRW(pubkeyid, addr, topic)
   326  		if err != nil {
   327  			return err
   328  		}
   329  		_, err = rw.handshake(handshakeRetryCount, true, true)
   330  		if err != nil {
   331  			return err
   332  		}
   333  		c.poolMu.Lock()
   334  		c.peerPool[topic][pubkeyid] = rw
   335  		c.poolMu.Unlock()
   336  		p := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%v", addr), []p2p.Cap{})
   337  		go c.protos[topic].Run(p, c.peerPool[topic][pubkeyid])
   338  	}
   339  	return nil
   340  }
   341  
   342  // Remove a pss peer
   343  //
   344  // TODO: underlying cleanup
   345  func (c *Client) RemovePssPeer(pubkeyid string, spec *protocols.Spec) {
   346  	log.Debug("closing pss client peer", "pubkey", pubkeyid, "protoname", spec.Name, "protoversion", spec.Version)
   347  	c.poolMu.Lock()
   348  	defer c.poolMu.Unlock()
   349  	topic := pss.ProtocolTopic(spec)
   350  	c.peerPool[topic][pubkeyid].closed = true
   351  	delete(c.peerPool[topic], pubkeyid)
   352  }