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 }