github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/whisper/whisperv5/peer.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package whisperv5 13 14 import ( 15 "fmt" 16 "time" 17 18 "github.com/Sberex/go-sberex/common" 19 "github.com/Sberex/go-sberex/log" 20 "github.com/Sberex/go-sberex/p2p" 21 "github.com/Sberex/go-sberex/rlp" 22 set "gopkg.in/fatih/set.v0" 23 ) 24 25 // peer represents a whisper protocol peer connection. 26 type Peer struct { 27 host *Whisper 28 peer *p2p.Peer 29 ws p2p.MsgReadWriter 30 trusted bool 31 32 known *set.Set // Messages already known by the peer to avoid wasting bandwidth 33 34 quit chan struct{} 35 } 36 37 // newPeer creates a new whisper peer object, but does not run the handshake itself. 38 func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { 39 return &Peer{ 40 host: host, 41 peer: remote, 42 ws: rw, 43 trusted: false, 44 known: set.New(), 45 quit: make(chan struct{}), 46 } 47 } 48 49 // start initiates the peer updater, periodically broadcasting the whisper packets 50 // into the network. 51 func (p *Peer) start() { 52 go p.update() 53 log.Trace("start", "peer", p.ID()) 54 } 55 56 // stop terminates the peer updater, stopping message forwarding to it. 57 func (p *Peer) stop() { 58 close(p.quit) 59 log.Trace("stop", "peer", p.ID()) 60 } 61 62 // handshake sends the protocol initiation status message to the remote peer and 63 // verifies the remote status too. 64 func (p *Peer) handshake() error { 65 // Send the handshake status message asynchronously 66 errc := make(chan error, 1) 67 go func() { 68 errc <- p2p.Send(p.ws, statusCode, ProtocolVersion) 69 }() 70 // Fetch the remote status packet and verify protocol match 71 packet, err := p.ws.ReadMsg() 72 if err != nil { 73 return err 74 } 75 if packet.Code != statusCode { 76 return fmt.Errorf("peer [%x] sent packet %x before status packet", p.ID(), packet.Code) 77 } 78 s := rlp.NewStream(packet.Payload, uint64(packet.Size)) 79 peerVersion, err := s.Uint() 80 if err != nil { 81 return fmt.Errorf("peer [%x] sent bad status message: %v", p.ID(), err) 82 } 83 if peerVersion != ProtocolVersion { 84 return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", p.ID(), peerVersion, ProtocolVersion) 85 } 86 // Wait until out own status is consumed too 87 if err := <-errc; err != nil { 88 return fmt.Errorf("peer [%x] failed to send status packet: %v", p.ID(), err) 89 } 90 return nil 91 } 92 93 // update executes periodic operations on the peer, including message transmission 94 // and expiration. 95 func (p *Peer) update() { 96 // Start the tickers for the updates 97 expire := time.NewTicker(expirationCycle) 98 transmit := time.NewTicker(transmissionCycle) 99 100 // Loop and transmit until termination is requested 101 for { 102 select { 103 case <-expire.C: 104 p.expire() 105 106 case <-transmit.C: 107 if err := p.broadcast(); err != nil { 108 log.Trace("broadcast failed", "reason", err, "peer", p.ID()) 109 return 110 } 111 112 case <-p.quit: 113 return 114 } 115 } 116 } 117 118 // mark marks an envelope known to the peer so that it won't be sent back. 119 func (peer *Peer) mark(envelope *Envelope) { 120 peer.known.Add(envelope.Hash()) 121 } 122 123 // marked checks if an envelope is already known to the remote peer. 124 func (peer *Peer) marked(envelope *Envelope) bool { 125 return peer.known.Has(envelope.Hash()) 126 } 127 128 // expire iterates over all the known envelopes in the host and removes all 129 // expired (unknown) ones from the known list. 130 func (peer *Peer) expire() { 131 unmark := make(map[common.Hash]struct{}) 132 peer.known.Each(func(v interface{}) bool { 133 if !peer.host.isEnvelopeCached(v.(common.Hash)) { 134 unmark[v.(common.Hash)] = struct{}{} 135 } 136 return true 137 }) 138 // Dump all known but no longer cached 139 for hash := range unmark { 140 peer.known.Remove(hash) 141 } 142 } 143 144 // broadcast iterates over the collection of envelopes and transmits yet unknown 145 // ones over the network. 146 func (p *Peer) broadcast() error { 147 var cnt int 148 envelopes := p.host.Envelopes() 149 for _, envelope := range envelopes { 150 if !p.marked(envelope) { 151 err := p2p.Send(p.ws, messagesCode, envelope) 152 if err != nil { 153 return err 154 } else { 155 p.mark(envelope) 156 cnt++ 157 } 158 } 159 } 160 if cnt > 0 { 161 log.Trace("broadcast", "num. messages", cnt) 162 } 163 return nil 164 } 165 166 func (p *Peer) ID() []byte { 167 id := p.peer.ID() 168 return id[:] 169 }