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