github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/sealing/engine_test.go (about)

     1  // (c) 2021 Dapper Labs - ALL RIGHTS RESERVED
     2  
     3  package sealing
     4  
     5  import (
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/stretchr/testify/suite"
    12  
    13  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    14  	"github.com/koko1123/flow-go-1/engine"
    15  	mockconsensus "github.com/koko1123/flow-go-1/engine/consensus/mock"
    16  	"github.com/koko1123/flow-go-1/model/flow"
    17  	"github.com/koko1123/flow-go-1/model/messages"
    18  	"github.com/koko1123/flow-go-1/module/metrics"
    19  	mockmodule "github.com/koko1123/flow-go-1/module/mock"
    20  	"github.com/koko1123/flow-go-1/network/channels"
    21  	mockprotocol "github.com/koko1123/flow-go-1/state/protocol/mock"
    22  	mockstorage "github.com/koko1123/flow-go-1/storage/mock"
    23  	"github.com/koko1123/flow-go-1/utils/unittest"
    24  )
    25  
    26  func TestSealingEngineContext(t *testing.T) {
    27  	suite.Run(t, new(SealingEngineSuite))
    28  }
    29  
    30  type SealingEngineSuite struct {
    31  	suite.Suite
    32  
    33  	core    *mockconsensus.SealingCore
    34  	state   *mockprotocol.State
    35  	index   *mockstorage.Index
    36  	results *mockstorage.ExecutionResults
    37  	myID    flow.Identifier
    38  
    39  	// Sealing Engine
    40  	engine *Engine
    41  }
    42  
    43  func (s *SealingEngineSuite) SetupTest() {
    44  	metrics := metrics.NewNoopCollector()
    45  	s.core = &mockconsensus.SealingCore{}
    46  	s.state = &mockprotocol.State{}
    47  	s.index = &mockstorage.Index{}
    48  	s.results = &mockstorage.ExecutionResults{}
    49  	s.myID = unittest.IdentifierFixture()
    50  	me := &mockmodule.Local{}
    51  	// set up local module mock
    52  	me.On("NodeID").Return(
    53  		func() flow.Identifier {
    54  			return s.myID
    55  		},
    56  	)
    57  
    58  	rootHeader, err := unittest.RootSnapshotFixture(unittest.IdentityListFixture(5, unittest.WithAllRoles())).Head()
    59  	require.NoError(s.T(), err)
    60  
    61  	s.engine = &Engine{
    62  		log:           unittest.Logger(),
    63  		unit:          engine.NewUnit(),
    64  		core:          s.core,
    65  		me:            me,
    66  		engineMetrics: metrics,
    67  		cacheMetrics:  metrics,
    68  		rootHeader:    rootHeader,
    69  		index:         s.index,
    70  		results:       s.results,
    71  		state:         s.state,
    72  	}
    73  
    74  	// setup inbound queues for trusted inputs and message handler for untrusted inputs
    75  	err = s.engine.setupTrustedInboundQueues()
    76  	require.NoError(s.T(), err)
    77  	err = s.engine.setupMessageHandler(unittest.NewSealingConfigs(RequiredApprovalsForSealConstructionTestingValue))
    78  	require.NoError(s.T(), err)
    79  
    80  	<-s.engine.Ready()
    81  }
    82  
    83  // TestOnFinalizedBlock tests if finalized block gets processed when send through `Engine`.
    84  // Tests the whole processing pipeline.
    85  func (s *SealingEngineSuite) TestOnFinalizedBlock() {
    86  
    87  	finalizedBlock := unittest.BlockHeaderFixture()
    88  	finalizedBlockID := finalizedBlock.ID()
    89  
    90  	s.state.On("Final").Return(unittest.StateSnapshotForKnownBlock(finalizedBlock, nil))
    91  	s.core.On("ProcessFinalizedBlock", finalizedBlockID).Return(nil).Once()
    92  	s.engine.OnFinalizedBlock(model.BlockFromFlow(finalizedBlock, finalizedBlock.View-1))
    93  
    94  	// matching engine has at least 100ms ticks for processing events
    95  	time.Sleep(1 * time.Second)
    96  
    97  	s.core.AssertExpectations(s.T())
    98  }
    99  
   100  // TestOnBlockIncorporated tests if incorporated block gets processed when send through `Engine`.
   101  // Tests the whole processing pipeline.
   102  func (s *SealingEngineSuite) TestOnBlockIncorporated() {
   103  	parentBlock := unittest.BlockHeaderFixture()
   104  	incorporatedBlock := unittest.BlockHeaderWithParentFixture(parentBlock)
   105  	incorporatedBlockID := incorporatedBlock.ID()
   106  	// setup payload fixture
   107  	payload := unittest.PayloadFixture(unittest.WithAllTheFixins)
   108  	index := &flow.Index{}
   109  
   110  	for _, result := range payload.Results {
   111  		index.ResultIDs = append(index.ReceiptIDs, result.ID())
   112  		s.results.On("ByID", result.ID()).Return(result, nil).Once()
   113  
   114  		IR := flow.NewIncorporatedResult(parentBlock.ID(), result)
   115  		s.core.On("ProcessIncorporatedResult", IR).Return(nil).Once()
   116  	}
   117  	s.index.On("ByBlockID", parentBlock.ID()).Return(index, nil)
   118  
   119  	// setup headers storage
   120  	headers := &mockstorage.Headers{}
   121  	headers.On("ByBlockID", incorporatedBlockID).Return(incorporatedBlock, nil).Once()
   122  	s.engine.headers = headers
   123  
   124  	s.engine.OnBlockIncorporated(model.BlockFromFlow(incorporatedBlock, incorporatedBlock.View-1))
   125  
   126  	// matching engine has at least 100ms ticks for processing events
   127  	time.Sleep(1 * time.Second)
   128  
   129  	s.core.AssertExpectations(s.T())
   130  }
   131  
   132  // TestMultipleProcessingItems tests that the engine queues multiple receipts and approvals
   133  // and eventually feeds them into sealing.Core for processing
   134  func (s *SealingEngineSuite) TestMultipleProcessingItems() {
   135  	originID := unittest.IdentifierFixture()
   136  	block := unittest.BlockFixture()
   137  
   138  	receipts := make([]*flow.ExecutionReceipt, 20)
   139  	for i := range receipts {
   140  		receipt := unittest.ExecutionReceiptFixture(
   141  			unittest.WithExecutorID(originID),
   142  			unittest.WithResult(unittest.ExecutionResultFixture(unittest.WithBlock(&block))),
   143  		)
   144  		receipts[i] = receipt
   145  	}
   146  
   147  	numApprovalsPerReceipt := 1
   148  	approvals := make([]*flow.ResultApproval, 0, len(receipts)*numApprovalsPerReceipt)
   149  	responseApprovals := make([]*messages.ApprovalResponse, 0)
   150  	approverID := unittest.IdentifierFixture()
   151  	for _, receipt := range receipts {
   152  		for j := 0; j < numApprovalsPerReceipt; j++ {
   153  			approval := unittest.ResultApprovalFixture(unittest.WithExecutionResultID(receipt.ID()),
   154  				unittest.WithApproverID(approverID))
   155  			responseApproval := &messages.ApprovalResponse{
   156  				Approval: *approval,
   157  			}
   158  			responseApprovals = append(responseApprovals, responseApproval)
   159  			approvals = append(approvals, approval)
   160  			s.core.On("ProcessApproval", approval).Return(nil).Twice()
   161  		}
   162  	}
   163  
   164  	var wg sync.WaitGroup
   165  	wg.Add(1)
   166  	go func() {
   167  		defer wg.Done()
   168  		for _, approval := range approvals {
   169  			err := s.engine.Process(channels.ReceiveApprovals, approverID, approval)
   170  			s.Require().NoError(err, "should process approval")
   171  		}
   172  	}()
   173  	wg.Add(1)
   174  	go func() {
   175  		defer wg.Done()
   176  		for _, approval := range responseApprovals {
   177  			err := s.engine.Process(channels.ReceiveApprovals, approverID, approval)
   178  			s.Require().NoError(err, "should process approval")
   179  		}
   180  	}()
   181  
   182  	wg.Wait()
   183  
   184  	// sealing engine has at least 100ms ticks for processing events
   185  	time.Sleep(1 * time.Second)
   186  
   187  	s.core.AssertExpectations(s.T())
   188  }
   189  
   190  // try to submit an approval where the message origin is inconsistent with the message creator
   191  func (s *SealingEngineSuite) TestApprovalInvalidOrigin() {
   192  	// approval from valid origin (i.e. a verification node) but with random ApproverID
   193  	originID := unittest.IdentifierFixture()
   194  	approval := unittest.ResultApprovalFixture() // with random ApproverID
   195  
   196  	err := s.engine.Process(channels.ReceiveApprovals, originID, approval)
   197  	s.Require().NoError(err, "approval from unknown verifier should be dropped but not error")
   198  
   199  	// sealing engine has at least 100ms ticks for processing events
   200  	time.Sleep(1 * time.Second)
   201  
   202  	// In both cases, we expect the approval to be rejected without hitting the mempools
   203  	s.core.AssertNumberOfCalls(s.T(), "ProcessApproval", 0)
   204  }
   205  
   206  // TestProcessUnsupportedMessageType tests that Process and ProcessLocal correctly handle a case where invalid message type
   207  // was submitted from network layer.
   208  func (s *SealingEngineSuite) TestProcessUnsupportedMessageType() {
   209  	invalidEvent := uint64(42)
   210  	err := s.engine.Process("ch", unittest.IdentifierFixture(), invalidEvent)
   211  	// shouldn't result in error since byzantine inputs are expected
   212  	require.NoError(s.T(), err)
   213  	// in case of local processing error cannot be consumed since all inputs are trusted
   214  	err = s.engine.ProcessLocal(invalidEvent)
   215  	require.Error(s.T(), err)
   216  	require.True(s.T(), engine.IsIncompatibleInputTypeError(err))
   217  }