package peer import ( "errors" "sync" "sync/atomic" . "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/merkle" ) /* A client is half of a p2p system. It can reach out to the network and establish connections with other peers. A client doesn't listen for incoming connections -- that's done by the server. All communication amongst peers are multiplexed by "channels". (Not the same as Go "channels") To send a message, encapsulate it into a "Packet" and send it to each peer. You can find all connected and active peers by iterating over ".Peers()". ".Broadcast()" is provided for convenience, but by iterating over the peers manually the caller can decide which subset receives a message. Incoming messages are received by calling ".Receive()". */ type Client struct { addrBook *AddrBook targetNumPeers int makePeerFn func(*Connection) *Peer self *Peer pktRecvQueues map[String]chan *InboundPacket peersMtx sync.Mutex peers merkle.Tree // addr -> *Peer quit chan struct{} stopped uint32 } var ( ErrClientStopped = errors.New("Client already stopped") ErrClientDuplicatePeer = errors.New("Duplicate peer") ) // "makePeerFn" is a factory method for generating new peers from new *Connections. // "makePeerFn(nil)" must return a prototypical peer that represents the self "peer". func NewClient(makePeerFn func(*Connection) *Peer) *Client { self := makePeerFn(nil) if self == nil { Panicf("makePeerFn(nil) must return a prototypical peer for self") } pktRecvQueues := make(map[String]chan *InboundPacket) for chName, _ := range self.channels { pktRecvQueues[chName] = make(chan *InboundPacket) } c := &Client{ addrBook: nil, // TODO targetNumPeers: 0, // TODO makePeerFn: makePeerFn, self: self, pktRecvQueues: pktRecvQueues, peers: merkle.NewIAVLTree(nil), quit: make(chan struct{}), stopped: 0, } // automatically start c.start() return c } func (c *Client) start() { // Handle PEX messages // TODO: hmm // go peerExchangeHandler(c) } func (c *Client) Stop() { log.Infof("Stopping client") // lock c.peersMtx.Lock() if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) { close(c.quit) // stop each peer. for peerValue := range c.peers.Values() { peer := peerValue.(*Peer) peer.stop() } // empty tree. c.peers = merkle.NewIAVLTree(nil) } c.peersMtx.Unlock() // unlock } func (c *Client) AddPeerWithConnection(conn *Connection, outgoing bool) (*Peer, error) { if atomic.LoadUint32(&c.stopped) == 1 { return nil, ErrClientStopped } log.Infof("Adding peer with connection: %v, outgoing: %v", conn, outgoing) peer := c.makePeerFn(conn) peer.outgoing = outgoing err := c.addPeer(peer) if err != nil { return nil, err } go peer.start(c.pktRecvQueues, c.StopPeerForError) return peer, nil } func (c *Client) Broadcast(pkt Packet) (numSuccess, numFailure int) { if atomic.LoadUint32(&c.stopped) == 1 { return } log.Tracef("Broadcast on [%v] len: %v", pkt.Channel, len(pkt.Bytes)) for v := range c.peers.Values() { peer := v.(*Peer) success := peer.TryQueue(pkt) log.Tracef("Broadcast for peer %v success: %v", peer, success) if success { numSuccess += 1 } else { numFailure += 1 } } return } /* Receive blocks on a channel until a message is found. */ func (c *Client) Receive(chName String) *InboundPacket { if atomic.LoadUint32(&c.stopped) == 1 { return nil } log.Tracef("Receive on [%v]", chName) q := c.pktRecvQueues[chName] if q == nil { Panicf("Expected pktRecvQueues[%f], found none", chName) } select { case <-c.quit: return nil case inPacket := <-q: return inPacket } } func (c *Client) Peers() merkle.Tree { // lock & defer c.peersMtx.Lock() defer c.peersMtx.Unlock() return c.peers.Copy() // unlock deferred } // Disconnect from a peer due to external error. // TODO: make record depending on reason. func (c *Client) StopPeerForError(peer *Peer, reason interface{}) { log.Infof("%v errored: %v", peer, reason) c.StopPeer(peer, false) } // Disconnect from a peer. // If graceful is true, last message sent is a disconnect message. // TODO: handle graceful disconnects. func (c *Client) StopPeer(peer *Peer, graceful bool) { // lock c.peersMtx.Lock() peerValue, _ := c.peers.Remove(peer.RemoteAddress()) c.peersMtx.Unlock() // unlock peer_ := peerValue.(*Peer) if peer_ != nil { peer_.stop() } } func (c *Client) addPeer(peer *Peer) error { addr := peer.RemoteAddress() // lock & defer c.peersMtx.Lock() defer c.peersMtx.Unlock() if c.stopped == 1 { return ErrClientStopped } if !c.peers.Has(addr) { log.Tracef("Actually putting addr: %v, peer: %v", addr, peer) c.peers.Put(addr, peer) return nil } else { // ignore duplicate peer for addr. log.Infof("Ignoring duplicate peer for addr %v", addr) return ErrClientDuplicatePeer } // unlock deferred }