| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // Copyright 2018 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // +build !nopssprotocol | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package pss | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/p2p" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/p2p/protocols" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/rlp" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/swarm/log" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	IsActiveProtocol = true | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Convenience wrapper for devp2p protocol messages for transport over pss | 
					
						
							|  |  |  | type ProtocolMsg struct { | 
					
						
							|  |  |  | 	Code       uint64 | 
					
						
							|  |  |  | 	Size       uint32 | 
					
						
							|  |  |  | 	Payload    []byte | 
					
						
							|  |  |  | 	ReceivedAt time.Time | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Creates a ProtocolMsg | 
					
						
							|  |  |  | func NewProtocolMsg(code uint64, msg interface{}) ([]byte, error) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rlpdata, err := rlp.EncodeToBytes(msg) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO verify that nested structs cannot be used in rlp | 
					
						
							|  |  |  | 	smsg := &ProtocolMsg{ | 
					
						
							|  |  |  | 		Code:    code, | 
					
						
							|  |  |  | 		Size:    uint32(len(rlpdata)), | 
					
						
							|  |  |  | 		Payload: rlpdata, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return rlp.EncodeToBytes(smsg) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Protocol options to be passed to a new Protocol instance | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The parameters specify which encryption schemes to allow | 
					
						
							|  |  |  | type ProtocolParams struct { | 
					
						
							|  |  |  | 	Asymmetric bool | 
					
						
							|  |  |  | 	Symmetric  bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PssReadWriter bridges pss send/receive with devp2p protocol send/receive | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Implements p2p.MsgReadWriter | 
					
						
							|  |  |  | type PssReadWriter struct { | 
					
						
							|  |  |  | 	*Pss | 
					
						
							|  |  |  | 	LastActive time.Time | 
					
						
							|  |  |  | 	rw         chan p2p.Msg | 
					
						
							|  |  |  | 	spec       *protocols.Spec | 
					
						
							|  |  |  | 	topic      *Topic | 
					
						
							|  |  |  | 	sendFunc   func(string, Topic, []byte) error | 
					
						
							|  |  |  | 	key        string | 
					
						
							|  |  |  | 	closed     bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Implements p2p.MsgReader | 
					
						
							|  |  |  | func (prw *PssReadWriter) ReadMsg() (p2p.Msg, error) { | 
					
						
							|  |  |  | 	msg := <-prw.rw | 
					
						
							|  |  |  | 	log.Trace(fmt.Sprintf("pssrw readmsg: %v", msg)) | 
					
						
							|  |  |  | 	return msg, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Implements p2p.MsgWriter | 
					
						
							|  |  |  | func (prw *PssReadWriter) WriteMsg(msg p2p.Msg) error { | 
					
						
							|  |  |  | 	log.Trace("pssrw writemsg", "msg", msg) | 
					
						
							|  |  |  | 	if prw.closed { | 
					
						
							|  |  |  | 		return fmt.Errorf("connection closed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rlpdata := make([]byte, msg.Size) | 
					
						
							|  |  |  | 	msg.Payload.Read(rlpdata) | 
					
						
							|  |  |  | 	pmsg, err := rlp.EncodeToBytes(ProtocolMsg{ | 
					
						
							|  |  |  | 		Code:    msg.Code, | 
					
						
							|  |  |  | 		Size:    msg.Size, | 
					
						
							|  |  |  | 		Payload: rlpdata, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return prw.sendFunc(prw.key, *prw.topic, pmsg) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Injects a p2p.Msg into the MsgReadWriter, so that it appears on the associated p2p.MsgReader | 
					
						
							|  |  |  | func (prw *PssReadWriter) injectMsg(msg p2p.Msg) error { | 
					
						
							|  |  |  | 	log.Trace(fmt.Sprintf("pssrw injectmsg: %v", msg)) | 
					
						
							|  |  |  | 	prw.rw <- msg | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Convenience object for emulation devp2p over pss | 
					
						
							|  |  |  | type Protocol struct { | 
					
						
							|  |  |  | 	*Pss | 
					
						
							|  |  |  | 	proto        *p2p.Protocol | 
					
						
							|  |  |  | 	topic        *Topic | 
					
						
							|  |  |  | 	spec         *protocols.Spec | 
					
						
							|  |  |  | 	pubKeyRWPool map[string]p2p.MsgReadWriter | 
					
						
							|  |  |  | 	symKeyRWPool map[string]p2p.MsgReadWriter | 
					
						
							|  |  |  | 	Asymmetric   bool | 
					
						
							|  |  |  | 	Symmetric    bool | 
					
						
							|  |  |  | 	RWPoolMu     sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Activates devp2p emulation over a specific pss topic | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // One or both encryption schemes must be specified. If | 
					
						
							|  |  |  | // only one is specified, the protocol will not be valid | 
					
						
							|  |  |  | // for the other, and will make the message handler | 
					
						
							|  |  |  | // return errors | 
					
						
							|  |  |  | func RegisterProtocol(ps *Pss, topic *Topic, spec *protocols.Spec, targetprotocol *p2p.Protocol, options *ProtocolParams) (*Protocol, error) { | 
					
						
							|  |  |  | 	if !options.Asymmetric && !options.Symmetric { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("specify at least one of asymmetric or symmetric messaging mode") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pp := &Protocol{ | 
					
						
							|  |  |  | 		Pss:          ps, | 
					
						
							|  |  |  | 		proto:        targetprotocol, | 
					
						
							|  |  |  | 		topic:        topic, | 
					
						
							|  |  |  | 		spec:         spec, | 
					
						
							|  |  |  | 		pubKeyRWPool: make(map[string]p2p.MsgReadWriter), | 
					
						
							|  |  |  | 		symKeyRWPool: make(map[string]p2p.MsgReadWriter), | 
					
						
							|  |  |  | 		Asymmetric:   options.Asymmetric, | 
					
						
							|  |  |  | 		Symmetric:    options.Symmetric, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return pp, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Generic handler for incoming messages over devp2p emulation | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // To be passed to pss.Register() | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Will run the protocol on a new incoming peer, provided that | 
					
						
							|  |  |  | // the encryption key of the message has a match in the internal | 
					
						
							|  |  |  | // pss keypool | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Fails if protocol is not valid for the message encryption scheme, | 
					
						
							|  |  |  | // if adding a new peer fails, or if the message is not a serialized | 
					
						
							|  |  |  | // p2p.Msg (which it always will be if it is sent from this object). | 
					
						
							|  |  |  | func (p *Protocol) Handle(msg []byte, peer *p2p.Peer, asymmetric bool, keyid string) error { | 
					
						
							|  |  |  | 	var vrw *PssReadWriter | 
					
						
							|  |  |  | 	if p.Asymmetric != asymmetric && p.Symmetric == !asymmetric { | 
					
						
							|  |  |  | 		return fmt.Errorf("invalid protocol encryption") | 
					
						
							|  |  |  | 	} else if (!p.isActiveSymKey(keyid, *p.topic) && !asymmetric) || | 
					
						
							|  |  |  | 		(!p.isActiveAsymKey(keyid, *p.topic) && asymmetric) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rw, err := p.AddPeer(peer, *p.topic, asymmetric, keyid) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		} else if rw == nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("handle called on nil MsgReadWriter for new key " + keyid) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		vrw = rw.(*PssReadWriter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pmsg, err := ToP2pMsg(msg) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("could not decode pssmsg") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if asymmetric { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		if p.pubKeyRWPool[keyid] == nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("handle called on nil MsgReadWriter for key " + keyid) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		vrw = p.pubKeyRWPool[keyid].(*PssReadWriter) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		if p.symKeyRWPool[keyid] == nil { | 
					
						
							|  |  |  | 			return fmt.Errorf("handle called on nil MsgReadWriter for key " + keyid) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		vrw = p.symKeyRWPool[keyid].(*PssReadWriter) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vrw.injectMsg(pmsg) | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // check if (peer) symmetric key is currently registered with this topic | 
					
						
							|  |  |  | func (p *Protocol) isActiveSymKey(key string, topic Topic) bool { | 
					
						
							|  |  |  | 	return p.symKeyRWPool[key] != nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // check if (peer) asymmetric key is currently registered with this topic | 
					
						
							|  |  |  | func (p *Protocol) isActiveAsymKey(key string, topic Topic) bool { | 
					
						
							|  |  |  | 	return p.pubKeyRWPool[key] != nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Creates a serialized (non-buffered) version of a p2p.Msg, used in the specialized internal p2p.MsgReadwriter implementations | 
					
						
							|  |  |  | func ToP2pMsg(msg []byte) (p2p.Msg, error) { | 
					
						
							|  |  |  | 	payload := &ProtocolMsg{} | 
					
						
							|  |  |  | 	if err := rlp.DecodeBytes(msg, payload); err != nil { | 
					
						
							|  |  |  | 		return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return p2p.Msg{ | 
					
						
							|  |  |  | 		Code:       payload.Code, | 
					
						
							|  |  |  | 		Size:       uint32(len(payload.Payload)), | 
					
						
							|  |  |  | 		ReceivedAt: time.Now(), | 
					
						
							|  |  |  | 		Payload:    bytes.NewBuffer(payload.Payload), | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Runs an emulated pss Protocol on the specified peer, | 
					
						
							|  |  |  | // linked to a specific topic | 
					
						
							|  |  |  | // `key` and `asymmetric` specifies what encryption key | 
					
						
							|  |  |  | // to link the peer to. | 
					
						
							|  |  |  | // The key must exist in the pss store prior to adding the peer. | 
					
						
							|  |  |  | func (p *Protocol) AddPeer(peer *p2p.Peer, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) { | 
					
						
							|  |  |  | 	rw := &PssReadWriter{ | 
					
						
							|  |  |  | 		Pss:   p.Pss, | 
					
						
							|  |  |  | 		rw:    make(chan p2p.Msg), | 
					
						
							|  |  |  | 		spec:  p.spec, | 
					
						
							|  |  |  | 		topic: p.topic, | 
					
						
							|  |  |  | 		key:   key, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if asymmetric { | 
					
						
							|  |  |  | 		rw.sendFunc = p.Pss.SendAsym | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		rw.sendFunc = p.Pss.SendSym | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if asymmetric { | 
					
						
							|  |  |  | 		p.Pss.pubKeyPoolMu.Lock() | 
					
						
							|  |  |  | 		if _, ok := p.Pss.pubKeyPool[key]; !ok { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("asym key does not exist: %s", key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		p.Pss.pubKeyPoolMu.Unlock() | 
					
						
							|  |  |  | 		p.RWPoolMu.Lock() | 
					
						
							|  |  |  | 		p.pubKeyRWPool[key] = rw | 
					
						
							|  |  |  | 		p.RWPoolMu.Unlock() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		p.Pss.symKeyPoolMu.Lock() | 
					
						
							|  |  |  | 		if _, ok := p.Pss.symKeyPool[key]; !ok { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("symkey does not exist: %s", key) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		p.Pss.symKeyPoolMu.Unlock() | 
					
						
							|  |  |  | 		p.RWPoolMu.Lock() | 
					
						
							|  |  |  | 		p.symKeyRWPool[key] = rw | 
					
						
							|  |  |  | 		p.RWPoolMu.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		err := p.proto.Run(peer, rw) | 
					
						
							|  |  |  | 		log.Warn(fmt.Sprintf("pss vprotocol quit on %v topic %v: %v", peer, topic, err)) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return rw, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Protocol) RemovePeer(asymmetric bool, key string) { | 
					
						
							|  |  |  | 	log.Debug("closing pss peer", "asym", asymmetric, "key", key) | 
					
						
							|  |  |  | 	p.RWPoolMu.Lock() | 
					
						
							|  |  |  | 	defer p.RWPoolMu.Unlock() | 
					
						
							|  |  |  | 	if asymmetric { | 
					
						
							|  |  |  | 		rw := p.pubKeyRWPool[key].(*PssReadWriter) | 
					
						
							|  |  |  | 		rw.closed = true | 
					
						
							|  |  |  | 		delete(p.pubKeyRWPool, key) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		rw := p.symKeyRWPool[key].(*PssReadWriter) | 
					
						
							|  |  |  | 		rw.closed = true | 
					
						
							|  |  |  | 		delete(p.symKeyRWPool, key) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Uniform translation of protocol specifiers to topic | 
					
						
							|  |  |  | func ProtocolTopic(spec *protocols.Spec) Topic { | 
					
						
							|  |  |  | 	return BytesToTopic([]byte(fmt.Sprintf("%s:%d", spec.Name, spec.Version))) | 
					
						
							|  |  |  | } |