Skip to content

Commit 4aab775

Browse files
committed
refactor(p2p,eth): remove per-peer dual-connection mechanism, fix #2351 #2359
XDPoSChain kept two independent RLPx/TCP connections per remote peer (a "primary" and a "pair"), a private divergence from upstream go-ethereum which allows only one connection per NodeID. This adds state-machine complexity (isPair, pairRw, PairPeer, ErrAddPairPeer, DiscPairPeerStop) without measured benefit, and the pair connection already carried both block-sync and BFT traffic, so it never isolated Vote/Timeout/SyncInfo from large BlockBodies/NodeData frames. Revert to a single connection per NodeID (upstream semantics) and route all eth messages, including the XDPoS v2 BFT messages, over that single connection using the existing upstream FIFO write scheduling. No write-priority lane is added; RLPx framing, message ids, capability strings and the handshake are unchanged, so the change is rolling-upgrade safe against legacy pair-capable nodes. Changes: - p2p/server.go: postHandshakeChecks rejects any duplicate NodeID with DiscAlreadyConnected; addPeer no longer stores a second connection. - p2p/peer.go: drop pairPeer field/methods and the DiscPairPeerStop cascade on Peer.run exit. - p2p/peer_error.go: remove ErrAddPairPeer; keep the DiscPairPeerStop enum slot (deprecated) to preserve on-the-wire numbering. - eth/peer.go: remove pairRw; all Send/Request helpers use p.rw; peerSet.Register reverts to one peer per id. - eth/handler.go: drop ErrAddPairPeer special-casing so every peer registers with the downloader and syncs transactions. Refs: #2359
1 parent e98320f commit 4aab775

8 files changed

Lines changed: 95 additions & 183 deletions

File tree

eth/handler.go

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -351,41 +351,38 @@ func (pm *ProtocolManager) handle(p *peer) error {
351351
rw.Init(p.version)
352352
}
353353
// Register the peer locally
354-
err := pm.peers.Register(p)
355-
if err != nil && err != p2p.ErrAddPairPeer {
354+
if err := pm.peers.Register(p); err != nil {
356355
p.Log().Error("Ethereum peer registration failed", "err", err)
357356
return err
358357
}
359358
defer pm.removePeer(p.id)
360-
if err != p2p.ErrAddPairPeer {
361-
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
362-
if err := pm.downloader.RegisterPeer(p.id, p.version, p); err != nil {
359+
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
360+
if err := pm.downloader.RegisterPeer(p.id, p.version, p); err != nil {
361+
return err
362+
}
363+
p.Log().Info("Register peer", "nodeid", p.ID().String(), "version", p.version, "addr", p.RemoteAddr())
364+
// Propagate existing transactions. new transactions appearing
365+
// after this will be sent via broadcasts.
366+
pm.syncTransactions(p)
367+
368+
// If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
369+
if daoBlock := pm.blockchain.Config().DAOForkBlock; daoBlock != nil {
370+
// Request the peer's DAO fork header for extra-data validation
371+
if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil {
363372
return err
364373
}
365-
p.Log().Info("Register peer", "nodeid", p.ID().String(), "version", p.version, "addr", p.RemoteAddr())
366-
// Propagate existing transactions. new transactions appearing
367-
// after this will be sent via broadcasts.
368-
pm.syncTransactions(p)
369-
370-
// If we're DAO hard-fork aware, validate any remote peer with regard to the hard-fork
371-
if daoBlock := pm.blockchain.Config().DAOForkBlock; daoBlock != nil {
372-
// Request the peer's DAO fork header for extra-data validation
373-
if err := p.RequestHeadersByNumber(daoBlock.Uint64(), 1, 0, false); err != nil {
374-
return err
374+
// Start a timer to disconnect if the peer doesn't reply in time
375+
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {
376+
p.Log().Debug("Timed out DAO fork-check, dropping")
377+
pm.removePeer(p.id)
378+
})
379+
// Make sure it's cleaned up if the peer dies off
380+
defer func() {
381+
if p.forkDrop != nil {
382+
p.forkDrop.Stop()
383+
p.forkDrop = nil
375384
}
376-
// Start a timer to disconnect if the peer doesn't reply in time
377-
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {
378-
p.Log().Debug("Timed out DAO fork-check, dropping")
379-
pm.removePeer(p.id)
380-
})
381-
// Make sure it's cleaned up if the peer dies off
382-
defer func() {
383-
if p.forkDrop != nil {
384-
p.forkDrop.Stop()
385-
p.forkDrop = nil
386-
}
387-
}()
388-
}
385+
}()
389386
}
390387
// main loop. handle incoming messages.
391388
for {

eth/peer.go

Lines changed: 18 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ type peer struct {
5959
id string
6060

6161
*p2p.Peer
62-
rw p2p.MsgReadWriter
63-
pairRw p2p.MsgReadWriter
62+
rw p2p.MsgReadWriter
6463

6564
version int // Protocol version negotiated
6665
forkDrop *time.Timer // Timed connection dropper if forks aren't validated in time
@@ -262,59 +261,35 @@ func (p *peer) SendNewBlock(block *types.Block, td *big.Int) error {
262261
}
263262

264263
p.knownBlocks.Add(block.Hash())
265-
if p.pairRw != nil {
266-
return p2p.Send(p.pairRw, NewBlockMsg, []interface{}{block, td})
267-
} else {
268-
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td})
269-
}
264+
return p2p.Send(p.rw, NewBlockMsg, []interface{}{block, td})
270265
}
271266

272267
// SendBlockHeaders sends a batch of block headers to the remote peer.
273268
func (p *peer) SendBlockHeaders(headers []*types.Header) error {
274-
if p.pairRw != nil {
275-
return p2p.Send(p.pairRw, BlockHeadersMsg, headers)
276-
} else {
277-
return p2p.Send(p.rw, BlockHeadersMsg, headers)
278-
}
269+
return p2p.Send(p.rw, BlockHeadersMsg, headers)
279270
}
280271

281272
// SendBlockBodies sends a batch of block contents to the remote peer.
282273
func (p *peer) SendBlockBodies(bodies []*blockBody) error {
283-
if p.pairRw != nil {
284-
return p2p.Send(p.pairRw, BlockBodiesMsg, blockBodiesData(bodies))
285-
} else {
286-
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies))
287-
}
274+
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesData(bodies))
288275
}
289276

290277
// SendBlockBodiesRLP sends a batch of block contents to the remote peer from
291278
// an already RLP encoded format.
292279
func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
293-
if p.pairRw != nil {
294-
return p2p.Send(p.pairRw, BlockBodiesMsg, bodies)
295-
} else {
296-
return p2p.Send(p.rw, BlockBodiesMsg, bodies)
297-
}
280+
return p2p.Send(p.rw, BlockBodiesMsg, bodies)
298281
}
299282

300283
// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
301284
// hashes requested.
302285
func (p *peer) SendNodeData(data [][]byte) error {
303-
if p.pairRw != nil {
304-
return p2p.Send(p.pairRw, NodeDataMsg, data)
305-
} else {
306-
return p2p.Send(p.rw, NodeDataMsg, data)
307-
}
286+
return p2p.Send(p.rw, NodeDataMsg, data)
308287
}
309288

310289
// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the
311290
// ones requested from an already RLP encoded format.
312291
func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
313-
if p.pairRw != nil {
314-
return p2p.Send(p.pairRw, ReceiptsMsg, receipts)
315-
} else {
316-
return p2p.Send(p.rw, ReceiptsMsg, receipts)
317-
}
292+
return p2p.Send(p.rw, ReceiptsMsg, receipts)
318293
}
319294

320295
func (p *peer) SendVote(vote *types.Vote) error {
@@ -323,11 +298,7 @@ func (p *peer) SendVote(vote *types.Vote) error {
323298
}
324299

325300
p.knownVote.Add(vote.Hash())
326-
if p.pairRw != nil {
327-
return p2p.Send(p.pairRw, VoteMsg, vote)
328-
} else {
329-
return p2p.Send(p.rw, VoteMsg, vote)
330-
}
301+
return p2p.Send(p.rw, VoteMsg, vote)
331302
}
332303

333304
/*
@@ -341,11 +312,7 @@ func (p *peer) SendTimeout(timeout *types.Timeout) error {
341312
}
342313

343314
p.knownTimeout.Add(timeout.Hash())
344-
if p.pairRw != nil {
345-
return p2p.Send(p.pairRw, TimeoutMsg, timeout)
346-
} else {
347-
return p2p.Send(p.rw, TimeoutMsg, timeout)
348-
}
315+
return p2p.Send(p.rw, TimeoutMsg, timeout)
349316
}
350317

351318
/*
@@ -359,11 +326,7 @@ func (p *peer) SendSyncInfo(syncInfo *types.SyncInfo) error {
359326
}
360327

361328
p.knownSyncInfo.Add(syncInfo.Hash())
362-
if p.pairRw != nil {
363-
return p2p.Send(p.pairRw, SyncInfoMsg, syncInfo)
364-
} else {
365-
return p2p.Send(p.rw, SyncInfoMsg, syncInfo)
366-
}
329+
return p2p.Send(p.rw, SyncInfoMsg, syncInfo)
367330
}
368331

369332
/*
@@ -376,65 +339,41 @@ func (p *peer) AsyncSendSyncInfo() {
376339
// single header. It is used solely by the fetcher.
377340
func (p *peer) RequestOneHeader(hash common.Hash) error {
378341
p.Log().Debug("Fetching single header", "hash", hash)
379-
if p.pairRw != nil {
380-
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false})
381-
} else {
382-
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false})
383-
}
342+
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: hash}, Amount: uint64(1), Skip: uint64(0), Reverse: false})
384343
}
385344

386345
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
387346
// specified header query, based on the hash of an origin block.
388347
func (p *peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
389348
p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse)
390-
if p.pairRw != nil {
391-
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
392-
} else {
393-
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
394-
}
349+
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
395350
}
396351

397352
// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the
398353
// specified header query, based on the number of an origin block.
399354
func (p *peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
400355
p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse)
401-
if p.pairRw != nil {
402-
return p2p.Send(p.pairRw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
403-
} else {
404-
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
405-
}
356+
return p2p.Send(p.rw, GetBlockHeadersMsg, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse})
406357
}
407358

408359
// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes
409360
// specified.
410361
func (p *peer) RequestBodies(hashes []common.Hash) error {
411362
p.Log().Debug("Fetching batch of block bodies", "count", len(hashes))
412-
if p.pairRw != nil {
413-
return p2p.Send(p.pairRw, GetBlockBodiesMsg, hashes)
414-
} else {
415-
return p2p.Send(p.rw, GetBlockBodiesMsg, hashes)
416-
}
363+
return p2p.Send(p.rw, GetBlockBodiesMsg, hashes)
417364
}
418365

419366
// RequestNodeData fetches a batch of arbitrary data from a node's known state
420367
// data, corresponding to the specified hashes.
421368
func (p *peer) RequestNodeData(hashes []common.Hash) error {
422369
p.Log().Debug("Fetching batch of state data", "count", len(hashes))
423-
if p.pairRw != nil {
424-
return p2p.Send(p.pairRw, GetNodeDataMsg, hashes)
425-
} else {
426-
return p2p.Send(p.rw, GetNodeDataMsg, hashes)
427-
}
370+
return p2p.Send(p.rw, GetNodeDataMsg, hashes)
428371
}
429372

430373
// RequestReceipts fetches a batch of transaction receipts from a remote node.
431374
func (p *peer) RequestReceipts(hashes []common.Hash) error {
432375
p.Log().Debug("Fetching batch of receipts", "count", len(hashes))
433-
if p.pairRw != nil {
434-
return p2p.Send(p.pairRw, GetReceiptsMsg, hashes)
435-
} else {
436-
return p2p.Send(p.rw, GetReceiptsMsg, hashes)
437-
}
376+
return p2p.Send(p.rw, GetReceiptsMsg, hashes)
438377
}
439378

440379
// Handshake executes the eth protocol handshake, negotiating version number,
@@ -530,14 +469,8 @@ func (ps *peerSet) Register(p *peer) error {
530469
if ps.closed {
531470
return errClosed
532471
}
533-
if existPeer, ok := ps.peers[p.id]; ok {
534-
if existPeer.pairRw != nil {
535-
return errAlreadyRegistered
536-
}
537-
existPeer.SetPairPeer(p.Peer)
538-
existPeer.pairRw = p.rw
539-
p.SetPairPeer(existPeer.Peer)
540-
return p2p.ErrAddPairPeer
472+
if _, ok := ps.peers[p.id]; ok {
473+
return errAlreadyRegistered
541474
}
542475
ps.peers[p.id] = p
543476
return nil

eth/peer_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package eth
2+
3+
import "testing"
4+
5+
func TestPeerSetRegisterRejectsDuplicateID(t *testing.T) {
6+
peers := newPeerSet()
7+
first := &peer{id: "dup"}
8+
second := &peer{id: "dup"}
9+
10+
if err := peers.Register(first); err != nil {
11+
t.Fatalf("first register failed: %v", err)
12+
}
13+
if err := peers.Register(second); err != errAlreadyRegistered {
14+
t.Fatalf("second register error mismatch: got %v want %v", err, errAlreadyRegistered)
15+
}
16+
if peers.Len() != 1 {
17+
t.Fatalf("peer set size mismatch: got %d want 1", peers.Len())
18+
}
19+
if got := peers.Peer("dup"); got != first {
20+
t.Fatalf("registered peer replaced: got %p want %p", got, first)
21+
}
22+
}

p2p/peer.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,6 @@ type Peer struct {
117117

118118
// events receives message send / receive events if set
119119
events *event.Feed
120-
121-
pairPeerMu sync.RWMutex
122-
pairPeer *Peer
123120
}
124121

125122
// NewPeer returns a peer for testing purposes.
@@ -202,30 +199,6 @@ func (p *Peer) Log() log.Logger {
202199
return p.log
203200
}
204201

205-
func (p *Peer) PairPeer() *Peer {
206-
p.pairPeerMu.RLock()
207-
defer p.pairPeerMu.RUnlock()
208-
209-
return p.pairPeer
210-
}
211-
212-
func (p *Peer) SetPairPeer(pair *Peer) {
213-
p.pairPeerMu.Lock()
214-
p.pairPeer = pair
215-
p.pairPeerMu.Unlock()
216-
}
217-
218-
func (p *Peer) ClearPairPeer(pair *Peer) bool {
219-
p.pairPeerMu.Lock()
220-
defer p.pairPeerMu.Unlock()
221-
222-
if p.pairPeer != pair {
223-
return false
224-
}
225-
p.pairPeer = nil
226-
return true
227-
}
228-
229202
func (p *Peer) run() (remoteRequested bool, err error) {
230203
var (
231204
writeStart = make(chan struct{}, 1)
@@ -271,9 +244,6 @@ loop:
271244
close(p.closed)
272245
p.rw.close(reason)
273246
p.wg.Wait()
274-
if pairPeer := p.PairPeer(); pairPeer != nil {
275-
go pairPeer.Disconnect(DiscPairPeerStop)
276-
}
277247
return remoteRequested, err
278248
}
279249

p2p/peer_error.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ func (pe *peerError) Error() string {
5454

5555
var errProtocolReturned = errors.New("protocol returned")
5656

57-
var ErrAddPairPeer = errors.New("add a pair peer")
58-
5957
type DiscReason uint
6058

6159
const (
@@ -71,6 +69,9 @@ const (
7169
DiscUnexpectedIdentity
7270
DiscSelf
7371
DiscReadTimeout
72+
// Deprecated: the per-peer pair connection mechanism was removed (issue #2359).
73+
// The enum slot is kept to preserve on-the-wire numbering of the values below
74+
// for rolling-upgrade compatibility. Do not reuse.
7475
DiscPairPeerStop
7576
DiscNonAllowlistedPeer
7677
DiscDenylistedPeer

p2p/peer_test.go

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -239,34 +239,6 @@ func TestPeerDisconnectRace(t *testing.T) {
239239
}
240240
}
241241

242-
func TestPeerRunDisconnectsPairPeer(t *testing.T) {
243-
closer, _, peer, errc := testPeer(nil)
244-
defer closer()
245-
246-
pairPeer := &Peer{
247-
disc: make(chan DiscReason, 1),
248-
closed: make(chan struct{}),
249-
}
250-
peer.SetPairPeer(pairPeer)
251-
252-
closer()
253-
254-
select {
255-
case <-errc:
256-
case <-time.After(2 * time.Second):
257-
t.Fatal("peer did not stop")
258-
}
259-
260-
select {
261-
case reason := <-pairPeer.disc:
262-
if reason != DiscPairPeerStop {
263-
t.Fatalf("unexpected pair disconnect reason: got %v want %v", reason, DiscPairPeerStop)
264-
}
265-
case <-time.After(2 * time.Second):
266-
t.Fatal("pair peer was not disconnected")
267-
}
268-
}
269-
270242
func TestNewPeer(t *testing.T) {
271243
name := "nodename"
272244
caps := []Cap{{"foo", 2}, {"bar", 3}}

0 commit comments

Comments
 (0)