github.com/gobitfly/go-ethereum@v1.8.12/swarm/pss/protocol.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 !nopssprotocol
    18  
    19  package pss
    20  
    21  import (
    22  	"bytes"
    23  	"fmt"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/p2p"
    28  	"github.com/ethereum/go-ethereum/p2p/protocols"
    29  	"github.com/ethereum/go-ethereum/rlp"
    30  	"github.com/ethereum/go-ethereum/swarm/log"
    31  )
    32  
    33  const (
    34  	IsActiveProtocol = true
    35  )
    36  
    37  // Convenience wrapper for devp2p protocol messages for transport over pss
    38  type ProtocolMsg struct {
    39  	Code       uint64
    40  	Size       uint32
    41  	Payload    []byte
    42  	ReceivedAt time.Time
    43  }
    44  
    45  // Creates a ProtocolMsg
    46  func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error) {
    47  
    48  	rlpdata, err := rlp.EncodeToBytes(msg)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// TODO verify that nested structs cannot be used in rlp
    54  	smsg := &ProtocolMsg{
    55  		Code:    code,
    56  		Size:    uint32(len(rlpdata)),
    57  		Payload: rlpdata,
    58  	}
    59  
    60  	return rlp.EncodeToBytes(smsg)
    61  }
    62  
    63  // Protocol options to be passed to a new Protocol instance
    64  //
    65  // The parameters specify which encryption schemes to allow
    66  type ProtocolParams struct {
    67  	Asymmetric bool
    68  	Symmetric  bool
    69  }
    70  
    71  // PssReadWriter bridges pss send/receive with devp2p protocol send/receive
    72  //
    73  // Implements p2p.MsgReadWriter
    74  type PssReadWriter struct {
    75  	*Pss
    76  	LastActive time.Time
    77  	rw         chan p2p.Msg
    78  	spec       *protocols.Spec
    79  	topic      *Topic
    80  	sendFunc   func(string, Topic, []byte) error
    81  	key        string
    82  	closed     bool
    83  }
    84  
    85  // Implements p2p.MsgReader
    86  func (prw *PssReadWriter) ReadMsg() (p2p.Msg, error) {
    87  	msg := <-prw.rw
    88  	log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg))
    89  	return msg, nil
    90  }
    91  
    92  // Implements p2p.MsgWriter
    93  func (prw *PssReadWriter) WriteMsg(msg p2p.Msg) error {
    94  	log.Trace("pssrw writemsg", "msg", msg)
    95  	if prw.closed {
    96  		return fmt.Errorf("connection closed")
    97  	}
    98  	rlpdata := make([]byte, msg.Size)
    99  	msg.Payload.Read(rlpdata)
   100  	pmsg, err := rlp.EncodeToBytes(ProtocolMsg{
   101  		Code:    msg.Code,
   102  		Size:    msg.Size,
   103  		Payload: rlpdata,
   104  	})
   105  	if err != nil {
   106  		return err
   107  	}
   108  	return prw.sendFunc(prw.key, *prw.topic, pmsg)
   109  }
   110  
   111  // Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader
   112  func (prw *PssReadWriter) injectMsg(msg p2p.Msg) error {
   113  	log.Trace(fmt.Sprintf("pssrw injectmsg: %v", msg))
   114  	prw.rw <- msg
   115  	return nil
   116  }
   117  
   118  // Convenience object for emulation devp2p over pss
   119  type Protocol struct {
   120  	*Pss
   121  	proto        *p2p.Protocol
   122  	topic        *Topic
   123  	spec         *protocols.Spec
   124  	pubKeyRWPool map[string]p2p.MsgReadWriter
   125  	symKeyRWPool map[string]p2p.MsgReadWriter
   126  	Asymmetric   bool
   127  	Symmetric    bool
   128  	RWPoolMu     sync.Mutex
   129  }
   130  
   131  // Activates devp2p emulation over a specific pss topic
   132  //
   133  // One or both encryption schemes must be specified. If
   134  // only one is specified, the protocol will not be valid
   135  // for the other, and will make the message handler
   136  // return errors
   137  func RegisterProtocol(ps *Pss, topic *Topic, spec *protocols.Spec, targetprotocol *p2p.Protocol, options *ProtocolParams) (*Protocol, error) {
   138  	if !options.Asymmetric && !options.Symmetric {
   139  		return nil, fmt.Errorf("specify at least one of asymmetric or symmetric messaging mode")
   140  	}
   141  	pp := &Protocol{
   142  		Pss:          ps,
   143  		proto:        targetprotocol,
   144  		topic:        topic,
   145  		spec:         spec,
   146  		pubKeyRWPool: make(map[string]p2p.MsgReadWriter),
   147  		symKeyRWPool: make(map[string]p2p.MsgReadWriter),
   148  		Asymmetric:   options.Asymmetric,
   149  		Symmetric:    options.Symmetric,
   150  	}
   151  	return pp, nil
   152  }
   153  
   154  // Generic handler for incoming messages over devp2p emulation
   155  //
   156  // To be passed to pss.Register()
   157  //
   158  // Will run the protocol on a new incoming peer, provided that
   159  // the encryption key of the message has a match in the internal
   160  // pss keypool
   161  //
   162  // Fails if protocol is not valid for the message encryption scheme,
   163  // if adding a new peer fails, or if the message is not a serialized
   164  // p2p.Msg (which it always will be if it is sent from this object).
   165  func (p *Protocol) Handle(msg []byte, peer *p2p.Peer, asymmetric bool, keyid string) error {
   166  	var vrw *PssReadWriter
   167  	if p.Asymmetric != asymmetric && p.Symmetric == !asymmetric {
   168  		return fmt.Errorf("invalid protocol encryption")
   169  	} else if (!p.isActiveSymKey(keyid, *p.topic) && !asymmetric) ||
   170  		(!p.isActiveAsymKey(keyid, *p.topic) && asymmetric) {
   171  
   172  		rw, err := p.AddPeer(peer, *p.topic, asymmetric, keyid)
   173  		if err != nil {
   174  			return err
   175  		}
   176  		vrw = rw.(*PssReadWriter)
   177  	}
   178  
   179  	pmsg, err := ToP2pMsg(msg)
   180  	if err != nil {
   181  		return fmt.Errorf("could not decode pssmsg")
   182  	}
   183  	if asymmetric {
   184  		vrw = p.pubKeyRWPool[keyid].(*PssReadWriter)
   185  	} else {
   186  		vrw = p.symKeyRWPool[keyid].(*PssReadWriter)
   187  	}
   188  	vrw.injectMsg(pmsg)
   189  	return nil
   190  }
   191  
   192  // check if (peer) symmetric key is currently registered with this topic
   193  func (p *Protocol) isActiveSymKey(key string, topic Topic) bool {
   194  	return p.symKeyRWPool[key] != nil
   195  }
   196  
   197  // check if (peer) asymmetric key is currently registered with this topic
   198  func (p *Protocol) isActiveAsymKey(key string, topic Topic) bool {
   199  	return p.pubKeyRWPool[key] != nil
   200  }
   201  
   202  // Creates a serialized (non-buffered) version of a p2p.Msg, used in the specialized internal p2p.MsgReadwriter implementations
   203  func ToP2pMsg(msg []byte) (p2p.Msg, error) {
   204  	payload := &ProtocolMsg{}
   205  	if err := rlp.DecodeBytes(msg, payload); err != nil {
   206  		return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err)
   207  	}
   208  
   209  	return p2p.Msg{
   210  		Code:       payload.Code,
   211  		Size:       uint32(len(payload.Payload)),
   212  		ReceivedAt: time.Now(),
   213  		Payload:    bytes.NewBuffer(payload.Payload),
   214  	}, nil
   215  }
   216  
   217  // Runs an emulated pss Protocol on the specified peer,
   218  // linked to a specific topic
   219  // `key` and `asymmetric` specifies what encryption key
   220  // to link the peer to.
   221  // The key must exist in the pss store prior to adding the peer.
   222  func (p *Protocol) AddPeer(peer *p2p.Peer, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) {
   223  	rw := &PssReadWriter{
   224  		Pss:   p.Pss,
   225  		rw:    make(chan p2p.Msg),
   226  		spec:  p.spec,
   227  		topic: p.topic,
   228  		key:   key,
   229  	}
   230  	if asymmetric {
   231  		rw.sendFunc = p.Pss.SendAsym
   232  	} else {
   233  		rw.sendFunc = p.Pss.SendSym
   234  	}
   235  	if asymmetric {
   236  		p.Pss.pubKeyPoolMu.Lock()
   237  		if _, ok := p.Pss.pubKeyPool[key]; !ok {
   238  			return nil, fmt.Errorf("asym key does not exist: %s", key)
   239  		}
   240  		p.Pss.pubKeyPoolMu.Unlock()
   241  		p.RWPoolMu.Lock()
   242  		p.pubKeyRWPool[key] = rw
   243  		p.RWPoolMu.Unlock()
   244  	} else {
   245  		p.Pss.symKeyPoolMu.Lock()
   246  		if _, ok := p.Pss.symKeyPool[key]; !ok {
   247  			return nil, fmt.Errorf("symkey does not exist: %s", key)
   248  		}
   249  		p.Pss.symKeyPoolMu.Unlock()
   250  		p.RWPoolMu.Lock()
   251  		p.symKeyRWPool[key] = rw
   252  		p.RWPoolMu.Unlock()
   253  	}
   254  	go func() {
   255  		err := p.proto.Run(peer, rw)
   256  		log.Warn(fmt.Sprintf("pss vprotocol quit on %v topic %v: %v", peer, topic, err))
   257  	}()
   258  	return rw, nil
   259  }
   260  
   261  func (p *Protocol) RemovePeer(asymmetric bool, key string) {
   262  	log.Debug("closing pss peer", "asym", asymmetric, "key", key)
   263  	p.RWPoolMu.Lock()
   264  	defer p.RWPoolMu.Unlock()
   265  	if asymmetric {
   266  		rw := p.pubKeyRWPool[key].(*PssReadWriter)
   267  		rw.closed = true
   268  		delete(p.pubKeyRWPool, key)
   269  	} else {
   270  		rw := p.symKeyRWPool[key].(*PssReadWriter)
   271  		rw.closed = true
   272  		delete(p.symKeyRWPool, key)
   273  	}
   274  }
   275  
   276  // Uniform translation of protocol specifiers to topic
   277  func ProtocolTopic(spec *protocols.Spec) Topic {
   278  	return BytesToTopic([]byte(fmt.Sprintf("%s:%d", spec.Name, spec.Version)))
   279  }