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  }