Skip to content

Commit 15bc254

Browse files
Jolly23unclezoro
andauthored
fix logic issue: handlers.removePeer() is called twice. (bnb-chain#856)
* fix logic issue: handlers.removePeer() is called twice. There is a logic issue which cause "Ethereum peer removal failed, err=peer not registered" occur quite often. handler.runEthPeer set up a defer removePeer(). This is always called after a peer is disconnected. However removePeer is also called by mulitple functions like downloader/fetcher. After those kind of functions removePeer(), peer handler executes defer removePeer(). This makes removePeer() happened twice, and this is the reason we often see "Ethereum peer removal failed, err=peer not registered". To solve this, removePeer only needs to hard Disconnect peer from networking layer. Then defer unregisterPeer() will do the cleanup task after then. * fix: modify test function for close testing. reference from go-thereum. Co-authored-by: zjubfd <[email protected]>
1 parent 1aeadc1 commit 15bc254

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

eth/handler.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
313313
peer.Log().Error("Ethereum peer registration failed", "err", err)
314314
return err
315315
}
316-
defer h.removePeer(peer.ID())
316+
defer h.unregisterPeer(peer.ID())
317317

318318
p := h.peers.peer(peer.ID())
319319
if p == nil {
@@ -395,9 +395,17 @@ func (h *handler) runDiffExtension(peer *diff.Peer, handler diff.Handler) error
395395
return handler(peer)
396396
}
397397

398-
// removePeer unregisters a peer from the downloader and fetchers, removes it from
399-
// the set of tracked peers and closes the network connection to it.
398+
// removePeer requests disconnection of a peer.
400399
func (h *handler) removePeer(id string) {
400+
peer := h.peers.peer(id)
401+
if peer != nil {
402+
// Hard disconnect at the networking layer. Handler will get an EOF and terminate the peer. defer unregisterPeer will do the cleanup task after then.
403+
peer.Peer.Disconnect(p2p.DiscUselessPeer)
404+
}
405+
}
406+
407+
// unregisterPeer removes a peer from the downloader, fetchers and main peer set.
408+
func (h *handler) unregisterPeer(id string) {
401409
// Create a custom logger to avoid printing the entire id
402410
var logger log.Logger
403411
if len(id) < 16 {
@@ -425,8 +433,6 @@ func (h *handler) removePeer(id string) {
425433
if err := h.peers.unregisterPeer(id); err != nil {
426434
logger.Error("Ethereum peer removal failed", "err", err)
427435
}
428-
// Hard disconnect at the networking layer
429-
peer.Peer.Disconnect(p2p.DiscUselessPeer)
430436
}
431437

432438
func (h *handler) Start(maxPeers int) {

eth/handler_eth_test.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -641,14 +641,20 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
641641
defer p2pLocal.Close()
642642
defer p2pRemote.Close()
643643

644-
local := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{1}, "", nil), p2pLocal, handler.txpool)
645-
remote := eth.NewPeer(eth.ETH65, p2p.NewPeer(enode.ID{2}, "", nil), p2pRemote, handler.txpool)
644+
local := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
645+
remote := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
646646
defer local.Close()
647647
defer remote.Close()
648648

649-
go handler.handler.runEthPeer(local, func(peer *eth.Peer) error {
650-
return eth.Handle((*ethHandler)(handler.handler), peer)
651-
})
649+
handlerDone := make(chan struct{})
650+
go func() {
651+
defer close(handlerDone)
652+
handler.handler.runEthPeer(local, func(peer *eth.Peer) error {
653+
err := eth.Handle((*ethHandler)(handler.handler), peer)
654+
return err
655+
})
656+
}()
657+
652658
// Run the handshake locally to avoid spinning up a remote handler
653659
var (
654660
genesis = handler.chain.Genesis()
@@ -685,6 +691,7 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
685691

686692
// Verify that the remote peer is maintained or dropped
687693
if drop {
694+
<-handlerDone
688695
if peers := handler.handler.peers.len(); peers != 0 {
689696
t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
690697
}

p2p/peer.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ type Peer struct {
116116
disc chan DiscReason
117117

118118
// events receives message send / receive events if set
119-
events *event.Feed
119+
events *event.Feed
120+
testPipe *MsgPipeRW // for testing
120121
}
121122

122123
// NewPeer returns a peer for testing purposes.
@@ -129,6 +130,15 @@ func NewPeer(id enode.ID, name string, caps []Cap) *Peer {
129130
return peer
130131
}
131132

133+
// NewPeerPipe creates a peer for testing purposes.
134+
// The message pipe given as the last parameter is closed when
135+
// Disconnect is called on the peer.
136+
func NewPeerPipe(id enode.ID, name string, caps []Cap, pipe *MsgPipeRW) *Peer {
137+
p := NewPeer(id, name, caps)
138+
p.testPipe = pipe
139+
return p
140+
}
141+
132142
// NewPeerWithProtocols returns a peer for testing purposes.
133143
func NewPeerWithProtocols(id enode.ID, protocols []Protocol, name string, caps []Cap) *Peer {
134144
pipe, _ := net.Pipe()
@@ -196,6 +206,10 @@ func (p *Peer) LocalAddr() net.Addr {
196206
// Disconnect terminates the peer connection with the given reason.
197207
// It returns immediately and does not wait until the connection is closed.
198208
func (p *Peer) Disconnect(reason DiscReason) {
209+
if p.testPipe != nil {
210+
p.testPipe.Close()
211+
}
212+
199213
select {
200214
case p.disc <- reason:
201215
case <-p.closed:

0 commit comments

Comments
 (0)