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