github.com/koko1123/flow-go-1@v0.29.6/engine/execution/provider/engine_test.go (about) 1 package provider 2 3 import ( 4 "context" 5 "errors" 6 "math/rand" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 _ "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/mock" 13 "github.com/stretchr/testify/require" 14 "go.uber.org/atomic" 15 16 state "github.com/koko1123/flow-go-1/engine/execution/state/mock" 17 "github.com/koko1123/flow-go-1/model/flow" 18 "github.com/koko1123/flow-go-1/model/messages" 19 "github.com/koko1123/flow-go-1/module/irrecoverable" 20 "github.com/koko1123/flow-go-1/module/mempool/queue" 21 "github.com/koko1123/flow-go-1/module/metrics" 22 "github.com/koko1123/flow-go-1/module/trace" 23 "github.com/koko1123/flow-go-1/network/channels" 24 "github.com/koko1123/flow-go-1/network/mocknetwork" 25 "github.com/koko1123/flow-go-1/state/protocol" 26 mockprotocol "github.com/koko1123/flow-go-1/state/protocol/mock" 27 "github.com/koko1123/flow-go-1/utils/unittest" 28 ) 29 30 func TestProviderEngine_onChunkDataRequest(t *testing.T) { 31 t.Run("non-verification engine", func(t *testing.T) { 32 ps := mockprotocol.NewState(t) 33 ss := mockprotocol.NewSnapshot(t) 34 net := mocknetwork.NewNetwork(t) 35 chunkConduit := mocknetwork.NewConduit(t) 36 execState := state.NewExecutionState(t) 37 38 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 39 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 40 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 41 42 e, err := New( 43 unittest.Logger(), 44 trace.NewNoopTracer(), 45 net, 46 ps, 47 execState, 48 metrics.NewNoopCollector(), 49 func(_ flow.Identifier) (bool, error) { return true, nil }, 50 requestQueue, 51 DefaultChunkDataPackRequestWorker, 52 DefaultChunkDataPackQueryTimeout, 53 DefaultChunkDataPackDeliveryTimeout) 54 require.NoError(t, err) 55 56 originID := unittest.IdentifierFixture() 57 chunkID := unittest.IdentifierFixture() 58 blockID := unittest.IdentifierFixture() 59 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 60 61 ps.On("AtBlockID", blockID).Return(ss).Once() 62 ss.On("Identity", originID).Return(unittest.IdentityFixture(unittest.WithRole(flow.RoleExecution)), nil) 63 execState.On("ChunkDataPackByChunkID", mock.Anything).Return(chunkDataPack, nil) 64 execState.On("GetBlockIDByChunkID", chunkID).Return(blockID, nil) 65 66 req := &messages.ChunkDataRequest{ 67 ChunkID: chunkID, 68 Nonce: rand.Uint64(), 69 } 70 71 cancelCtx, cancel := context.WithCancel(context.Background()) 72 defer cancel() 73 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 74 e.Start(ctx) 75 // submit using origin ID with invalid role 76 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 77 require.NoError(t, e.Process(channels.RequestChunks, originID, req)) 78 79 require.Eventually(t, func() bool { 80 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 81 return !ok 82 }, 1*time.Second, 10*time.Millisecond) 83 84 cancel() 85 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 86 87 // no chunk data pack response should be sent to an invalid role's request 88 chunkConduit.AssertNotCalled(t, "Unicast") 89 }) 90 91 t.Run("unauthorized (0 weight) origin", func(t *testing.T) { 92 ps := mockprotocol.NewState(t) 93 ss := mockprotocol.NewSnapshot(t) 94 net := mocknetwork.NewNetwork(t) 95 chunkConduit := mocknetwork.NewConduit(t) 96 execState := state.NewExecutionState(t) 97 98 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 99 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 100 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 101 102 e, err := New( 103 unittest.Logger(), 104 trace.NewNoopTracer(), 105 net, 106 ps, 107 execState, 108 metrics.NewNoopCollector(), 109 func(_ flow.Identifier) (bool, error) { return true, nil }, 110 requestQueue, 111 DefaultChunkDataPackRequestWorker, 112 DefaultChunkDataPackQueryTimeout, 113 DefaultChunkDataPackDeliveryTimeout) 114 require.NoError(t, err) 115 116 originID := unittest.IdentifierFixture() 117 chunkID := unittest.IdentifierFixture() 118 blockID := unittest.IdentifierFixture() 119 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 120 121 ps.On("AtBlockID", blockID).Return(ss).Once() 122 ss.On("Identity", originID).Return(unittest.IdentityFixture(unittest.WithRole(flow.RoleExecution), unittest.WithWeight(0)), nil) 123 execState.On("ChunkDataPackByChunkID", mock.Anything).Return(chunkDataPack, nil) 124 execState.On("GetBlockIDByChunkID", chunkID).Return(blockID, nil) 125 126 req := &messages.ChunkDataRequest{ 127 ChunkID: chunkID, 128 Nonce: rand.Uint64(), 129 } 130 cancelCtx, cancel := context.WithCancel(context.Background()) 131 defer cancel() 132 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 133 e.Start(ctx) 134 // submit using origin ID with zero weight 135 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 136 require.NoError(t, e.Process(channels.RequestChunks, originID, req)) 137 138 require.Eventually(t, func() bool { 139 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 140 return !ok 141 }, 1*time.Second, 10*time.Millisecond) 142 143 cancel() 144 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 145 146 // no chunk data pack response should be sent to a request coming from 0-weight node 147 chunkConduit.AssertNotCalled(t, "Unicast") 148 }) 149 150 t.Run("un-authorized (not found origin) origin", func(t *testing.T) { 151 ps := mockprotocol.NewState(t) 152 ss := mockprotocol.NewSnapshot(t) 153 net := mocknetwork.NewNetwork(t) 154 chunkConduit := mocknetwork.NewConduit(t) 155 execState := state.NewExecutionState(t) 156 157 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 158 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 159 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 160 161 e, err := New( 162 unittest.Logger(), 163 trace.NewNoopTracer(), 164 net, 165 ps, 166 execState, 167 metrics.NewNoopCollector(), 168 func(_ flow.Identifier) (bool, error) { return true, nil }, 169 requestQueue, 170 DefaultChunkDataPackRequestWorker, 171 DefaultChunkDataPackQueryTimeout, 172 DefaultChunkDataPackDeliveryTimeout) 173 require.NoError(t, err) 174 175 originID := unittest.IdentifierFixture() 176 chunkID := unittest.IdentifierFixture() 177 blockID := unittest.IdentifierFixture() 178 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 179 180 ps.On("AtBlockID", blockID).Return(ss).Once() 181 ss.On("Identity", originID).Return(nil, protocol.IdentityNotFoundError{}) 182 execState.On("ChunkDataPackByChunkID", mock.Anything).Return(chunkDataPack, nil) 183 execState.On("GetBlockIDByChunkID", chunkID).Return(blockID, nil) 184 185 req := &messages.ChunkDataRequest{ 186 ChunkID: chunkID, 187 Nonce: rand.Uint64(), 188 } 189 cancelCtx, cancel := context.WithCancel(context.Background()) 190 defer cancel() 191 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 192 e.Start(ctx) 193 // submit using non-existing origin ID 194 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 195 require.NoError(t, e.Process(channels.RequestChunks, originID, req)) 196 197 require.Eventually(t, func() bool { 198 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 199 return !ok 200 }, 1*time.Second, 10*time.Millisecond) 201 202 cancel() 203 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 204 205 // no chunk data pack response should be sent to a request coming from a non-existing origin ID 206 chunkConduit.AssertNotCalled(t, "Unicast") 207 }) 208 209 t.Run("non-existent chunk", func(t *testing.T) { 210 ps := mockprotocol.NewState(t) 211 net := mocknetwork.NewNetwork(t) 212 chunkConduit := mocknetwork.NewConduit(t) 213 execState := state.NewExecutionState(t) 214 215 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 216 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 217 218 execState.On("ChunkDataPackByChunkID", mock.Anything).Return(nil, errors.New("not found!")) 219 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 220 221 e, err := New( 222 unittest.Logger(), 223 trace.NewNoopTracer(), 224 net, 225 ps, 226 execState, 227 metrics.NewNoopCollector(), 228 func(_ flow.Identifier) (bool, error) { return true, nil }, 229 requestQueue, 230 DefaultChunkDataPackRequestWorker, 231 DefaultChunkDataPackQueryTimeout, 232 DefaultChunkDataPackDeliveryTimeout) 233 require.NoError(t, err) 234 235 originIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 236 237 chunkID := unittest.IdentifierFixture() 238 239 req := &messages.ChunkDataRequest{ 240 ChunkID: chunkID, 241 Nonce: rand.Uint64(), 242 } 243 244 cancelCtx, cancel := context.WithCancel(context.Background()) 245 defer cancel() 246 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 247 e.Start(ctx) 248 // submit using non-existing origin ID 249 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 250 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 251 252 require.Eventually(t, func() bool { 253 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 254 return !ok 255 }, 1*time.Second, 10*time.Millisecond) 256 257 cancel() 258 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 259 260 // no chunk data pack response should be sent to a request coming from a non-existing origin ID 261 chunkConduit.AssertNotCalled(t, "Unicast") 262 }) 263 264 t.Run("success", func(t *testing.T) { 265 ps := new(mockprotocol.State) 266 ss := new(mockprotocol.Snapshot) 267 net := new(mocknetwork.Network) 268 chunkConduit := &mocknetwork.Conduit{} 269 execState := new(state.ExecutionState) 270 271 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 272 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 273 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 274 275 e, err := New( 276 unittest.Logger(), 277 trace.NewNoopTracer(), 278 net, 279 ps, 280 execState, 281 metrics.NewNoopCollector(), 282 func(_ flow.Identifier) (bool, error) { return true, nil }, 283 requestQueue, 284 DefaultChunkDataPackRequestWorker, 285 DefaultChunkDataPackQueryTimeout, 286 DefaultChunkDataPackDeliveryTimeout) 287 require.NoError(t, err) 288 289 originIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 290 291 chunkID := unittest.IdentifierFixture() 292 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 293 blockID := unittest.IdentifierFixture() 294 295 ps.On("AtBlockID", blockID).Return(ss).Once() 296 ss.On("Identity", originIdentity.NodeID).Return(originIdentity, nil) 297 chunkConduit.On("Unicast", mock.Anything, originIdentity.NodeID). 298 Run(func(args mock.Arguments) { 299 res, ok := args[0].(*messages.ChunkDataResponse) 300 require.True(t, ok) 301 302 actualChunkID := res.ChunkDataPack.ChunkID 303 assert.Equal(t, chunkID, actualChunkID) 304 }). 305 Return(nil) 306 307 execState.On("GetBlockIDByChunkID", chunkID).Return(blockID, nil) 308 execState.On("ChunkDataPackByChunkID", chunkID).Return(chunkDataPack, nil) 309 310 req := &messages.ChunkDataRequest{ 311 ChunkID: chunkID, 312 Nonce: rand.Uint64(), 313 } 314 315 cancelCtx, cancel := context.WithCancel(context.Background()) 316 defer cancel() 317 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 318 e.Start(ctx) 319 // submit using non-existing origin ID 320 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 321 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 322 323 require.Eventually(t, func() bool { 324 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 325 return !ok 326 }, 1*time.Second, 10*time.Millisecond) 327 328 cancel() 329 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 330 }) 331 332 t.Run("reply to chunk data pack request only when authorized", func(t *testing.T) { 333 currentAuthorizedState := atomic.Bool{} 334 currentAuthorizedState.Store(true) 335 ps := mockprotocol.NewState(t) 336 ss := mockprotocol.NewSnapshot(t) 337 net := mocknetwork.NewNetwork(t) 338 chunkConduit := mocknetwork.NewConduit(t) 339 execState := state.NewExecutionState(t) 340 341 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 342 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 343 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 344 345 e, err := New( 346 unittest.Logger(), 347 trace.NewNoopTracer(), 348 net, 349 ps, 350 execState, 351 metrics.NewNoopCollector(), 352 func(_ flow.Identifier) (bool, error) { return currentAuthorizedState.Load(), nil }, 353 requestQueue, 354 DefaultChunkDataPackRequestWorker, 355 DefaultChunkDataPackQueryTimeout, 356 DefaultChunkDataPackDeliveryTimeout) 357 require.NoError(t, err) 358 359 originIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 360 361 chunkID := unittest.IdentifierFixture() 362 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 363 blockID := unittest.IdentifierFixture() 364 365 execState.On("GetBlockIDByChunkID", chunkID).Return(blockID, nil) 366 ps.On("AtBlockID", blockID).Return(ss).Once() 367 ss.On("Identity", originIdentity.NodeID).Return(originIdentity, nil).Once() 368 369 // channel tracking for the first chunk data pack request responded. 370 chunkConduit.On("Unicast", mock.Anything, originIdentity.NodeID). 371 Run(func(args mock.Arguments) { 372 res, ok := args[0].(*messages.ChunkDataResponse) 373 require.True(t, ok) 374 375 actualChunkID := res.ChunkDataPack.ChunkID 376 assert.Equal(t, chunkID, actualChunkID) 377 }). 378 Return(nil).Once() 379 380 execState.On("ChunkDataPackByChunkID", chunkID).Return(chunkDataPack, nil).Twice() 381 382 req := &messages.ChunkDataRequest{ 383 ChunkID: chunkID, 384 Nonce: rand.Uint64(), 385 } 386 387 cancelCtx, cancel := context.WithCancel(context.Background()) 388 defer cancel() 389 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 390 e.Start(ctx) 391 // submit using non-existing origin ID 392 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 393 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 394 395 require.Eventually(t, func() bool { 396 _, ok := requestQueue.Get() // ensuring first request has been picked up from the queue. 397 return !ok 398 }, 1*time.Second, 100*time.Millisecond) 399 currentAuthorizedState.Store(false) 400 401 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 402 require.Eventually(t, func() bool { 403 _, ok := requestQueue.Get() // ensuring second request has been picked up from the queue as well. 404 return !ok 405 }, 1*time.Second, 10*time.Millisecond) 406 407 cancel() 408 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 409 }) 410 }