This repository was archived by the owner on Jan 16, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 212
test(node): connection drop test #2554
Closed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
5ff854c
test(node): add restart tests
theochap 1114afc
test(node): add connection drop tests
theochap 652817b
fix(test): removing restart tests
theochap 1c2fc01
fix(node/test): fix connection drop test
theochap c615d0f
chore(node/test): unused methods
theochap 146c137
fix(node/test): fix connection drop test
theochap File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,240 @@ | ||||||
| package node | ||||||
|
|
||||||
| import ( | ||||||
| "sync" | ||||||
| "testing" | ||||||
| "time" | ||||||
|
|
||||||
| "github.com/ethereum-optimism/optimism/op-devstack/devtest" | ||||||
| "github.com/ethereum-optimism/optimism/op-devstack/dsl" | ||||||
| "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types" | ||||||
| "golang.org/x/sync/errgroup" | ||||||
| ) | ||||||
|
|
||||||
| // TestConnDrops tests what happens when a node drops his connection to his peers. | ||||||
| // We simulate that by blacklisting all the peers of a node. | ||||||
| func TestConnDrops(gt *testing.T) { | ||||||
| t := devtest.SerialT(gt) | ||||||
|
|
||||||
| out := NewMixedOpKona(t) | ||||||
|
|
||||||
| nodes := out.L2CLNodes() | ||||||
|
|
||||||
| ref := nodes[0] | ||||||
|
|
||||||
| var wg sync.WaitGroup | ||||||
| for _, node := range nodes { | ||||||
| if node == ref { | ||||||
| continue | ||||||
| } | ||||||
|
|
||||||
| t.Log("testing conn drops for node %s", node.Escape().ID().Key()) | ||||||
|
|
||||||
| wg.Add(1) | ||||||
| go func() { | ||||||
| defer wg.Done() | ||||||
|
|
||||||
| // Check that both the safe and unsafe chains are advancing | ||||||
| dsl.CheckAll(t, node.MatchedFn(&ref, types.LocalSafe, 50), node.MatchedFn(&ref, types.LocalUnsafe, 50)) | ||||||
|
|
||||||
| // Blacklist all the peers of the node | ||||||
| peers := node.Peers() | ||||||
| for _, peer := range peers.Peers { | ||||||
| t.Log("blacklisting peer %s", peer.PeerID) | ||||||
| err := node.Escape().P2PAPI().BlockPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to block peer %s", peer.PeerID) | ||||||
| // Disconnect the peer to ensure that the node is not connected to it anymore | ||||||
| err = node.Escape().P2PAPI().DisconnectPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to disconnect peer %s", peer.PeerID) | ||||||
| } | ||||||
|
|
||||||
| check := []dsl.CheckFunc{} | ||||||
|
|
||||||
| // Wait for the safe chain to advance. The node should _only_ be able to sync the L1 chain: only the safe chain should advance. | ||||||
| // The local safe chain may diverge from the reference node, but the unsafe chain should be in sync. | ||||||
| check = append(check, node.AdvancedFn(types.LocalSafe, 20, 50)) | ||||||
|
|
||||||
| // The node should be able to sync the unsafe chain (by consolidating the safe chain) | ||||||
| check = append(check, node.AdvancedFn(types.LocalUnsafe, 20, 50)) | ||||||
|
|
||||||
| dsl.CheckAll(t, check...) | ||||||
|
|
||||||
| if !isSequencer(&node) { | ||||||
| // The unsafe and safe chains should match | ||||||
| syncStatus := node.SyncStatus() | ||||||
| t.Require().Equal(syncStatus.UnsafeL2, syncStatus.SafeL2, "expected unsafe and safe chains to be in sync") | ||||||
| } else { | ||||||
| // The unsafe and safe chains should diverge | ||||||
| syncStatus := node.SyncStatus() | ||||||
| t.Require().NotEqual(syncStatus.UnsafeL2, syncStatus.SafeL2, "expected unsafe and safe chains to diverge") | ||||||
| } | ||||||
|
|
||||||
| // Unblock the peers of the node | ||||||
| for _, peer := range peers.Peers { | ||||||
| t.Log("unblocking peer %s", peer.PeerID) | ||||||
| err := node.Escape().P2PAPI().UnblockPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to unblock peer %s", peer.PeerID) | ||||||
| } | ||||||
|
|
||||||
| // Wait for the safe and unsafe chains to advance. The node should be able to sync both the safe and unsafe chains. | ||||||
| // The chains should be in sync with the reference node! | ||||||
| dsl.CheckAll(t, node.MatchedFn(&ref, types.LocalSafe, 50), node.MatchedFn(&ref, types.LocalUnsafe, 50)) | ||||||
|
|
||||||
| }() | ||||||
| } | ||||||
|
|
||||||
| wg.Wait() | ||||||
| } | ||||||
|
|
||||||
| // TestConnDropsWithSequencer tests what happens when the sequencer node drops his connection to all the other nodes of the network. | ||||||
| // In that case, the sequencer should be able to sync both the safe and unsafe chains. The other nodes should be able to sync the L1 chain but diverge from the sequencer. | ||||||
| func TestConnDropsWithSequencer(gt *testing.T) { | ||||||
| t := devtest.SerialT(gt) | ||||||
|
|
||||||
| out := NewMixedOpKona(t) | ||||||
|
|
||||||
| nodes := out.L2CLNodes() | ||||||
|
|
||||||
| sequencerList := filterSequencer(nodes) | ||||||
|
|
||||||
| // Ensure that there is only one sequencer node (otherwise op-conductor might make matters tricky) | ||||||
| t.Gate().Equal(len(sequencerList), 1, "expected only one sequencer node") | ||||||
|
|
||||||
| sequencer := sequencerList[0] | ||||||
|
|
||||||
| // Blacklist all the peers of the sequencer | ||||||
| peers := sequencer.Peers() | ||||||
| for _, peer := range peers.Peers { | ||||||
| t.Log("blacklisting peer %s", peer.PeerID) | ||||||
| err := sequencer.Escape().P2PAPI().BlockPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to block peer %s", peer.PeerID) | ||||||
| // Disconnect the peer to ensure that the node is not connected to it anymore | ||||||
| err = sequencer.Escape().P2PAPI().DisconnectPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to disconnect peer %s", peer.PeerID) | ||||||
| } | ||||||
|
|
||||||
| // Now: | ||||||
| // - The sequencer should be able to sync the L1 chain | ||||||
| // - The other nodes should be able to sync the L1 chain but diverge from the sequencer | ||||||
| // - The sequencer should be able to sync the safe and unsafe chains | ||||||
| // - The other nodes should be able to sync the safe and unsafe chains | ||||||
|
|
||||||
| toCheck := []dsl.CheckFunc{} | ||||||
| toCheckErr := []dsl.CheckFunc{} | ||||||
|
|
||||||
| toCheck = append(toCheck, sequencer.AdvancedFn(types.LocalSafe, 20, 50), sequencer.AdvancedFn(types.LocalUnsafe, 20, 50)) | ||||||
|
|
||||||
| for _, node := range nodes { | ||||||
| if node == sequencer { | ||||||
| continue | ||||||
| } | ||||||
|
|
||||||
| toCheck = append(toCheck, node.AdvancedFn(types.LocalSafe, 20, 50)) | ||||||
| toCheck = append(toCheck, node.AdvancedFn(types.LocalUnsafe, 20, 50)) | ||||||
|
|
||||||
| // The other nodes should _always_ diverge from the sequencer | ||||||
| toCheckErr = append(toCheckErr, node.MatchedFn(&sequencer, types.LocalUnsafe, 50)) | ||||||
| } | ||||||
|
|
||||||
| dsl.CheckAll(t, toCheck...) | ||||||
| CheckErr(t, toCheckErr...) | ||||||
|
|
||||||
| // Unblock the peers of the sequencer. The network should get back to normal. | ||||||
| for _, peer := range peers.Peers { | ||||||
| t.Log("unblocking peer %s", peer.PeerID) | ||||||
| err := sequencer.Escape().P2PAPI().UnblockPeer(t.Ctx(), peer.PeerID) | ||||||
| t.Require().NoError(err, "failed to unblock peer %s", peer.PeerID) | ||||||
| // Reconnect the peer to ensure that the node is connected to it again | ||||||
| err = sequencer.Escape().P2PAPI().ConnectPeer(t.Ctx(), peer.Addresses[0]) | ||||||
| t.Require().NoError(err, "failed to connect peer %s", peer.PeerID) | ||||||
| } | ||||||
|
|
||||||
| toCheck = []dsl.CheckFunc{} | ||||||
|
|
||||||
| // Wait for the safe and unsafe chains to advance. The sequencer should be able to sync both the safe and unsafe chains. | ||||||
| toCheck = append(toCheck, sequencer.AdvancedFn(types.LocalSafe, 20, 50), sequencer.AdvancedFn(types.LocalUnsafe, 20, 50)) | ||||||
|
|
||||||
| for _, node := range nodes { | ||||||
| if node == sequencer { | ||||||
| continue | ||||||
| } | ||||||
|
|
||||||
| toCheck = append(toCheck, node.MatchedFn(&sequencer, types.LocalSafe, 50)) | ||||||
| toCheck = append(toCheck, node.MatchedFn(&sequencer, types.LocalUnsafe, 50)) | ||||||
| } | ||||||
|
|
||||||
| dsl.CheckAll(t, toCheck...) | ||||||
| } | ||||||
|
|
||||||
| // Like CheckAll, but expects an error. | ||||||
| func CheckErr(t devtest.T, checks ...dsl.CheckFunc) { | ||||||
| var g errgroup.Group | ||||||
| for _, check := range checks { | ||||||
| check := check | ||||||
| g.Go(func() error { | ||||||
| return check() | ||||||
| }) | ||||||
| } | ||||||
| t.Require().Error(g.Wait()) | ||||||
| } | ||||||
|
|
||||||
| // TestConnDropsEngineTaskCount tests that the engine task count is correctly updated when a node drops his connection to his peers. | ||||||
| func TestConnDropsEngineTaskCount(gt *testing.T) { | ||||||
| t := devtest.SerialT(gt) | ||||||
|
|
||||||
| out := NewMixedOpKona(t) | ||||||
|
|
||||||
| nodes := out.L2CLNodes() | ||||||
|
|
||||||
| ref := nodes[0] | ||||||
|
|
||||||
| var wg sync.WaitGroup | ||||||
| for _, node := range nodes { | ||||||
| if node == ref { | ||||||
| continue | ||||||
| } | ||||||
|
|
||||||
| t.Log("testing conn drops for node %s", node.Escape().ID().Key()) | ||||||
|
||||||
| t.Log("testing conn drops for node %s", node.Escape().ID().Key()) | |
| t.Logf("testing conn drops for node %s", node.Escape().ID().Key()) |
Copilot
AI
Jul 25, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message uses printf-style formatting with %s but the NoError method may not support format specifiers in this context. Consider using fmt.Sprintf to format the message properly.
Suggested change
| t.Require().NoError(err, "failed to get RPC endpoint for node %s", node.Escape().ID().Key()) | |
| t.Require().NoError(err, fmt.Sprintf("failed to get RPC endpoint for node %s", node.Escape().ID().Key())) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log statement uses printf-style formatting with %s but calls t.Log which doesn't support format specifiers. Use t.Logf instead or remove the format specifier.