// Copyright 2020 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package eth import ( "errors" "sync" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/eth/protocols/eth" "github.com/ledgerwatch/turbo-geth/p2p" ) var ( // errPeerSetClosed is returned if a peer is attempted to be added or removed // from the peer set after it has been terminated. errPeerSetClosed = errors.New("peerset closed") // errPeerAlreadyRegistered is returned if a peer is attempted to be added // to the peer set, but one with the same id already exists. errPeerAlreadyRegistered = errors.New("peer already registered") // errPeerNotRegistered is returned if a peer is attempted to be removed from // a peer set, but no peer with the given id exists. errPeerNotRegistered = errors.New("peer not registered") ) // peerSet represents the collection of active peers currently participating in // the `eth` protocol, with or without the `snap` extension. type peerSet struct { peers map[string]*ethPeer // Peers connected on the `eth` protocol lock sync.RWMutex closed bool } // newPeerSet creates a new peer set to track the active participants. func newPeerSet() *peerSet { return &peerSet{ peers: make(map[string]*ethPeer), } } // registerPeer injects a new `eth` peer into the working set, or returns an error // if the peer is already known. func (ps *peerSet) registerPeer(peer *eth.Peer) error { // Start tracking the new peer ps.lock.Lock() defer ps.lock.Unlock() if ps.closed { return errPeerSetClosed } id := peer.ID() if _, ok := ps.peers[id]; ok { return errPeerAlreadyRegistered } eth := ðPeer{ Peer: peer, } ps.peers[id] = eth return nil } // unregisterPeer removes a remote peer from the active set, disabling any further // actions to/from that particular entity. func (ps *peerSet) unregisterPeer(id string) error { ps.lock.Lock() defer ps.lock.Unlock() _, ok := ps.peers[id] if !ok { return errPeerNotRegistered } delete(ps.peers, id) return nil } // peer retrieves the registered peer with the given id. func (ps *peerSet) peer(id string) *ethPeer { ps.lock.RLock() defer ps.lock.RUnlock() return ps.peers[id] } // peersWithoutBlock retrieves a list of peers that do not have a given block in // their set of known hashes so it might be propagated to them. func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer { ps.lock.RLock() defer ps.lock.RUnlock() list := make([]*ethPeer, 0, len(ps.peers)) for _, p := range ps.peers { if !p.KnownBlock(hash) { list = append(list, p) } } return list } // peersWithoutTransaction retrieves a list of peers that do not have a given // transaction in their set of known hashes. func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer { ps.lock.RLock() defer ps.lock.RUnlock() list := make([]*ethPeer, 0, len(ps.peers)) for _, p := range ps.peers { if !p.KnownTransaction(hash) { list = append(list, p) } } return list } // len returns if the current number of `eth` peers in the set. Since the `snap` // peers are tied to the existence of an `eth` connection, that will always be a // subset of `eth`. func (ps *peerSet) len() int { ps.lock.RLock() defer ps.lock.RUnlock() return len(ps.peers) } // peerWithHighestNumber retrieves the known peer with the currently highest block height. func (ps *peerSet) peerWithHighestNumber() *eth.Peer { ps.lock.RLock() defer ps.lock.RUnlock() var ( bestPeer *eth.Peer bestNumber uint64 ) for _, p := range ps.peers { if _, headNumber := p.Head(); bestPeer == nil || headNumber > bestNumber { bestPeer, bestNumber = p.Peer, headNumber } } return bestPeer } // close disconnects all peers. func (ps *peerSet) close() { ps.lock.Lock() defer ps.lock.Unlock() for _, p := range ps.peers { p.Disconnect(p2p.DiscQuitting) } ps.closed = true }