github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/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/ShyftNetwork/go-empyrean/p2p" 28 "github.com/ShyftNetwork/go-empyrean/p2p/protocols" 29 "github.com/ShyftNetwork/go-empyrean/rlp" 30 "github.com/ShyftNetwork/go-empyrean/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 } else if rw == nil { 176 return fmt.Errorf("handle called on nil MsgReadWriter for new key " + keyid) 177 } 178 vrw = rw.(*PssReadWriter) 179 } 180 181 pmsg, err := ToP2pMsg(msg) 182 if err != nil { 183 return fmt.Errorf("could not decode pssmsg") 184 } 185 if asymmetric { 186 if p.pubKeyRWPool[keyid] == nil { 187 return fmt.Errorf("handle called on nil MsgReadWriter for key " + keyid) 188 } 189 vrw = p.pubKeyRWPool[keyid].(*PssReadWriter) 190 } else { 191 if p.symKeyRWPool[keyid] == nil { 192 return fmt.Errorf("handle called on nil MsgReadWriter for key " + keyid) 193 } 194 vrw = p.symKeyRWPool[keyid].(*PssReadWriter) 195 } 196 vrw.injectMsg(pmsg) 197 return nil 198 } 199 200 // check if (peer) symmetric key is currently registered with this topic 201 func (p *Protocol) isActiveSymKey(key string, topic Topic) bool { 202 return p.symKeyRWPool[key] != nil 203 } 204 205 // check if (peer) asymmetric key is currently registered with this topic 206 func (p *Protocol) isActiveAsymKey(key string, topic Topic) bool { 207 return p.pubKeyRWPool[key] != nil 208 } 209 210 // Creates a serialized (non-buffered) version of a p2p.Msg, used in the specialized internal p2p.MsgReadwriter implementations 211 func ToP2pMsg(msg []byte) (p2p.Msg, error) { 212 payload := &ProtocolMsg{} 213 if err := rlp.DecodeBytes(msg, payload); err != nil { 214 return p2p.Msg{}, fmt.Errorf("pss protocol handler unable to decode payload as p2p message: %v", err) 215 } 216 217 return p2p.Msg{ 218 Code: payload.Code, 219 Size: uint32(len(payload.Payload)), 220 ReceivedAt: time.Now(), 221 Payload: bytes.NewBuffer(payload.Payload), 222 }, nil 223 } 224 225 // Runs an emulated pss Protocol on the specified peer, 226 // linked to a specific topic 227 // `key` and `asymmetric` specifies what encryption key 228 // to link the peer to. 229 // The key must exist in the pss store prior to adding the peer. 230 func (p *Protocol) AddPeer(peer *p2p.Peer, topic Topic, asymmetric bool, key string) (p2p.MsgReadWriter, error) { 231 rw := &PssReadWriter{ 232 Pss: p.Pss, 233 rw: make(chan p2p.Msg), 234 spec: p.spec, 235 topic: p.topic, 236 key: key, 237 } 238 if asymmetric { 239 rw.sendFunc = p.Pss.SendAsym 240 } else { 241 rw.sendFunc = p.Pss.SendSym 242 } 243 if asymmetric { 244 p.Pss.pubKeyPoolMu.Lock() 245 if _, ok := p.Pss.pubKeyPool[key]; !ok { 246 return nil, fmt.Errorf("asym key does not exist: %s", key) 247 } 248 p.Pss.pubKeyPoolMu.Unlock() 249 p.RWPoolMu.Lock() 250 p.pubKeyRWPool[key] = rw 251 p.RWPoolMu.Unlock() 252 } else { 253 p.Pss.symKeyPoolMu.Lock() 254 if _, ok := p.Pss.symKeyPool[key]; !ok { 255 return nil, fmt.Errorf("symkey does not exist: %s", key) 256 } 257 p.Pss.symKeyPoolMu.Unlock() 258 p.RWPoolMu.Lock() 259 p.symKeyRWPool[key] = rw 260 p.RWPoolMu.Unlock() 261 } 262 go func() { 263 err := p.proto.Run(peer, rw) 264 log.Warn(fmt.Sprintf("pss vprotocol quit on %v topic %v: %v", peer, topic, err)) 265 }() 266 return rw, nil 267 } 268 269 func (p *Protocol) RemovePeer(asymmetric bool, key string) { 270 log.Debug("closing pss peer", "asym", asymmetric, "key", key) 271 p.RWPoolMu.Lock() 272 defer p.RWPoolMu.Unlock() 273 if asymmetric { 274 rw := p.pubKeyRWPool[key].(*PssReadWriter) 275 rw.closed = true 276 delete(p.pubKeyRWPool, key) 277 } else { 278 rw := p.symKeyRWPool[key].(*PssReadWriter) 279 rw.closed = true 280 delete(p.symKeyRWPool, key) 281 } 282 } 283 284 // Uniform translation of protocol specifiers to topic 285 func ProtocolTopic(spec *protocols.Spec) Topic { 286 return BytesToTopic([]byte(fmt.Sprintf("%s:%d", spec.Name, spec.Version))) 287 }