p2p: new dial scheduler (#20592)
* p2p: new dial scheduler This change replaces the peer-to-peer dial scheduler with a new and improved implementation. The new code is better than the previous implementation in two key aspects: - The time between discovery of a node and dialing that node is significantly lower in the new version. The old dialState kept a buffer of nodes and launched a task to refill it whenever the buffer became empty. This worked well with the discovery interface we used to have, but doesn't really work with the new iterator-based discovery API. - Selection of static dial candidates (created by Server.AddPeer or through static-nodes.json) performs much better for large amounts of static peers. Connections to static nodes are now limited like dynanic dials and can no longer overstep MaxPeers or the dial ratio. * p2p/simulations/adapters: adapt to new NodeDialer interface * p2p: re-add check for self in checkDial * p2p: remove peersetCh * p2p: allow static dials when discovery is disabled * p2p: add test for dialScheduler.removeStatic * p2p: remove blank line * p2p: fix documentation of maxDialPeers * p2p: change "ok" to "added" in static node log * p2p: improve dialTask docs Also increase log level for "Can't resolve node" * p2p: ensure dial resolver is truly nil without discovery * p2p: add "looking for peers" log message * p2p: clean up Server.run comments * p2p: fix maxDialedConns for maxpeers < dialRatio Always allocate at least one dial slot unless dialing is disabled using NoDial or MaxPeers == 0. Most importantly, this fixes MaxPeers == 1 to dedicate the sole slot to dialing instead of listening. * p2p: fix RemovePeer to disconnect the peer again Also make RemovePeer synchronous and add a test. * p2p: remove "Connection set up" log message * p2p: clean up connection logging We previously logged outgoing connection failures up to three times. - in SetupConn() as "Setting up connection failed addr=..." - in setupConn() with an error-specific message and "id=... addr=..." - in dial() as "Dial error task=..." This commit ensures a single log message is emitted per failure and adds "id=... addr=... conn=..." everywhere (id= omitted when the ID isn't known yet). Also avoid printing a log message when a static dial fails but can't be resolved because discv4 is disabled. The light client hit this case all the time, increasing the message count to four lines per failed connection. * p2p: document that RemovePeer blocks
This commit is contained in:
320
p2p/server.go
320
p2p/server.go
@ -51,7 +51,6 @@ const (
|
||||
discmixTimeout = 5 * time.Second
|
||||
|
||||
// Connectivity defaults.
|
||||
maxActiveDialTasks = 16
|
||||
defaultMaxPendingPeers = 50
|
||||
defaultDialRatio = 3
|
||||
|
||||
@ -156,6 +155,8 @@ type Config struct {
|
||||
|
||||
// Logger is a custom logger to use with the p2p.Server.
|
||||
Logger log.Logger `toml:",omitempty"`
|
||||
|
||||
clock mclock.Clock
|
||||
}
|
||||
|
||||
// Server manages all peer connections.
|
||||
@ -183,13 +184,10 @@ type Server struct {
|
||||
ntab *discover.UDPv4
|
||||
DiscV5 *discv5.Network
|
||||
discmix *enode.FairMix
|
||||
|
||||
staticNodeResolver nodeResolver
|
||||
dialsched *dialScheduler
|
||||
|
||||
// Channels into the run loop.
|
||||
quit chan struct{}
|
||||
addstatic chan *enode.Node
|
||||
removestatic chan *enode.Node
|
||||
addtrusted chan *enode.Node
|
||||
removetrusted chan *enode.Node
|
||||
peerOp chan peerOpFunc
|
||||
@ -302,47 +300,57 @@ func (srv *Server) LocalNode() *enode.LocalNode {
|
||||
// Peers returns all connected peers.
|
||||
func (srv *Server) Peers() []*Peer {
|
||||
var ps []*Peer
|
||||
select {
|
||||
// Note: We'd love to put this function into a variable but
|
||||
// that seems to cause a weird compiler error in some
|
||||
// environments.
|
||||
case srv.peerOp <- func(peers map[enode.ID]*Peer) {
|
||||
srv.doPeerOp(func(peers map[enode.ID]*Peer) {
|
||||
for _, p := range peers {
|
||||
ps = append(ps, p)
|
||||
}
|
||||
}:
|
||||
<-srv.peerOpDone
|
||||
case <-srv.quit:
|
||||
}
|
||||
})
|
||||
return ps
|
||||
}
|
||||
|
||||
// PeerCount returns the number of connected peers.
|
||||
func (srv *Server) PeerCount() int {
|
||||
var count int
|
||||
select {
|
||||
case srv.peerOp <- func(ps map[enode.ID]*Peer) { count = len(ps) }:
|
||||
<-srv.peerOpDone
|
||||
case <-srv.quit:
|
||||
}
|
||||
srv.doPeerOp(func(ps map[enode.ID]*Peer) {
|
||||
count = len(ps)
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
// AddPeer connects to the given node and maintains the connection until the
|
||||
// server is shut down. If the connection fails for any reason, the server will
|
||||
// attempt to reconnect the peer.
|
||||
// AddPeer adds the given node to the static node set. When there is room in the peer set,
|
||||
// the server will connect to the node. If the connection fails for any reason, the server
|
||||
// will attempt to reconnect the peer.
|
||||
func (srv *Server) AddPeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.addstatic <- node:
|
||||
case <-srv.quit:
|
||||
}
|
||||
srv.dialsched.addStatic(node)
|
||||
}
|
||||
|
||||
// RemovePeer disconnects from the given node
|
||||
// RemovePeer removes a node from the static node set. It also disconnects from the given
|
||||
// node if it is currently connected as a peer.
|
||||
//
|
||||
// This method blocks until all protocols have exited and the peer is removed. Do not use
|
||||
// RemovePeer in protocol implementations, call Disconnect on the Peer instead.
|
||||
func (srv *Server) RemovePeer(node *enode.Node) {
|
||||
select {
|
||||
case srv.removestatic <- node:
|
||||
case <-srv.quit:
|
||||
var (
|
||||
ch chan *PeerEvent
|
||||
sub event.Subscription
|
||||
)
|
||||
// Disconnect the peer on the main loop.
|
||||
srv.doPeerOp(func(peers map[enode.ID]*Peer) {
|
||||
srv.dialsched.removeStatic(node)
|
||||
if peer := peers[node.ID()]; peer != nil {
|
||||
ch = make(chan *PeerEvent, 1)
|
||||
sub = srv.peerFeed.Subscribe(ch)
|
||||
peer.Disconnect(DiscRequested)
|
||||
}
|
||||
})
|
||||
// Wait for the peer connection to end.
|
||||
if ch != nil {
|
||||
defer sub.Unsubscribe()
|
||||
for ev := range ch {
|
||||
if ev.Peer == node.ID() && ev.Type == PeerEventTypeDrop {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,6 +445,9 @@ func (srv *Server) Start() (err error) {
|
||||
if srv.log == nil {
|
||||
srv.log = log.Root()
|
||||
}
|
||||
if srv.clock == nil {
|
||||
srv.clock = mclock.System{}
|
||||
}
|
||||
if srv.NoDial && srv.ListenAddr == "" {
|
||||
srv.log.Warn("P2P server will be useless, neither dialing nor listening")
|
||||
}
|
||||
@ -451,15 +462,10 @@ func (srv *Server) Start() (err error) {
|
||||
if srv.listenFunc == nil {
|
||||
srv.listenFunc = net.Listen
|
||||
}
|
||||
if srv.Dialer == nil {
|
||||
srv.Dialer = TCPDialer{&net.Dialer{Timeout: defaultDialTimeout}}
|
||||
}
|
||||
srv.quit = make(chan struct{})
|
||||
srv.delpeer = make(chan peerDrop)
|
||||
srv.checkpointPostHandshake = make(chan *conn)
|
||||
srv.checkpointAddPeer = make(chan *conn)
|
||||
srv.addstatic = make(chan *enode.Node)
|
||||
srv.removestatic = make(chan *enode.Node)
|
||||
srv.addtrusted = make(chan *enode.Node)
|
||||
srv.removetrusted = make(chan *enode.Node)
|
||||
srv.peerOp = make(chan peerOpFunc)
|
||||
@ -476,11 +482,10 @@ func (srv *Server) Start() (err error) {
|
||||
if err := srv.setupDiscovery(); err != nil {
|
||||
return err
|
||||
}
|
||||
srv.setupDialScheduler()
|
||||
|
||||
dynPeers := srv.maxDialedConns()
|
||||
dialer := newDialState(srv.localnode.ID(), dynPeers, &srv.Config)
|
||||
srv.loopWG.Add(1)
|
||||
go srv.run(dialer)
|
||||
go srv.run()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -583,7 +588,6 @@ func (srv *Server) setupDiscovery() error {
|
||||
}
|
||||
srv.ntab = ntab
|
||||
srv.discmix.AddSource(ntab.RandomNodes())
|
||||
srv.staticNodeResolver = ntab
|
||||
}
|
||||
|
||||
// Discovery V5
|
||||
@ -606,6 +610,47 @@ func (srv *Server) setupDiscovery() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) setupDialScheduler() {
|
||||
config := dialConfig{
|
||||
self: srv.localnode.ID(),
|
||||
maxDialPeers: srv.maxDialedConns(),
|
||||
maxActiveDials: srv.MaxPendingPeers,
|
||||
log: srv.Logger,
|
||||
netRestrict: srv.NetRestrict,
|
||||
dialer: srv.Dialer,
|
||||
clock: srv.clock,
|
||||
}
|
||||
if srv.ntab != nil {
|
||||
config.resolver = srv.ntab
|
||||
}
|
||||
if config.dialer == nil {
|
||||
config.dialer = tcpDialer{&net.Dialer{Timeout: defaultDialTimeout}}
|
||||
}
|
||||
srv.dialsched = newDialScheduler(config, srv.discmix, srv.SetupConn)
|
||||
for _, n := range srv.StaticNodes {
|
||||
srv.dialsched.addStatic(n)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) maxInboundConns() int {
|
||||
return srv.MaxPeers - srv.maxDialedConns()
|
||||
}
|
||||
|
||||
func (srv *Server) maxDialedConns() (limit int) {
|
||||
if srv.NoDial || srv.MaxPeers == 0 {
|
||||
return 0
|
||||
}
|
||||
if srv.DialRatio == 0 {
|
||||
limit = srv.MaxPeers / defaultDialRatio
|
||||
} else {
|
||||
limit = srv.MaxPeers / srv.DialRatio
|
||||
}
|
||||
if limit == 0 {
|
||||
limit = 1
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
func (srv *Server) setupListening() error {
|
||||
// Launch the listener.
|
||||
listener, err := srv.listenFunc("tcp", srv.ListenAddr)
|
||||
@ -632,112 +677,55 @@ func (srv *Server) setupListening() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type dialer interface {
|
||||
newTasks(running int, peers map[enode.ID]*Peer, now time.Time) []task
|
||||
taskDone(task, time.Time)
|
||||
addStatic(*enode.Node)
|
||||
removeStatic(*enode.Node)
|
||||
// doPeerOp runs fn on the main loop.
|
||||
func (srv *Server) doPeerOp(fn peerOpFunc) {
|
||||
select {
|
||||
case srv.peerOp <- fn:
|
||||
<-srv.peerOpDone
|
||||
case <-srv.quit:
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) run(dialstate dialer) {
|
||||
// run is the main loop of the server.
|
||||
func (srv *Server) run() {
|
||||
srv.log.Info("Started P2P networking", "self", srv.localnode.Node().URLv4())
|
||||
defer srv.loopWG.Done()
|
||||
defer srv.nodedb.Close()
|
||||
defer srv.discmix.Close()
|
||||
defer srv.dialsched.stop()
|
||||
|
||||
var (
|
||||
peers = make(map[enode.ID]*Peer)
|
||||
inboundCount = 0
|
||||
trusted = make(map[enode.ID]bool, len(srv.TrustedNodes))
|
||||
taskdone = make(chan task, maxActiveDialTasks)
|
||||
tick = time.NewTicker(30 * time.Second)
|
||||
runningTasks []task
|
||||
queuedTasks []task // tasks that can't run yet
|
||||
)
|
||||
defer tick.Stop()
|
||||
|
||||
// Put trusted nodes into a map to speed up checks.
|
||||
// Trusted peers are loaded on startup or added via AddTrustedPeer RPC.
|
||||
for _, n := range srv.TrustedNodes {
|
||||
trusted[n.ID()] = true
|
||||
}
|
||||
|
||||
// removes t from runningTasks
|
||||
delTask := func(t task) {
|
||||
for i := range runningTasks {
|
||||
if runningTasks[i] == t {
|
||||
runningTasks = append(runningTasks[:i], runningTasks[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// starts until max number of active tasks is satisfied
|
||||
startTasks := func(ts []task) (rest []task) {
|
||||
i := 0
|
||||
for ; len(runningTasks) < maxActiveDialTasks && i < len(ts); i++ {
|
||||
t := ts[i]
|
||||
srv.log.Trace("New dial task", "task", t)
|
||||
go func() { t.Do(srv); taskdone <- t }()
|
||||
runningTasks = append(runningTasks, t)
|
||||
}
|
||||
return ts[i:]
|
||||
}
|
||||
scheduleTasks := func() {
|
||||
// Start from queue first.
|
||||
queuedTasks = append(queuedTasks[:0], startTasks(queuedTasks)...)
|
||||
// Query dialer for new tasks and start as many as possible now.
|
||||
if len(runningTasks) < maxActiveDialTasks {
|
||||
nt := dialstate.newTasks(len(runningTasks)+len(queuedTasks), peers, time.Now())
|
||||
queuedTasks = append(queuedTasks, startTasks(nt)...)
|
||||
}
|
||||
}
|
||||
|
||||
running:
|
||||
for {
|
||||
scheduleTasks()
|
||||
|
||||
select {
|
||||
case <-tick.C:
|
||||
// This is just here to ensure the dial scheduler runs occasionally.
|
||||
|
||||
case <-srv.quit:
|
||||
// The server was stopped. Run the cleanup logic.
|
||||
break running
|
||||
|
||||
case n := <-srv.addstatic:
|
||||
// This channel is used by AddPeer to add to the
|
||||
// ephemeral static peer list. Add it to the dialer,
|
||||
// it will keep the node connected.
|
||||
srv.log.Trace("Adding static node", "node", n)
|
||||
dialstate.addStatic(n)
|
||||
|
||||
case n := <-srv.removestatic:
|
||||
// This channel is used by RemovePeer to send a
|
||||
// disconnect request to a peer and begin the
|
||||
// stop keeping the node connected.
|
||||
srv.log.Trace("Removing static node", "node", n)
|
||||
dialstate.removeStatic(n)
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.Disconnect(DiscRequested)
|
||||
}
|
||||
|
||||
case n := <-srv.addtrusted:
|
||||
// This channel is used by AddTrustedPeer to add an enode
|
||||
// This channel is used by AddTrustedPeer to add a node
|
||||
// to the trusted node set.
|
||||
srv.log.Trace("Adding trusted node", "node", n)
|
||||
trusted[n.ID()] = true
|
||||
// Mark any already-connected peer as trusted
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.rw.set(trustedConn, true)
|
||||
}
|
||||
|
||||
case n := <-srv.removetrusted:
|
||||
// This channel is used by RemoveTrustedPeer to remove an enode
|
||||
// This channel is used by RemoveTrustedPeer to remove a node
|
||||
// from the trusted node set.
|
||||
srv.log.Trace("Removing trusted node", "node", n)
|
||||
delete(trusted, n.ID())
|
||||
|
||||
// Unmark any already-connected peer as trusted
|
||||
if p, ok := peers[n.ID()]; ok {
|
||||
p.rw.set(trustedConn, false)
|
||||
}
|
||||
@ -747,14 +735,6 @@ running:
|
||||
op(peers)
|
||||
srv.peerOpDone <- struct{}{}
|
||||
|
||||
case t := <-taskdone:
|
||||
// A task got done. Tell dialstate about it so it
|
||||
// can update its state and remove it from the active
|
||||
// tasks list.
|
||||
srv.log.Trace("Dial task done", "task", t)
|
||||
dialstate.taskDone(t, time.Now())
|
||||
delTask(t)
|
||||
|
||||
case c := <-srv.checkpointPostHandshake:
|
||||
// A connection has passed the encryption handshake so
|
||||
// the remote identity is known (but hasn't been verified yet).
|
||||
@ -771,33 +751,25 @@ running:
|
||||
err := srv.addPeerChecks(peers, inboundCount, c)
|
||||
if err == nil {
|
||||
// The handshakes are done and it passed all checks.
|
||||
p := newPeer(srv.log, c, srv.Protocols)
|
||||
// If message events are enabled, pass the peerFeed
|
||||
// to the peer
|
||||
if srv.EnableMsgEvents {
|
||||
p.events = &srv.peerFeed
|
||||
}
|
||||
name := truncateName(c.name)
|
||||
p.log.Debug("Adding p2p peer", "addr", p.RemoteAddr(), "peers", len(peers)+1, "name", name)
|
||||
go srv.runPeer(p)
|
||||
p := srv.launchPeer(c)
|
||||
peers[c.node.ID()] = p
|
||||
if p.Inbound() {
|
||||
inboundCount++
|
||||
}
|
||||
srv.log.Debug("Adding p2p peer", "peercount", len(peers), "id", p.ID(), "conn", c.flags, "addr", p.RemoteAddr(), "name", truncateName(c.name))
|
||||
srv.dialsched.peerAdded(c)
|
||||
if conn, ok := c.fd.(*meteredConn); ok {
|
||||
conn.handshakeDone(p)
|
||||
}
|
||||
if p.Inbound() {
|
||||
inboundCount++
|
||||
}
|
||||
}
|
||||
// The dialer logic relies on the assumption that
|
||||
// dial tasks complete after the peer has been added or
|
||||
// discarded. Unblock the task last.
|
||||
c.cont <- err
|
||||
|
||||
case pd := <-srv.delpeer:
|
||||
// A peer disconnected.
|
||||
d := common.PrettyDuration(mclock.Now() - pd.created)
|
||||
pd.log.Debug("Removing p2p peer", "addr", pd.RemoteAddr(), "peers", len(peers)-1, "duration", d, "req", pd.requested, "err", pd.err)
|
||||
delete(peers, pd.ID())
|
||||
srv.log.Debug("Removing p2p peer", "peercount", len(peers), "id", pd.ID(), "duration", d, "req", pd.requested, "err", pd.err)
|
||||
srv.dialsched.peerRemoved(pd.rw)
|
||||
if pd.Inbound() {
|
||||
inboundCount--
|
||||
}
|
||||
@ -822,14 +794,14 @@ running:
|
||||
// is closed.
|
||||
for len(peers) > 0 {
|
||||
p := <-srv.delpeer
|
||||
p.log.Trace("<-delpeer (spindown)", "remainingTasks", len(runningTasks))
|
||||
p.log.Trace("<-delpeer (spindown)")
|
||||
delete(peers, p.ID())
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) postHandshakeChecks(peers map[enode.ID]*Peer, inboundCount int, c *conn) error {
|
||||
switch {
|
||||
case !c.is(trustedConn|staticDialedConn) && len(peers) >= srv.MaxPeers:
|
||||
case !c.is(trustedConn) && len(peers) >= srv.MaxPeers:
|
||||
return DiscTooManyPeers
|
||||
case !c.is(trustedConn) && c.is(inboundConn) && inboundCount >= srv.maxInboundConns():
|
||||
return DiscTooManyPeers
|
||||
@ -852,21 +824,6 @@ func (srv *Server) addPeerChecks(peers map[enode.ID]*Peer, inboundCount int, c *
|
||||
return srv.postHandshakeChecks(peers, inboundCount, c)
|
||||
}
|
||||
|
||||
func (srv *Server) maxInboundConns() int {
|
||||
return srv.MaxPeers - srv.maxDialedConns()
|
||||
}
|
||||
|
||||
func (srv *Server) maxDialedConns() int {
|
||||
if srv.NoDiscovery || srv.NoDial {
|
||||
return 0
|
||||
}
|
||||
r := srv.DialRatio
|
||||
if r == 0 {
|
||||
r = defaultDialRatio
|
||||
}
|
||||
return srv.MaxPeers / r
|
||||
}
|
||||
|
||||
// listenLoop runs in its own goroutine and accepts
|
||||
// inbound connections.
|
||||
func (srv *Server) listenLoop() {
|
||||
@ -935,18 +892,20 @@ func (srv *Server) listenLoop() {
|
||||
}
|
||||
|
||||
func (srv *Server) checkInboundConn(fd net.Conn, remoteIP net.IP) error {
|
||||
if remoteIP != nil {
|
||||
// Reject connections that do not match NetRestrict.
|
||||
if srv.NetRestrict != nil && !srv.NetRestrict.Contains(remoteIP) {
|
||||
return fmt.Errorf("not whitelisted in NetRestrict")
|
||||
}
|
||||
// Reject Internet peers that try too often.
|
||||
srv.inboundHistory.expire(time.Now())
|
||||
if !netutil.IsLAN(remoteIP) && srv.inboundHistory.contains(remoteIP.String()) {
|
||||
return fmt.Errorf("too many attempts")
|
||||
}
|
||||
srv.inboundHistory.add(remoteIP.String(), time.Now().Add(inboundThrottleTime))
|
||||
if remoteIP == nil {
|
||||
return nil
|
||||
}
|
||||
// Reject connections that do not match NetRestrict.
|
||||
if srv.NetRestrict != nil && !srv.NetRestrict.Contains(remoteIP) {
|
||||
return fmt.Errorf("not whitelisted in NetRestrict")
|
||||
}
|
||||
// Reject Internet peers that try too often.
|
||||
now := srv.clock.Now()
|
||||
srv.inboundHistory.expire(now, nil)
|
||||
if !netutil.IsLAN(remoteIP) && srv.inboundHistory.contains(remoteIP.String()) {
|
||||
return fmt.Errorf("too many attempts")
|
||||
}
|
||||
srv.inboundHistory.add(remoteIP.String(), now.Add(inboundThrottleTime))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -958,7 +917,6 @@ func (srv *Server) SetupConn(fd net.Conn, flags connFlag, dialDest *enode.Node)
|
||||
err := srv.setupConn(c, flags, dialDest)
|
||||
if err != nil {
|
||||
c.close(err)
|
||||
srv.log.Trace("Setting up connection failed", "addr", fd.RemoteAddr(), "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -977,7 +935,9 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
|
||||
if dialDest != nil {
|
||||
dialPubkey = new(ecdsa.PublicKey)
|
||||
if err := dialDest.Load((*enode.Secp256k1)(dialPubkey)); err != nil {
|
||||
return errors.New("dial destination doesn't have a secp256k1 public key")
|
||||
err = errors.New("dial destination doesn't have a secp256k1 public key")
|
||||
srv.log.Trace("Setting up connection failed", "addr", c.fd.RemoteAddr(), "conn", c.flags, "err", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -1006,7 +966,7 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
|
||||
// Run the capability negotiation handshake.
|
||||
phs, err := c.doProtoHandshake(srv.ourHandshake)
|
||||
if err != nil {
|
||||
clog.Trace("Failed proto handshake", "err", err)
|
||||
clog.Trace("Failed p2p handshake", "err", err)
|
||||
return err
|
||||
}
|
||||
if id := c.node.ID(); !bytes.Equal(crypto.Keccak256(phs.ID), id[:]) {
|
||||
@ -1020,9 +980,6 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
|
||||
return err
|
||||
}
|
||||
|
||||
// If the checks completed successfully, the connection has been added as a peer and
|
||||
// runPeer has been launched.
|
||||
clog.Trace("Connection set up", "inbound", dialDest == nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1054,15 +1011,22 @@ func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error {
|
||||
return <-c.cont
|
||||
}
|
||||
|
||||
func (srv *Server) launchPeer(c *conn) *Peer {
|
||||
p := newPeer(srv.log, c, srv.Protocols)
|
||||
if srv.EnableMsgEvents {
|
||||
// If message events are enabled, pass the peerFeed
|
||||
// to the peer.
|
||||
p.events = &srv.peerFeed
|
||||
}
|
||||
go srv.runPeer(p)
|
||||
return p
|
||||
}
|
||||
|
||||
// runPeer runs in its own goroutine for each peer.
|
||||
// it waits until the Peer logic returns and removes
|
||||
// the peer.
|
||||
func (srv *Server) runPeer(p *Peer) {
|
||||
if srv.newPeerHook != nil {
|
||||
srv.newPeerHook(p)
|
||||
}
|
||||
|
||||
// broadcast peer add
|
||||
srv.peerFeed.Send(&PeerEvent{
|
||||
Type: PeerEventTypeAdd,
|
||||
Peer: p.ID(),
|
||||
@ -1070,10 +1034,18 @@ func (srv *Server) runPeer(p *Peer) {
|
||||
LocalAddress: p.LocalAddr().String(),
|
||||
})
|
||||
|
||||
// run the protocol
|
||||
// Run the per-peer main loop.
|
||||
remoteRequested, err := p.run()
|
||||
|
||||
// broadcast peer drop
|
||||
// Announce disconnect on the main loop to update the peer set.
|
||||
// The main loop waits for existing peers to be sent on srv.delpeer
|
||||
// before returning, so this send should not select on srv.quit.
|
||||
srv.delpeer <- peerDrop{p, err, remoteRequested}
|
||||
|
||||
// Broadcast peer drop to external subscribers. This needs to be
|
||||
// after the send to delpeer so subscribers have a consistent view of
|
||||
// the peer set (i.e. Server.Peers() doesn't include the peer when the
|
||||
// event is received.
|
||||
srv.peerFeed.Send(&PeerEvent{
|
||||
Type: PeerEventTypeDrop,
|
||||
Peer: p.ID(),
|
||||
@ -1081,10 +1053,6 @@ func (srv *Server) runPeer(p *Peer) {
|
||||
RemoteAddress: p.RemoteAddr().String(),
|
||||
LocalAddress: p.LocalAddr().String(),
|
||||
})
|
||||
|
||||
// Note: run waits for existing peers to be sent on srv.delpeer
|
||||
// before returning, so this send should not select on srv.quit.
|
||||
srv.delpeer <- peerDrop{p, err, remoteRequested}
|
||||
}
|
||||
|
||||
// NodeInfo represents a short summary of the information known about the host.
|
||||
|
Reference in New Issue
Block a user