github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/whisper.go (about) 1 // Copyright 2014 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package whisperv2 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "sync" 23 "time" 24 25 "github.com/SmartMeshFoundation/Spectrum/common" 26 "github.com/SmartMeshFoundation/Spectrum/crypto" 27 "github.com/SmartMeshFoundation/Spectrum/crypto/ecies" 28 "github.com/SmartMeshFoundation/Spectrum/event/filter" 29 "github.com/SmartMeshFoundation/Spectrum/log" 30 "github.com/SmartMeshFoundation/Spectrum/p2p" 31 "github.com/SmartMeshFoundation/Spectrum/rpc" 32 33 "gopkg.in/fatih/set.v0" 34 ) 35 36 const ( 37 statusCode = 0x00 38 messagesCode = 0x01 39 40 protocolVersion uint64 = 0x02 41 protocolName = "shh" 42 43 signatureFlag = byte(1 << 7) 44 signatureLength = 65 45 46 expirationCycle = 800 * time.Millisecond 47 transmissionCycle = 300 * time.Millisecond 48 ) 49 50 const ( 51 DefaultTTL = 50 * time.Second 52 DefaultPoW = 50 * time.Millisecond 53 ) 54 55 type MessageEvent struct { 56 To *ecdsa.PrivateKey 57 From *ecdsa.PublicKey 58 Message *Message 59 } 60 61 // Whisper represents a dark communication interface through the Ethereum 62 // network, using its very own P2P communication layer. 63 type Whisper struct { 64 protocol p2p.Protocol 65 filters *filter.Filters 66 67 keys map[string]*ecdsa.PrivateKey 68 69 messages map[common.Hash]*Envelope // Pool of messages currently tracked by this node 70 expirations map[uint32]*set.SetNonTS // Message expiration pool (TODO: something lighter) 71 poolMu sync.RWMutex // Mutex to sync the message and expiration pools 72 73 peers map[*peer]struct{} // Set of currently active peers 74 peerMu sync.RWMutex // Mutex to sync the active peer set 75 76 quit chan struct{} 77 } 78 79 // New creates a Whisper client ready to communicate through the Ethereum P2P 80 // network. 81 func New() *Whisper { 82 whisper := &Whisper{ 83 filters: filter.New(), 84 keys: make(map[string]*ecdsa.PrivateKey), 85 messages: make(map[common.Hash]*Envelope), 86 expirations: make(map[uint32]*set.SetNonTS), 87 peers: make(map[*peer]struct{}), 88 quit: make(chan struct{}), 89 } 90 whisper.filters.Start() 91 92 // p2p whisper sub protocol handler 93 whisper.protocol = p2p.Protocol{ 94 Name: protocolName, 95 Version: uint(protocolVersion), 96 Length: 2, 97 Run: whisper.handlePeer, 98 } 99 100 return whisper 101 } 102 103 // APIs returns the RPC descriptors the Whisper implementation offers 104 func (s *Whisper) APIs() []rpc.API { 105 return []rpc.API{ 106 { 107 Namespace: "shh", 108 Version: "1.0", 109 Service: NewPublicWhisperAPI(s), 110 Public: true, 111 }, 112 } 113 } 114 115 // Protocols returns the whisper sub-protocols ran by this particular client. 116 func (self *Whisper) Protocols() []p2p.Protocol { 117 return []p2p.Protocol{self.protocol} 118 } 119 120 // Version returns the whisper sub-protocols version number. 121 func (self *Whisper) Version() uint { 122 return self.protocol.Version 123 } 124 125 // NewIdentity generates a new cryptographic identity for the client, and injects 126 // it into the known identities for message decryption. 127 func (self *Whisper) NewIdentity() *ecdsa.PrivateKey { 128 key, err := crypto.GenerateKey() 129 if err != nil { 130 panic(err) 131 } 132 self.keys[string(crypto.FromECDSAPub(&key.PublicKey))] = key 133 134 return key 135 } 136 137 // HasIdentity checks if the the whisper node is configured with the private key 138 // of the specified public pair. 139 func (self *Whisper) HasIdentity(key *ecdsa.PublicKey) bool { 140 return self.keys[string(crypto.FromECDSAPub(key))] != nil 141 } 142 143 // GetIdentity retrieves the private key of the specified public identity. 144 func (self *Whisper) GetIdentity(key *ecdsa.PublicKey) *ecdsa.PrivateKey { 145 return self.keys[string(crypto.FromECDSAPub(key))] 146 } 147 148 // Watch installs a new message handler to run in case a matching packet arrives 149 // from the whisper network. 150 func (self *Whisper) Watch(options Filter) int { 151 filter := filterer{ 152 to: string(crypto.FromECDSAPub(options.To)), 153 from: string(crypto.FromECDSAPub(options.From)), 154 matcher: newTopicMatcher(options.Topics...), 155 fn: func(data interface{}) { 156 options.Fn(data.(*Message)) 157 }, 158 } 159 return self.filters.Install(filter) 160 } 161 162 // Unwatch removes an installed message handler. 163 func (self *Whisper) Unwatch(id int) { 164 self.filters.Uninstall(id) 165 } 166 167 // Send injects a message into the whisper send queue, to be distributed in the 168 // network in the coming cycles. 169 func (self *Whisper) Send(envelope *Envelope) error { 170 return self.add(envelope) 171 } 172 173 // Start implements node.Service, starting the background data propagation thread 174 // of the Whisper protocol. 175 func (self *Whisper) Start(*p2p.Server) error { 176 log.Info("Whisper started") 177 go self.update() 178 return nil 179 } 180 181 // Stop implements node.Service, stopping the background data propagation thread 182 // of the Whisper protocol. 183 func (self *Whisper) Stop() error { 184 close(self.quit) 185 log.Info("Whisper stopped") 186 return nil 187 } 188 189 // Messages retrieves all the currently pooled messages matching a filter id. 190 func (self *Whisper) Messages(id int) []*Message { 191 messages := make([]*Message, 0) 192 if filter := self.filters.Get(id); filter != nil { 193 for _, envelope := range self.messages { 194 if message := self.open(envelope); message != nil { 195 if self.filters.Match(filter, createFilter(message, envelope.Topics)) { 196 messages = append(messages, message) 197 } 198 } 199 } 200 } 201 return messages 202 } 203 204 // handlePeer is called by the underlying P2P layer when the whisper sub-protocol 205 // connection is negotiated. 206 func (self *Whisper) handlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 207 // Create the new peer and start tracking it 208 whisperPeer := newPeer(self, peer, rw) 209 210 self.peerMu.Lock() 211 self.peers[whisperPeer] = struct{}{} 212 self.peerMu.Unlock() 213 214 defer func() { 215 self.peerMu.Lock() 216 delete(self.peers, whisperPeer) 217 self.peerMu.Unlock() 218 }() 219 220 // Run the peer handshake and state updates 221 if err := whisperPeer.handshake(); err != nil { 222 return err 223 } 224 whisperPeer.start() 225 defer whisperPeer.stop() 226 227 // Read and process inbound messages directly to merge into client-global state 228 for { 229 // Fetch the next packet and decode the contained envelopes 230 packet, err := rw.ReadMsg() 231 if err != nil { 232 return err 233 } 234 var envelopes []*Envelope 235 if err := packet.Decode(&envelopes); err != nil { 236 log.Info(fmt.Sprintf("%v: failed to decode envelope: %v", peer, err)) 237 continue 238 } 239 // Inject all envelopes into the internal pool 240 for _, envelope := range envelopes { 241 if err := self.add(envelope); err != nil { 242 // TODO Punish peer here. Invalid envelope. 243 log.Debug(fmt.Sprintf("%v: failed to pool envelope: %v", peer, err)) 244 } 245 whisperPeer.mark(envelope) 246 } 247 } 248 } 249 250 // add inserts a new envelope into the message pool to be distributed within the 251 // whisper network. It also inserts the envelope into the expiration pool at the 252 // appropriate time-stamp. 253 func (self *Whisper) add(envelope *Envelope) error { 254 self.poolMu.Lock() 255 defer self.poolMu.Unlock() 256 257 // short circuit when a received envelope has already expired 258 if envelope.Expiry < uint32(time.Now().Unix()) { 259 return nil 260 } 261 262 // Insert the message into the tracked pool 263 hash := envelope.Hash() 264 if _, ok := self.messages[hash]; ok { 265 log.Trace(fmt.Sprintf("whisper envelope already cached: %x\n", envelope)) 266 return nil 267 } 268 self.messages[hash] = envelope 269 270 // Insert the message into the expiration pool for later removal 271 if self.expirations[envelope.Expiry] == nil { 272 self.expirations[envelope.Expiry] = set.NewNonTS() 273 } 274 if !self.expirations[envelope.Expiry].Has(hash) { 275 self.expirations[envelope.Expiry].Add(hash) 276 277 // Notify the local node of a message arrival 278 go self.postEvent(envelope) 279 } 280 log.Trace(fmt.Sprintf("cached whisper envelope %x\n", envelope)) 281 return nil 282 } 283 284 // postEvent opens an envelope with the configured identities and delivers the 285 // message upstream from application processing. 286 func (self *Whisper) postEvent(envelope *Envelope) { 287 if message := self.open(envelope); message != nil { 288 self.filters.Notify(createFilter(message, envelope.Topics), message) 289 } 290 } 291 292 // open tries to decrypt a whisper envelope with all the configured identities, 293 // returning the decrypted message and the key used to achieve it. If not keys 294 // are configured, open will return the payload as if non encrypted. 295 func (self *Whisper) open(envelope *Envelope) *Message { 296 // Short circuit if no identity is set, and assume clear-text 297 if len(self.keys) == 0 { 298 if message, err := envelope.Open(nil); err == nil { 299 return message 300 } 301 } 302 // Iterate over the keys and try to decrypt the message 303 for _, key := range self.keys { 304 message, err := envelope.Open(key) 305 if err == nil { 306 message.To = &key.PublicKey 307 return message 308 } else if err == ecies.ErrInvalidPublicKey { 309 return message 310 } 311 } 312 // Failed to decrypt, don't return anything 313 return nil 314 } 315 316 // createFilter creates a message filter to check against installed handlers. 317 func createFilter(message *Message, topics []Topic) filter.Filter { 318 matcher := make([][]Topic, len(topics)) 319 for i, topic := range topics { 320 matcher[i] = []Topic{topic} 321 } 322 return filterer{ 323 to: string(crypto.FromECDSAPub(message.To)), 324 from: string(crypto.FromECDSAPub(message.Recover())), 325 matcher: newTopicMatcher(matcher...), 326 } 327 } 328 329 // update loops until the lifetime of the whisper node, updating its internal 330 // state by expiring stale messages from the pool. 331 func (self *Whisper) update() { 332 // Start a ticker to check for expirations 333 expire := time.NewTicker(expirationCycle) 334 335 // Repeat updates until termination is requested 336 for { 337 select { 338 case <-expire.C: 339 self.expire() 340 341 case <-self.quit: 342 return 343 } 344 } 345 } 346 347 // expire iterates over all the expiration timestamps, removing all stale 348 // messages from the pools. 349 func (self *Whisper) expire() { 350 self.poolMu.Lock() 351 defer self.poolMu.Unlock() 352 353 now := uint32(time.Now().Unix()) 354 for then, hashSet := range self.expirations { 355 // Short circuit if a future time 356 if then > now { 357 continue 358 } 359 // Dump all expired messages and remove timestamp 360 hashSet.Each(func(v interface{}) bool { 361 delete(self.messages, v.(common.Hash)) 362 return true 363 }) 364 self.expirations[then].Clear() 365 } 366 } 367 368 // envelopes retrieves all the messages currently pooled by the node. 369 func (self *Whisper) envelopes() []*Envelope { 370 self.poolMu.RLock() 371 defer self.poolMu.RUnlock() 372 373 envelopes := make([]*Envelope, 0, len(self.messages)) 374 for _, envelope := range self.messages { 375 envelopes = append(envelopes, envelope) 376 } 377 return envelopes 378 }