github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/whisper/whisperv5/peer.go (about)

     1  // Copyright 2016 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 whisperv5
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	"github.com/vntchain/go-vnt/common"
    24  	"github.com/vntchain/go-vnt/log"
    25  	"github.com/vntchain/go-vnt/rlp"
    26  	"github.com/vntchain/go-vnt/vntp2p"
    27  	set "gopkg.in/fatih/set.v0"
    28  
    29  	libp2p "github.com/libp2p/go-libp2p-peer"
    30  )
    31  
    32  // Peer represents a whisper protocol peer connection.
    33  type Peer struct {
    34  	host    *Whisper
    35  	peer    *vntp2p.Peer
    36  	ws      vntp2p.MsgReadWriter
    37  	trusted bool
    38  
    39  	known *set.Set // Messages already known by the peer to avoid wasting bandwidth
    40  
    41  	quit chan struct{}
    42  }
    43  
    44  // newPeer creates a new whisper peer object, but does not run the handshake itself.
    45  func newPeer(host *Whisper, remote *vntp2p.Peer, rw vntp2p.MsgReadWriter) *Peer {
    46  	return &Peer{
    47  		host:    host,
    48  		peer:    remote,
    49  		ws:      rw,
    50  		trusted: false,
    51  		known:   set.New(),
    52  		quit:    make(chan struct{}),
    53  	}
    54  }
    55  
    56  // start initiates the peer updater, periodically broadcasting the whisper packets
    57  // into the network.
    58  func (peer *Peer) start() {
    59  	go peer.update()
    60  	log.Trace("start", "peer", peer.ID())
    61  }
    62  
    63  // stop terminates the peer updater, stopping message forwarding to it.
    64  func (peer *Peer) stop() {
    65  	close(peer.quit)
    66  	log.Trace("stop", "peer", peer.ID())
    67  }
    68  
    69  // handshake sends the protocol initiation status message to the remote peer and
    70  // verifies the remote status too.
    71  func (peer *Peer) handshake() error {
    72  	// Send the handshake status message asynchronously
    73  	errc := make(chan error, 1)
    74  	go func() {
    75  		errc <- vntp2p.Send(peer.ws, ProtocolName, statusCode, ProtocolVersion)
    76  	}()
    77  	// Fetch the remote status packet and verify protocol match
    78  	packet, err := peer.ws.ReadMsg()
    79  	if err != nil {
    80  		return err
    81  	}
    82  	if packet.Body.Type != statusCode {
    83  		return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Body.Type)
    84  	}
    85  	s := rlp.NewStream(packet.Body.Payload, uint64(packet.Body.PayloadSize))
    86  	peerVersion, err := s.Uint()
    87  	if err != nil {
    88  		return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err)
    89  	}
    90  	if peerVersion != ProtocolVersion {
    91  		return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion)
    92  	}
    93  	// Wait until out own status is consumed too
    94  	if err := <-errc; err != nil {
    95  		return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err)
    96  	}
    97  	return nil
    98  }
    99  
   100  // update executes periodic operations on the peer, including message transmission
   101  // and expiration.
   102  func (peer *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  			peer.expire()
   112  
   113  		case <-transmit.C:
   114  			if err := peer.broadcast(); err != nil {
   115  				log.Trace("broadcast failed", "reason", err, "peer", peer.ID())
   116  				return
   117  			}
   118  
   119  		case <-peer.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 (peer *Peer) mark(envelope *Envelope) {
   127  	peer.known.Add(envelope.Hash())
   128  }
   129  
   130  // marked checks if an envelope is already known to the remote peer.
   131  func (peer *Peer) marked(envelope *Envelope) bool {
   132  	return peer.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 (peer *Peer) expire() {
   138  	unmark := make(map[common.Hash]struct{})
   139  	peer.known.Each(func(v interface{}) bool {
   140  		if !peer.host.isEnvelopeCached(v.(common.Hash)) {
   141  			unmark[v.(common.Hash)] = struct{}{}
   142  		}
   143  		return true
   144  	})
   145  	// Dump all known but no longer cached
   146  	for hash := range unmark {
   147  		peer.known.Remove(hash)
   148  	}
   149  }
   150  
   151  // broadcast iterates over the collection of envelopes and transmits yet unknown
   152  // ones over the network.
   153  func (peer *Peer) broadcast() error {
   154  	var cnt int
   155  	envelopes := peer.host.Envelopes()
   156  	for _, envelope := range envelopes {
   157  		if !peer.marked(envelope) {
   158  			err := vntp2p.Send(peer.ws, ProtocolName, messagesCode, envelope)
   159  			if err != nil {
   160  				return err
   161  			} else {
   162  				peer.mark(envelope)
   163  				cnt++
   164  			}
   165  		}
   166  	}
   167  	if cnt > 0 {
   168  		log.Trace("broadcast", "num. messages", cnt)
   169  	}
   170  	return nil
   171  }
   172  
   173  func (peer *Peer) ID() libp2p.ID {
   174  	id := peer.peer.RemoteID()
   175  	return id
   176  }