github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/whisper/whisperv2/peer.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 "fmt" 21 "time" 22 23 "github.com/SmartMeshFoundation/Spectrum/common" 24 "github.com/SmartMeshFoundation/Spectrum/log" 25 "github.com/SmartMeshFoundation/Spectrum/p2p" 26 "github.com/SmartMeshFoundation/Spectrum/rlp" 27 "gopkg.in/fatih/set.v0" 28 ) 29 30 // peer represents a whisper protocol peer connection. 31 type peer struct { 32 host *Whisper 33 peer *p2p.Peer 34 ws p2p.MsgReadWriter 35 36 known *set.Set // Messages already known by the peer to avoid wasting bandwidth 37 38 quit chan struct{} 39 } 40 41 // newPeer creates a new whisper peer object, but does not run the handshake itself. 42 func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *peer { 43 return &peer{ 44 host: host, 45 peer: remote, 46 ws: rw, 47 known: set.New(), 48 quit: make(chan struct{}), 49 } 50 } 51 52 // start initiates the peer updater, periodically broadcasting the whisper packets 53 // into the network. 54 func (self *peer) start() { 55 go self.update() 56 log.Debug(fmt.Sprintf("%v: whisper started", self.peer)) 57 } 58 59 // stop terminates the peer updater, stopping message forwarding to it. 60 func (self *peer) stop() { 61 close(self.quit) 62 log.Debug(fmt.Sprintf("%v: whisper stopped", self.peer)) 63 } 64 65 // handshake sends the protocol initiation status message to the remote peer and 66 // verifies the remote status too. 67 func (self *peer) handshake() error { 68 // Send the handshake status message asynchronously 69 errc := make(chan error, 1) 70 go func() { 71 errc <- p2p.SendItems(self.ws, statusCode, protocolVersion) 72 }() 73 // Fetch the remote status packet and verify protocol match 74 packet, err := self.ws.ReadMsg() 75 if err != nil { 76 return err 77 } 78 if packet.Code != statusCode { 79 return fmt.Errorf("peer sent %x before status packet", packet.Code) 80 } 81 s := rlp.NewStream(packet.Payload, uint64(packet.Size)) 82 if _, err := s.List(); err != nil { 83 return fmt.Errorf("bad status message: %v", err) 84 } 85 peerVersion, err := s.Uint() 86 if err != nil { 87 return fmt.Errorf("bad status message: %v", err) 88 } 89 if peerVersion != protocolVersion { 90 return fmt.Errorf("protocol version mismatch %d != %d", peerVersion, protocolVersion) 91 } 92 // Wait until out own status is consumed too 93 if err := <-errc; err != nil { 94 return fmt.Errorf("failed to send status packet: %v", err) 95 } 96 return nil 97 } 98 99 // update executes periodic operations on the peer, including message transmission 100 // and expiration. 101 func (self *peer) update() { 102 // Start the tickers for the updates 103 expire := time.NewTicker(expirationCycle) 104 transmit := time.NewTicker(transmissionCycle) 105 106 // Loop and transmit until termination is requested 107 for { 108 select { 109 case <-expire.C: 110 self.expire() 111 112 case <-transmit.C: 113 if err := self.broadcast(); err != nil { 114 log.Info(fmt.Sprintf("%v: broadcast failed: %v", self.peer, err)) 115 return 116 } 117 118 case <-self.quit: 119 return 120 } 121 } 122 } 123 124 // mark marks an envelope known to the peer so that it won't be sent back. 125 func (self *peer) mark(envelope *Envelope) { 126 self.known.Add(envelope.Hash()) 127 } 128 129 // marked checks if an envelope is already known to the remote peer. 130 func (self *peer) marked(envelope *Envelope) bool { 131 return self.known.Has(envelope.Hash()) 132 } 133 134 // expire iterates over all the known envelopes in the host and removes all 135 // expired (unknown) ones from the known list. 136 func (self *peer) expire() { 137 // Assemble the list of available envelopes 138 available := set.NewNonTS() 139 for _, envelope := range self.host.envelopes() { 140 available.Add(envelope.Hash()) 141 } 142 // Cross reference availability with known status 143 unmark := make(map[common.Hash]struct{}) 144 self.known.Each(func(v interface{}) bool { 145 if !available.Has(v.(common.Hash)) { 146 unmark[v.(common.Hash)] = struct{}{} 147 } 148 return true 149 }) 150 // Dump all known but unavailable 151 for hash := range unmark { 152 self.known.Remove(hash) 153 } 154 } 155 156 // broadcast iterates over the collection of envelopes and transmits yet unknown 157 // ones over the network. 158 func (self *peer) broadcast() error { 159 // Fetch the envelopes and collect the unknown ones 160 envelopes := self.host.envelopes() 161 transmit := make([]*Envelope, 0, len(envelopes)) 162 for _, envelope := range envelopes { 163 if !self.marked(envelope) { 164 transmit = append(transmit, envelope) 165 self.mark(envelope) 166 } 167 } 168 // Transmit the unknown batch (potentially empty) 169 if err := p2p.Send(self.ws, messagesCode, transmit); err != nil { 170 return err 171 } 172 log.Trace(fmt.Sprint(self.peer, "broadcasted", len(transmit), "message(s)")) 173 return nil 174 }