github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/verification/verifier/engine_test.go (about) 1 package verifier_test 2 3 import ( 4 "crypto/rand" 5 "errors" 6 "testing" 7 8 "github.com/ipfs/go-cid" 9 testifymock "github.com/stretchr/testify/mock" 10 "github.com/stretchr/testify/require" 11 "github.com/stretchr/testify/suite" 12 13 "github.com/onflow/crypto" 14 "github.com/onflow/crypto/hash" 15 16 "github.com/onflow/flow-go/engine/testutil/mocklocal" 17 "github.com/onflow/flow-go/engine/verification/utils" 18 "github.com/onflow/flow-go/engine/verification/verifier" 19 chmodel "github.com/onflow/flow-go/model/chunks" 20 "github.com/onflow/flow-go/model/flow" 21 "github.com/onflow/flow-go/model/verification" 22 realModule "github.com/onflow/flow-go/module" 23 mockmodule "github.com/onflow/flow-go/module/mock" 24 "github.com/onflow/flow-go/module/trace" 25 "github.com/onflow/flow-go/network/channels" 26 "github.com/onflow/flow-go/network/mocknetwork" 27 protocol "github.com/onflow/flow-go/state/protocol/mock" 28 mockstorage "github.com/onflow/flow-go/storage/mock" 29 "github.com/onflow/flow-go/utils/unittest" 30 ) 31 32 type VerifierEngineTestSuite struct { 33 suite.Suite 34 net *mocknetwork.Network 35 tracer realModule.Tracer 36 state *protocol.State 37 ss *protocol.Snapshot 38 me *mocklocal.MockLocal 39 sk crypto.PrivateKey 40 hasher hash.Hasher 41 chain flow.Chain 42 pushCon *mocknetwork.Conduit // mocks con for submitting result approvals 43 pullCon *mocknetwork.Conduit 44 metrics *mockmodule.VerificationMetrics // mocks performance monitoring metrics 45 approvals *mockstorage.ResultApprovals 46 chunkVerifier *mockmodule.ChunkVerifier 47 } 48 49 func TestVerifierEngine(t *testing.T) { 50 suite.Run(t, new(VerifierEngineTestSuite)) 51 } 52 53 func (suite *VerifierEngineTestSuite) SetupTest() { 54 suite.state = new(protocol.State) 55 suite.net = mocknetwork.NewNetwork(suite.T()) 56 suite.tracer = trace.NewNoopTracer() 57 suite.ss = new(protocol.Snapshot) 58 suite.pushCon = mocknetwork.NewConduit(suite.T()) 59 suite.pullCon = mocknetwork.NewConduit(suite.T()) 60 suite.metrics = mockmodule.NewVerificationMetrics(suite.T()) 61 suite.chain = flow.Testnet.Chain() 62 suite.approvals = mockstorage.NewResultApprovals(suite.T()) 63 suite.chunkVerifier = mockmodule.NewChunkVerifier(suite.T()) 64 65 suite.net.On("Register", channels.PushApprovals, testifymock.Anything). 66 Return(suite.pushCon, nil). 67 Once() 68 69 suite.net.On("Register", channels.ProvideApprovalsByChunk, testifymock.Anything). 70 Return(suite.pullCon, nil). 71 Once() 72 73 suite.state.On("Final").Return(suite.ss) 74 75 // Mocks the signature oracle of the engine 76 // 77 // generates signing and verification keys 78 seed := make([]byte, crypto.KeyGenSeedMinLen) 79 n, err := rand.Read(seed) 80 require.Equal(suite.T(), n, crypto.KeyGenSeedMinLen) 81 require.NoError(suite.T(), err) 82 83 // creates private key of verification node 84 sk, err := crypto.GeneratePrivateKey(crypto.BLSBLS12381, seed) 85 require.NoError(suite.T(), err) 86 suite.sk = sk 87 88 // tag of hasher should be the same as the tag of engine's hasher 89 suite.hasher = utils.NewResultApprovalHasher() 90 91 // defines the identity of verification node and attaches its key. 92 verIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 93 verIdentity.StakingPubKey = sk.PublicKey() 94 suite.me = mocklocal.NewMockLocal(sk, verIdentity.NodeID, suite.T()) 95 } 96 97 func (suite *VerifierEngineTestSuite) getTestNewEngine() *verifier.Engine { 98 e, err := verifier.New( 99 unittest.Logger(), 100 suite.metrics, 101 suite.tracer, 102 suite.net, 103 suite.state, 104 suite.me, 105 suite.chunkVerifier, 106 suite.approvals) 107 require.Nil(suite.T(), err) 108 109 suite.net.AssertExpectations(suite.T()) 110 return e 111 112 } 113 114 // TestVerifyHappyPath tests the verification path for a single verifiable chunk, which is 115 // assigned to the verifier node, and is passed by the ingest engine 116 // The tests evaluates that a result approval is emitted to all consensus nodes 117 // about the input execution receipt 118 func (suite *VerifierEngineTestSuite) TestVerifyHappyPath() { 119 eng := suite.getTestNewEngine() 120 121 consensusNodes := unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleConsensus)) 122 suite.ss.On("Identities", testifymock.Anything).Return(consensusNodes, nil) 123 124 vChunk := unittest.VerifiableChunkDataFixture(uint64(0)) 125 126 tests := []struct { 127 name string 128 err error 129 }{ 130 // tests that a valid verifiable chunk is verified and a result approval is emitted 131 { 132 name: "chunk verified successfully", 133 err: nil, 134 }, 135 // tests that a verifiable chunk that triggers a CFMissingRegisterTouch fault still emits a result approval 136 { 137 name: "chunk failed with missing register touch fault", 138 err: chmodel.NewCFMissingRegisterTouch( 139 []string{"test missing register touch"}, 140 vChunk.Chunk.Index, 141 vChunk.Result.ID(), 142 unittest.TransactionFixture().ID()), 143 }, 144 } 145 146 for _, test := range tests { 147 suite.Run(test.name, func() { 148 var expectedApproval *flow.ResultApproval 149 150 suite.approvals. 151 On("Store", testifymock.Anything). 152 Return(nil). 153 Run(func(args testifymock.Arguments) { 154 ra, ok := args[0].(*flow.ResultApproval) 155 suite.Require().True(ok) 156 157 suite.Assert().Equal(vChunk.Chunk.BlockID, ra.Body.BlockID) 158 suite.Assert().Equal(vChunk.Result.ID(), ra.Body.ExecutionResultID) 159 suite.Assert().Equal(vChunk.Chunk.Index, ra.Body.ChunkIndex) 160 suite.Assert().Equal(suite.me.NodeID(), ra.Body.ApproverID) 161 162 // verifies the signatures 163 atstID := ra.Body.Attestation.ID() 164 suite.Assert().True(suite.sk.PublicKey().Verify(ra.Body.AttestationSignature, atstID[:], suite.hasher)) 165 bodyID := ra.Body.ID() 166 suite.Assert().True(suite.sk.PublicKey().Verify(ra.VerifierSignature, bodyID[:], suite.hasher)) 167 168 // spock should be non-nil 169 suite.Assert().NotNil(ra.Body.Spock) 170 171 expectedApproval = ra 172 }). 173 Once() 174 suite.approvals. 175 On("Index", testifymock.Anything, testifymock.Anything, testifymock.Anything). 176 Return(nil). 177 Run(func(args testifymock.Arguments) { 178 erID, ok := args[0].(flow.Identifier) 179 suite.Require().True(ok) 180 suite.Assert().Equal(expectedApproval.Body.ExecutionResultID, erID) 181 182 chIndex, ok := args[1].(uint64) 183 suite.Require().True(ok) 184 suite.Assert().Equal(expectedApproval.Body.ChunkIndex, chIndex) 185 186 raID, ok := args[2].(flow.Identifier) 187 suite.Require().True(ok) 188 suite.Assert().Equal(expectedApproval.ID(), raID) 189 }). 190 Once() 191 192 suite.pushCon. 193 On("Publish", testifymock.Anything, testifymock.Anything). 194 Return(nil). 195 Run(func(args testifymock.Arguments) { 196 // check that the approval matches the input execution result 197 ra, ok := args[0].(*flow.ResultApproval) 198 suite.Require().True(ok) 199 suite.Assert().Equal(expectedApproval, ra) 200 201 // note: mock includes each variadic argument as a separate element in slice 202 node, ok := args[1].(flow.Identifier) 203 suite.Require().True(ok) 204 suite.Assert().Equal(consensusNodes.NodeIDs()[0], node) 205 suite.Assert().Len(args, 2) // only a single node should be in the list 206 }). 207 Once() 208 209 suite.metrics.On("OnVerifiableChunkReceivedAtVerifierEngine").Return().Once() 210 suite.metrics.On("OnResultApprovalDispatchedInNetworkByVerifier").Return().Once() 211 212 suite.chunkVerifier.On("Verify", vChunk).Return(nil, test.err).Once() 213 214 err := eng.ProcessLocal(vChunk) 215 suite.Assert().NoError(err) 216 }) 217 } 218 } 219 220 func (suite *VerifierEngineTestSuite) TestVerifyUnhappyPaths() { 221 eng := suite.getTestNewEngine() 222 223 var tests = []struct { 224 errFn func(vc *verification.VerifiableChunkData) error 225 }{ 226 // Note: skipping CFMissingRegisterTouch because it does emit a result approval 227 { 228 errFn: func(vc *verification.VerifiableChunkData) error { 229 return chmodel.NewCFInvalidVerifiableChunk( 230 "test", 231 errors.New("test invalid verifiable chunk"), 232 vc.Chunk.Index, 233 vc.Result.ID()) 234 }, 235 }, 236 { 237 errFn: func(vc *verification.VerifiableChunkData) error { 238 return chmodel.NewCFNonMatchingFinalState( 239 unittest.StateCommitmentFixture(), 240 unittest.StateCommitmentFixture(), 241 vc.Chunk.Index, 242 vc.Result.ID()) 243 }, 244 }, 245 { 246 errFn: func(vc *verification.VerifiableChunkData) error { 247 return chmodel.NewCFInvalidEventsCollection( 248 unittest.IdentifierFixture(), 249 unittest.IdentifierFixture(), 250 vc.Chunk.Index, 251 vc.Result.ID(), 252 flow.EventsList{}) 253 }, 254 }, 255 { 256 errFn: func(vc *verification.VerifiableChunkData) error { 257 return chmodel.NewCFSystemChunkIncludedCollection(vc.Chunk.Index, vc.Result.ID()) 258 }, 259 }, 260 { 261 errFn: func(vc *verification.VerifiableChunkData) error { 262 return chmodel.NewCFExecutionDataBlockIDMismatch( 263 unittest.IdentifierFixture(), 264 unittest.IdentifierFixture(), 265 vc.Chunk.Index, 266 vc.Result.ID()) 267 }, 268 }, 269 { 270 errFn: func(vc *verification.VerifiableChunkData) error { 271 return chmodel.NewCFExecutionDataChunksLengthMismatch( 272 0, 273 0, 274 vc.Chunk.Index, 275 vc.Result.ID()) 276 }, 277 }, 278 { 279 errFn: func(vc *verification.VerifiableChunkData) error { 280 return chmodel.NewCFExecutionDataInvalidChunkCID( 281 cid.Cid{}, 282 cid.Cid{}, 283 vc.Chunk.Index, 284 vc.Result.ID()) 285 }, 286 }, 287 { 288 errFn: func(vc *verification.VerifiableChunkData) error { 289 return chmodel.NewCFInvalidExecutionDataID( 290 unittest.IdentifierFixture(), 291 unittest.IdentifierFixture(), 292 vc.Chunk.Index, 293 vc.Result.ID()) 294 }, 295 }, 296 { 297 errFn: func(vc *verification.VerifiableChunkData) error { 298 return errors.New("test error") 299 }, 300 }, 301 } 302 303 for i, test := range tests { 304 vc := unittest.VerifiableChunkDataFixture(uint64(i)) 305 expectedErr := test.errFn(vc) 306 307 suite.chunkVerifier.On("Verify", vc).Return(nil, expectedErr).Once() 308 309 suite.metrics.On("OnVerifiableChunkReceivedAtVerifierEngine").Return().Once() 310 // note: we shouldn't publish any result approval or emit OnResultApprovalDispatchedInNetworkByVerifier 311 312 err := eng.ProcessLocal(vc) 313 314 // no error returned from the engine 315 suite.Assert().NoError(err) 316 } 317 }