|
9 | 9 |
|
10 | 10 | "github.com/ethereum-optimism/infra/proxyd"
|
11 | 11 | ms "github.com/ethereum-optimism/infra/proxyd/tools/mockserver/handler"
|
| 12 | + "github.com/ethereum/go-ethereum/common/hexutil" |
12 | 13 | "github.com/stretchr/testify/require"
|
13 | 14 | )
|
14 | 15 |
|
@@ -129,3 +130,165 @@ func TestConsensusSkipSyncTest(t *testing.T) {
|
129 | 130 | require.Equal(t, 2, len(consensusGroup))
|
130 | 131 | })
|
131 | 132 | }
|
| 133 | + |
| 134 | +func TestConsensusBlockDriftThreshold(t *testing.T) { |
| 135 | + nodes, bg, _, shutdown := setupCustomConfig(t, "consensus_block_drift_threshold") |
| 136 | + defer nodes["node1"].mockBackend.Close() |
| 137 | + defer nodes["node2"].mockBackend.Close() |
| 138 | + defer shutdown() |
| 139 | + |
| 140 | + ctx := context.Background() |
| 141 | + |
| 142 | + // poll for updated consensus |
| 143 | + update := func() { |
| 144 | + for _, be := range bg.Backends { |
| 145 | + bg.Consensus.UpdateBackend(ctx, be) |
| 146 | + } |
| 147 | + bg.Consensus.UpdateBackendGroupConsensus(ctx) |
| 148 | + } |
| 149 | + |
| 150 | + // convenient methods to manipulate state and mock responses |
| 151 | + reset := func() { |
| 152 | + for _, node := range nodes { |
| 153 | + node.handler.ResetOverrides() |
| 154 | + node.mockBackend.Reset() |
| 155 | + node.backend.ClearSlidingWindows() |
| 156 | + } |
| 157 | + bg.Consensus.ClearListeners() |
| 158 | + bg.Consensus.Reset() |
| 159 | + |
| 160 | + } |
| 161 | + |
| 162 | + initSetupAndAssetions := func() { |
| 163 | + reset() |
| 164 | + update() |
| 165 | + |
| 166 | + require.Equal(t, "0x101", bg.Consensus.GetLatestBlockNumber().String()) |
| 167 | + require.Equal(t, "0xe1", bg.Consensus.GetSafeBlockNumber().String()) |
| 168 | + require.Equal(t, "0xc1", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 169 | + } |
| 170 | + |
| 171 | + override := func(node string, method string, block string, response string) { |
| 172 | + if _, ok := nodes[node]; !ok { |
| 173 | + t.Fatalf("node %s does not exist in the nodes map", node) |
| 174 | + } |
| 175 | + nodes[node].handler.AddOverride(&ms.MethodTemplate{ |
| 176 | + Method: method, |
| 177 | + Block: block, |
| 178 | + Response: response, |
| 179 | + }) |
| 180 | + } |
| 181 | + |
| 182 | + overrideBlock := func(node string, blockRequest string, blockResponse string) { |
| 183 | + override(node, |
| 184 | + "eth_getBlockByNumber", |
| 185 | + blockRequest, |
| 186 | + buildResponse(map[string]string{ |
| 187 | + "number": blockResponse, |
| 188 | + "hash": "hash_" + blockResponse, |
| 189 | + })) |
| 190 | + } |
| 191 | + |
| 192 | + overridePeerCount := func(node string, count int) { |
| 193 | + override(node, "net_peerCount", "", buildResponse(hexutil.Uint64(count).String())) |
| 194 | + } |
| 195 | + |
| 196 | + // force ban node2 and make sure node1 is the only one in consensus |
| 197 | + useOnlyNode1 := func() { |
| 198 | + overridePeerCount("node2", 0) |
| 199 | + update() |
| 200 | + |
| 201 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 202 | + require.Equal(t, 1, len(consensusGroup)) |
| 203 | + require.Contains(t, consensusGroup, nodes["node1"].backend) |
| 204 | + nodes["node1"].mockBackend.Reset() |
| 205 | + } |
| 206 | + |
| 207 | + t.Run("allow backend if tags are messed if below tolerance - safe dropped", func(t *testing.T) { |
| 208 | + initSetupAndAssetions() |
| 209 | + |
| 210 | + overrideBlock("node1", "safe", "0xe0") // 1 blocks behind is ok |
| 211 | + update() |
| 212 | + |
| 213 | + require.Equal(t, "0x101", bg.Consensus.GetLatestBlockNumber().String()) |
| 214 | + require.Equal(t, "0xe0", bg.Consensus.GetSafeBlockNumber().String()) |
| 215 | + require.Equal(t, "0xc1", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 216 | + |
| 217 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 218 | + require.Contains(t, consensusGroup, nodes["node1"].backend) |
| 219 | + require.False(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 220 | + require.Equal(t, 2, len(consensusGroup)) |
| 221 | + }) |
| 222 | + |
| 223 | + t.Run("ban backend if tags are messed above tolerance - safe dropped", func(t *testing.T) { |
| 224 | + initSetupAndAssetions() |
| 225 | + overrideBlock("node1", "safe", "0xdf") // 2 blocks behind is not ok |
| 226 | + update() |
| 227 | + |
| 228 | + require.Equal(t, "0x101", bg.Consensus.GetLatestBlockNumber().String()) |
| 229 | + require.Equal(t, "0xe1", bg.Consensus.GetSafeBlockNumber().String()) |
| 230 | + require.Equal(t, "0xc1", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 231 | + |
| 232 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 233 | + require.NotContains(t, consensusGroup, nodes["node1"].backend) |
| 234 | + require.True(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 235 | + require.Equal(t, 1, len(consensusGroup)) |
| 236 | + }) |
| 237 | + |
| 238 | + t.Run("allow backend if tags are messed if below tolerance - finalized dropped", func(t *testing.T) { |
| 239 | + initSetupAndAssetions() |
| 240 | + overrideBlock("node1", "finalized", "0xbf") // finalized 2 blocks behind is ok |
| 241 | + update() |
| 242 | + |
| 243 | + require.Equal(t, "0x101", bg.Consensus.GetLatestBlockNumber().String()) |
| 244 | + require.Equal(t, "0xe1", bg.Consensus.GetSafeBlockNumber().String()) |
| 245 | + require.Equal(t, "0xbf", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 246 | + |
| 247 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 248 | + require.Contains(t, consensusGroup, nodes["node1"].backend) |
| 249 | + require.False(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 250 | + require.Equal(t, 2, len(consensusGroup)) |
| 251 | + }) |
| 252 | + |
| 253 | + t.Run("ban backend if tags are messed - finalized dropped", func(t *testing.T) { |
| 254 | + initSetupAndAssetions() |
| 255 | + overrideBlock("node1", "finalized", "0xbe") // finalized 3 blocks behind is not ok |
| 256 | + update() |
| 257 | + |
| 258 | + require.Equal(t, "0x101", bg.Consensus.GetLatestBlockNumber().String()) |
| 259 | + require.Equal(t, "0xe1", bg.Consensus.GetSafeBlockNumber().String()) |
| 260 | + require.Equal(t, "0xc1", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 261 | + |
| 262 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 263 | + require.NotContains(t, consensusGroup, nodes["node1"].backend) |
| 264 | + require.True(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 265 | + require.Equal(t, 1, len(consensusGroup)) |
| 266 | + }) |
| 267 | + |
| 268 | + t.Run("recover after safe and finalized dropped", func(t *testing.T) { |
| 269 | + reset() |
| 270 | + useOnlyNode1() |
| 271 | + overrideBlock("node1", "latest", "0xd1") |
| 272 | + overrideBlock("node1", "safe", "0xb1") |
| 273 | + overrideBlock("node1", "finalized", "0x91") |
| 274 | + update() |
| 275 | + |
| 276 | + consensusGroup := bg.Consensus.GetConsensusGroup() |
| 277 | + require.NotContains(t, consensusGroup, nodes["node1"].backend) |
| 278 | + require.True(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 279 | + require.Equal(t, 0, len(consensusGroup)) |
| 280 | + |
| 281 | + // unban and see if it recovers |
| 282 | + bg.Consensus.Unban(nodes["node1"].backend) |
| 283 | + update() |
| 284 | + |
| 285 | + consensusGroup = bg.Consensus.GetConsensusGroup() |
| 286 | + require.Contains(t, consensusGroup, nodes["node1"].backend) |
| 287 | + require.False(t, bg.Consensus.IsBanned(nodes["node1"].backend)) |
| 288 | + require.Equal(t, 1, len(consensusGroup)) |
| 289 | + |
| 290 | + require.Equal(t, "0xd1", bg.Consensus.GetLatestBlockNumber().String()) |
| 291 | + require.Equal(t, "0xb1", bg.Consensus.GetSafeBlockNumber().String()) |
| 292 | + require.Equal(t, "0x91", bg.Consensus.GetFinalizedBlockNumber().String()) |
| 293 | + }) |
| 294 | +} |
0 commit comments