github.com/koko1123/flow-go-1@v0.29.6/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/rs/zerolog" 9 "github.com/stretchr/testify/mock" 10 testifymock "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 "github.com/stretchr/testify/suite" 13 14 "github.com/onflow/flow-go/crypto" 15 "github.com/onflow/flow-go/crypto/hash" 16 "github.com/koko1123/flow-go-1/engine/testutil/mocklocal" 17 "github.com/koko1123/flow-go-1/engine/verification/utils" 18 "github.com/koko1123/flow-go-1/engine/verification/verifier" 19 chmodel "github.com/koko1123/flow-go-1/model/chunks" 20 "github.com/koko1123/flow-go-1/model/flow" 21 "github.com/koko1123/flow-go-1/model/verification" 22 realModule "github.com/koko1123/flow-go-1/module" 23 mockmodule "github.com/koko1123/flow-go-1/module/mock" 24 "github.com/koko1123/flow-go-1/module/trace" 25 "github.com/koko1123/flow-go-1/network/channels" 26 "github.com/koko1123/flow-go-1/network/mocknetwork" 27 protocol "github.com/koko1123/flow-go-1/state/protocol/mock" 28 mockstorage "github.com/koko1123/flow-go-1/storage/mock" 29 "github.com/koko1123/flow-go-1/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 } 47 48 func TestVerifierEngine(t *testing.T) { 49 suite.Run(t, new(VerifierEngineTestSuite)) 50 } 51 52 func (suite *VerifierEngineTestSuite) SetupTest() { 53 54 suite.state = &protocol.State{} 55 suite.net = &mocknetwork.Network{} 56 suite.tracer = trace.NewNoopTracer() 57 suite.ss = &protocol.Snapshot{} 58 suite.pushCon = &mocknetwork.Conduit{} 59 suite.pullCon = &mocknetwork.Conduit{} 60 suite.metrics = &mockmodule.VerificationMetrics{} 61 suite.chain = flow.Testnet.Chain() 62 suite.approvals = &mockstorage.ResultApprovals{} 63 64 suite.approvals.On("Store", mock.Anything).Return(nil) 65 suite.approvals.On("Index", mock.Anything, mock.Anything, mock.Anything).Return(nil) 66 67 suite.net.On("Register", channels.PushApprovals, testifymock.Anything). 68 Return(suite.pushCon, nil). 69 Once() 70 71 suite.net.On("Register", channels.ProvideApprovalsByChunk, testifymock.Anything). 72 Return(suite.pullCon, nil). 73 Once() 74 75 suite.state.On("Final").Return(suite.ss) 76 77 // Mocks the signature oracle of the engine 78 // 79 // generates signing and verification keys 80 seed := make([]byte, crypto.KeyGenSeedMinLenBLSBLS12381) 81 n, err := rand.Read(seed) 82 require.Equal(suite.T(), n, crypto.KeyGenSeedMinLenBLSBLS12381) 83 require.NoError(suite.T(), err) 84 85 // creates private key of verification node 86 sk, err := crypto.GeneratePrivateKey(crypto.BLSBLS12381, seed) 87 require.NoError(suite.T(), err) 88 suite.sk = sk 89 90 // tag of hasher should be the same as the tag of engine's hasher 91 suite.hasher = utils.NewResultApprovalHasher() 92 93 // defines the identity of verification node and attaches its key. 94 verIdentity := unittest.IdentityFixture(unittest.WithRole(flow.RoleVerification)) 95 verIdentity.StakingPubKey = sk.PublicKey() 96 suite.me = mocklocal.NewMockLocal(sk, verIdentity.NodeID, suite.T()) 97 } 98 99 func (suite *VerifierEngineTestSuite) TestNewEngine() *verifier.Engine { 100 e, err := verifier.New( 101 zerolog.Logger{}, 102 suite.metrics, 103 suite.tracer, 104 suite.net, 105 suite.state, 106 suite.me, 107 ChunkVerifierMock{}, 108 suite.approvals) 109 require.Nil(suite.T(), err) 110 111 suite.net.AssertExpectations(suite.T()) 112 return e 113 114 } 115 116 func (suite *VerifierEngineTestSuite) TestIncorrectResult() { 117 // TODO when ERs are verified 118 } 119 120 // TestVerifyHappyPath tests the verification path for a single verifiable chunk, which is 121 // assigned to the verifier node, and is passed by the ingest engine 122 // The tests evaluates that a result approval is emitted to all consensus nodes 123 // about the input execution receipt 124 func (suite *VerifierEngineTestSuite) TestVerifyHappyPath() { 125 126 eng := suite.TestNewEngine() 127 myID := unittest.IdentifierFixture() 128 consensusNodes := unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleConsensus)) 129 // creates a verifiable chunk 130 vChunk := unittest.VerifiableChunkDataFixture(uint64(0)) 131 132 // mocking node ID using the LocalMock 133 suite.me.MockNodeID(myID) 134 suite.ss.On("Identities", testifymock.Anything).Return(consensusNodes, nil) 135 136 // mocks metrics 137 // reception of verifiable chunk 138 suite.metrics.On("OnVerifiableChunkReceivedAtVerifierEngine").Return() 139 // emission of result approval 140 suite.metrics.On("OnResultApprovalDispatchedInNetworkByVerifier").Return() 141 142 suite.pushCon. 143 On("Publish", testifymock.Anything, testifymock.Anything). 144 Return(nil). 145 Run(func(args testifymock.Arguments) { 146 // check that the approval matches the input execution result 147 ra, ok := args[0].(*flow.ResultApproval) 148 suite.Assert().True(ok) 149 suite.Assert().Equal(vChunk.Result.ID(), ra.Body.ExecutionResultID) 150 151 // verifies the signatures 152 atstID := ra.Body.Attestation.ID() 153 suite.Assert().True(suite.sk.PublicKey().Verify(ra.Body.AttestationSignature, atstID[:], suite.hasher)) 154 bodyID := ra.Body.ID() 155 suite.Assert().True(suite.sk.PublicKey().Verify(ra.VerifierSignature, bodyID[:], suite.hasher)) 156 157 // spock should be non-nil 158 suite.Assert().NotNil(ra.Body.Spock) 159 }). 160 Once() 161 162 err := eng.ProcessLocal(vChunk) 163 suite.Assert().NoError(err) 164 suite.ss.AssertExpectations(suite.T()) 165 suite.pushCon.AssertExpectations(suite.T()) 166 167 } 168 169 func (suite *VerifierEngineTestSuite) TestVerifyUnhappyPaths() { 170 eng := suite.TestNewEngine() 171 myID := unittest.IdentifierFixture() 172 consensusNodes := unittest.IdentityListFixture(1, unittest.WithRole(flow.RoleConsensus)) 173 174 // mocking node ID using the LocalMock 175 suite.me.MockNodeID(myID) 176 suite.ss.On("Identities", testifymock.Anything).Return(consensusNodes, nil) 177 178 // mocks metrics 179 // reception of verifiable chunk 180 suite.metrics.On("OnVerifiableChunkReceivedAtVerifierEngine").Return() 181 182 // we shouldn't receive any result approval 183 suite.pushCon. 184 On("Publish", testifymock.Anything, testifymock.Anything). 185 Return(nil). 186 Run(func(args testifymock.Arguments) { 187 // TODO change this to check challeneges 188 _, ok := args[0].(*flow.ResultApproval) 189 // TODO change this to false when missing register is rolled back 190 suite.Assert().True(ok) 191 }) 192 193 // emission of result approval 194 suite.metrics.On("OnResultApprovalDispatchedInNetworkByVerifier").Return() 195 196 var tests = []struct { 197 vc *verification.VerifiableChunkData 198 expectedErr error 199 }{ 200 {unittest.VerifiableChunkDataFixture(uint64(1)), nil}, 201 {unittest.VerifiableChunkDataFixture(uint64(2)), nil}, 202 {unittest.VerifiableChunkDataFixture(uint64(3)), nil}, 203 } 204 for _, test := range tests { 205 err := eng.ProcessLocal(test.vc) 206 suite.Assert().NoError(err) 207 } 208 } 209 210 type ChunkVerifierMock struct { 211 } 212 213 func (v ChunkVerifierMock) Verify(vc *verification.VerifiableChunkData) ([]byte, chmodel.ChunkFault, error) { 214 if vc.IsSystemChunk { 215 return nil, nil, nil 216 } 217 218 switch vc.Chunk.Index { 219 case 0: 220 return []byte{}, nil, nil 221 // return error 222 case 1: 223 return nil, chmodel.NewCFMissingRegisterTouch( 224 []string{"test missing register touch"}, 225 vc.Chunk.Index, 226 vc.Result.ID(), 227 unittest.TransactionFixture().ID()), nil 228 229 case 2: 230 return nil, chmodel.NewCFInvalidVerifiableChunk( 231 "test", 232 errors.New("test invalid verifiable chunk"), 233 vc.Chunk.Index, 234 vc.Result.ID()), nil 235 236 case 3: 237 return nil, chmodel.NewCFNonMatchingFinalState( 238 unittest.StateCommitmentFixture(), 239 unittest.StateCommitmentFixture(), 240 vc.Chunk.Index, 241 vc.Result.ID()), nil 242 243 // TODO add cases for challenges 244 // return successful by default 245 default: 246 return nil, nil, nil 247 } 248 249 }