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