From 1e45f1ba97a2f1e2b687866a5bc12fe451c6c9c6 Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Mon, 2 Mar 2026 13:50:31 +0000 Subject: [PATCH] Deflake async peer registration and harden PeerList.All TestReactor_BlockProviders flakes because peer connectivity is established before the reactor has asynchronously processed peer-updates and appended all peers to r.peers. Fix this categorically by: * waiting in reactorTestSuite.AddPeer until the new peer is visible in reactor.peers before returning; * making PeerList.All() return a copy (snapshot) instead of the internal slice; * adding TestPeerListAllReturnsSnapshot to enforce snapshot semantics. This removes timing-sensitive reads of partially-registered peers and prevents callers from observing/mutating shared peer-list backing storage. --- sei-tendermint/internal/statesync/dispatcher.go | 4 +++- .../internal/statesync/dispatcher_test.go | 17 +++++++++++++++++ .../internal/statesync/reactor_test.go | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/sei-tendermint/internal/statesync/dispatcher.go b/sei-tendermint/internal/statesync/dispatcher.go index 1aff1e90dd..22a1b0d0af 100644 --- a/sei-tendermint/internal/statesync/dispatcher.go +++ b/sei-tendermint/internal/statesync/dispatcher.go @@ -283,7 +283,9 @@ func (l *PeerList) Remove(peer types.NodeID) { func (l *PeerList) All() []types.NodeID { l.mtx.Lock() defer l.mtx.Unlock() - return l.peers + peers := make([]types.NodeID, len(l.peers)) + copy(peers, l.peers) + return peers } func (l *PeerList) Contains(id types.NodeID) bool { diff --git a/sei-tendermint/internal/statesync/dispatcher_test.go b/sei-tendermint/internal/statesync/dispatcher_test.go index 7a19008e01..da7dd55c38 100644 --- a/sei-tendermint/internal/statesync/dispatcher_test.go +++ b/sei-tendermint/internal/statesync/dispatcher_test.go @@ -235,6 +235,23 @@ func TestPeerListBasic(t *testing.T) { assert.Equal(t, half, peerList.Len()) } +func TestPeerListAllReturnsSnapshot(t *testing.T) { + peerList := NewPeerList() + peerSet := createPeerSet(2) + for _, peer := range peerSet { + peerList.Append(peer) + } + + peers := peerList.All() + require.Len(t, peers, 2) + + // Mutating the returned list must not mutate the internal peer list. + peers[0] = peerSet[1] + peers = peerList.All() + require.Equal(t, peerSet[0], peers[0]) + require.Equal(t, peerSet[1], peers[1]) +} + func TestPeerListBlocksWhenEmpty(t *testing.T) { t.Cleanup(leaktest.Check(t)) peerList := NewPeerList() diff --git a/sei-tendermint/internal/statesync/reactor_test.go b/sei-tendermint/internal/statesync/reactor_test.go index afad5d95e1..1cbc2eb1f1 100644 --- a/sei-tendermint/internal/statesync/reactor_test.go +++ b/sei-tendermint/internal/statesync/reactor_test.go @@ -141,6 +141,11 @@ func (rts *reactorTestSuite) AddPeer(t *testing.T) *Node { paramsCh: orPanic(p2p.OpenChannel(testNode.Router, GetParamsChannelDescriptor())), } rts.node.Connect(t.Context(), testNode) + // Peer registration in the reactor is asynchronous, so block until this peer + // is visible before returning to callers that may assert on peer counts. + require.Eventually(t, func() bool { + return rts.reactor.peers.Contains(testNode.NodeID) + }, 5*time.Second, 50*time.Millisecond) return n }