github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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 15 state "github.com/onflow/flow-go/engine/execution/state/mock" 16 "github.com/onflow/flow-go/model/flow" 17 "github.com/onflow/flow-go/model/messages" 18 "github.com/onflow/flow-go/module/irrecoverable" 19 "github.com/onflow/flow-go/module/mempool/queue" 20 "github.com/onflow/flow-go/module/metrics" 21 "github.com/onflow/flow-go/module/trace" 22 "github.com/onflow/flow-go/network/channels" 23 "github.com/onflow/flow-go/network/mocknetwork" 24 mockprotocol "github.com/onflow/flow-go/state/protocol/mock" 25 "github.com/onflow/flow-go/utils/unittest" 26 ) 27 28 func TestProviderEngine_onChunkDataRequest(t *testing.T) { 29 30 t.Run("non-existent chunk", func(t *testing.T) { 31 net := mocknetwork.NewNetwork(t) 32 chunkConduit := mocknetwork.NewConduit(t) 33 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 34 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 35 e, _, es, requestQueue := newTestEngine(t, net, true) 36 37 es.On("ChunkDataPackByChunkID", mock.Anything).Return(nil, errors.New("not found!")) 38 39 originIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 40 41 chunkID := unittest.IdentifierFixture() 42 43 req := &messages.ChunkDataRequest{ 44 ChunkID: chunkID, 45 Nonce: rand.Uint64(), 46 } 47 48 cancelCtx, cancel := context.WithCancel(context.Background()) 49 defer cancel() 50 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 51 e.Start(ctx) 52 // submit using non-existing origin ID 53 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 54 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 55 56 require.Eventually(t, func() bool { 57 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 58 return !ok 59 }, 1*time.Second, 10*time.Millisecond) 60 61 cancel() 62 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 63 64 // no chunk data pack response should be sent to a request coming from a non-existing origin ID 65 chunkConduit.AssertNotCalled(t, "Unicast") 66 }) 67 68 t.Run("success", func(t *testing.T) { 69 net := mocknetwork.NewNetwork(t) 70 chunkConduit := &mocknetwork.Conduit{} 71 net.On("Register", channels.PushReceipts, mock.Anything).Return(&mocknetwork.Conduit{}, nil) 72 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 73 e, _, es, requestQueue := newTestEngine(t, net, true) 74 75 originIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 76 77 chunkID := unittest.IdentifierFixture() 78 chunkDataPack := unittest.ChunkDataPackFixture(chunkID) 79 80 chunkConduit.On("Unicast", mock.Anything, originIdentity.NodeID). 81 Run(func(args mock.Arguments) { 82 res, ok := args[0].(*messages.ChunkDataResponse) 83 require.True(t, ok) 84 85 actualChunkID := res.ChunkDataPack.ChunkID 86 assert.Equal(t, chunkID, actualChunkID) 87 }). 88 Return(nil) 89 90 es.On("ChunkDataPackByChunkID", chunkID).Return(chunkDataPack, nil) 91 92 req := &messages.ChunkDataRequest{ 93 ChunkID: chunkID, 94 Nonce: rand.Uint64(), 95 } 96 97 cancelCtx, cancel := context.WithCancel(context.Background()) 98 defer cancel() 99 ctx, _ := irrecoverable.WithSignaler(cancelCtx) 100 e.Start(ctx) 101 // submit using non-existing origin ID 102 unittest.RequireCloseBefore(t, e.Ready(), 100*time.Millisecond, "could not start engine") 103 require.NoError(t, e.Process(channels.RequestChunks, originIdentity.NodeID, req)) 104 105 require.Eventually(t, func() bool { 106 _, ok := requestQueue.Get() // ensuring all requests have been picked up from the queue. 107 return !ok 108 }, 1*time.Second, 10*time.Millisecond) 109 110 cancel() 111 unittest.RequireCloseBefore(t, e.Done(), 100*time.Millisecond, "could not stop engine") 112 }) 113 114 } 115 116 func TestProviderEngine_BroadcastExecutionReceipt(t *testing.T) { 117 // prepare 118 net := mocknetwork.NewNetwork(t) 119 chunkConduit := mocknetwork.NewConduit(t) 120 receiptConduit := mocknetwork.NewConduit(t) 121 net.On("Register", channels.PushReceipts, mock.Anything).Return(receiptConduit, nil) 122 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 123 e, ps, _, _ := newTestEngine(t, net, true) 124 125 sealedBlock := unittest.BlockHeaderFixture() 126 sealed := new(mockprotocol.Snapshot) 127 sealed.On("Head").Return(sealedBlock, nil) 128 ps.On("Sealed").Return(sealed) 129 sealedHeight := sealedBlock.Height 130 131 receivers := unittest.IdentityListFixture(1) 132 snap := new(mockprotocol.Snapshot) 133 snap.On("Identities", mock.Anything).Return(receivers, nil) 134 ps.On("Final").Return(snap) 135 136 // verify that above the sealed height will be broadcasted 137 receipt1 := unittest.ExecutionReceiptFixture() 138 receiptConduit.On("Publish", receipt1, receivers.NodeIDs()[0]).Return(nil) 139 140 broadcasted, err := e.BroadcastExecutionReceipt(context.Background(), sealedHeight+1, receipt1) 141 require.NoError(t, err) 142 require.True(t, broadcasted) 143 144 // verify that equal the sealed height will NOT be broadcasted 145 receipt2 := unittest.ExecutionReceiptFixture() 146 broadcasted, err = e.BroadcastExecutionReceipt(context.Background(), sealedHeight, receipt2) 147 require.NoError(t, err) 148 require.False(t, broadcasted) 149 150 // verify that below the sealed height will NOT be broadcasted 151 broadcasted, err = e.BroadcastExecutionReceipt(context.Background(), sealedHeight-1, receipt2) 152 require.NoError(t, err) 153 require.False(t, broadcasted) 154 } 155 156 func TestProviderEngine_BroadcastExecutionUnauthorized(t *testing.T) { 157 net := mocknetwork.NewNetwork(t) 158 chunkConduit := mocknetwork.NewConduit(t) 159 receiptConduit := mocknetwork.NewConduit(t) 160 net.On("Register", channels.PushReceipts, mock.Anything).Return(receiptConduit, nil) 161 net.On("Register", channels.ProvideChunks, mock.Anything).Return(chunkConduit, nil) 162 // make sure the node is not authorized for broadcasting 163 authorized := false 164 e, ps, _, _ := newTestEngine(t, net, authorized) 165 166 sealedBlock := unittest.BlockHeaderFixture() 167 sealed := mockprotocol.NewSnapshot(t) 168 sealed.On("Head").Return(sealedBlock, nil) 169 ps.On("Sealed").Return(sealed) 170 sealedHeight := sealedBlock.Height 171 172 // verify that unstaked node will NOT broadcast 173 receipt2 := unittest.ExecutionReceiptFixture() 174 broadcasted, err := e.BroadcastExecutionReceipt(context.Background(), sealedHeight+1, receipt2) 175 require.NoError(t, err) 176 require.False(t, broadcasted) 177 } 178 179 func newTestEngine(t *testing.T, net *mocknetwork.Network, authorized bool) ( 180 *Engine, 181 *mockprotocol.State, 182 *state.ExecutionState, 183 *queue.HeroStore, 184 ) { 185 ps := mockprotocol.NewState(t) 186 execState := state.NewExecutionState(t) 187 requestQueue := queue.NewHeroStore(10, unittest.Logger(), metrics.NewNoopCollector()) 188 189 e, err := New( 190 unittest.Logger(), 191 trace.NewNoopTracer(), 192 net, 193 ps, 194 execState, 195 metrics.NewNoopCollector(), 196 func(_ flow.Identifier) (bool, error) { return authorized, nil }, 197 requestQueue, 198 DefaultChunkDataPackRequestWorker, 199 DefaultChunkDataPackQueryTimeout, 200 DefaultChunkDataPackDeliveryTimeout) 201 require.NoError(t, err) 202 return e, ps, execState, requestQueue 203 }