github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/verification/verifier/engine.go (about) 1 package verifier 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/onflow/crypto" 8 "github.com/onflow/crypto/hash" 9 "github.com/rs/zerolog" 10 "go.opentelemetry.io/otel/attribute" 11 12 "github.com/onflow/flow-go/engine" 13 "github.com/onflow/flow-go/engine/verification/utils" 14 chmodels "github.com/onflow/flow-go/model/chunks" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/model/flow/filter" 17 "github.com/onflow/flow-go/model/messages" 18 "github.com/onflow/flow-go/model/verification" 19 "github.com/onflow/flow-go/module" 20 "github.com/onflow/flow-go/module/signature" 21 "github.com/onflow/flow-go/module/trace" 22 "github.com/onflow/flow-go/network" 23 "github.com/onflow/flow-go/network/channels" 24 "github.com/onflow/flow-go/state/protocol" 25 "github.com/onflow/flow-go/storage" 26 "github.com/onflow/flow-go/utils/logging" 27 ) 28 29 // Engine (verifier engine) verifies chunks, generates result approvals or raises challenges. 30 // as input it accepts verifiable chunks (chunk + all data needed) and perform verification by 31 // constructing a partial trie, executing transactions and check the final state commitment and 32 // other chunk meta data (e.g. tx count) 33 type Engine struct { 34 unit *engine.Unit // used to control startup/shutdown 35 log zerolog.Logger // used to log relevant actions 36 metrics module.VerificationMetrics // used to capture the performance metrics 37 tracer module.Tracer // used for tracing 38 pushConduit network.Conduit // used to push result approvals 39 pullConduit network.Conduit // used to respond to requests for result approvals 40 me module.Local // used to access local node information 41 state protocol.State // used to access the protocol state 42 approvalHasher hash.Hasher // used as hasher to sign the result approvals 43 chVerif module.ChunkVerifier // used to verify chunks 44 spockHasher hash.Hasher // used for generating spocks 45 approvals storage.ResultApprovals // used to store result approvals 46 } 47 48 // New creates and returns a new instance of a verifier engine. 49 func New( 50 log zerolog.Logger, 51 metrics module.VerificationMetrics, 52 tracer module.Tracer, 53 net network.EngineRegistry, 54 state protocol.State, 55 me module.Local, 56 chVerif module.ChunkVerifier, 57 approvals storage.ResultApprovals, 58 ) (*Engine, error) { 59 60 e := &Engine{ 61 unit: engine.NewUnit(), 62 log: log.With().Str("engine", "verifier").Logger(), 63 metrics: metrics, 64 tracer: tracer, 65 state: state, 66 me: me, 67 chVerif: chVerif, 68 approvalHasher: utils.NewResultApprovalHasher(), 69 spockHasher: signature.NewBLSHasher(signature.SPOCKTag), 70 approvals: approvals, 71 } 72 73 var err error 74 e.pushConduit, err = net.Register(channels.PushApprovals, e) 75 if err != nil { 76 return nil, fmt.Errorf("could not register engine on approval push channel: %w", err) 77 } 78 79 e.pullConduit, err = net.Register(channels.ProvideApprovalsByChunk, e) 80 if err != nil { 81 return nil, fmt.Errorf("could not register engine on approval pull channel: %w", err) 82 } 83 84 return e, nil 85 } 86 87 // Ready returns a channel that is closed when the verifier engine is ready. 88 func (e *Engine) Ready() <-chan struct{} { 89 return e.unit.Ready() 90 } 91 92 // Done returns a channel that is closed when the verifier engine is done. 93 func (e *Engine) Done() <-chan struct{} { 94 return e.unit.Done() 95 } 96 97 // SubmitLocal submits an event originating on the local node. 98 func (e *Engine) SubmitLocal(event interface{}) { 99 e.unit.Launch(func() { 100 err := e.ProcessLocal(event) 101 if err != nil { 102 engine.LogError(e.log, err) 103 } 104 }) 105 } 106 107 // Submit submits the given event from the node with the given origin ID 108 // for processing in a non-blocking manner. It returns instantly and logs 109 // a potential processing error internally when done. 110 func (e *Engine) Submit(channel channels.Channel, originID flow.Identifier, event interface{}) { 111 e.unit.Launch(func() { 112 err := e.Process(channel, originID, event) 113 if err != nil { 114 engine.LogError(e.log, err) 115 } 116 }) 117 } 118 119 // ProcessLocal processes an event originating on the local node. 120 func (e *Engine) ProcessLocal(event interface{}) error { 121 return e.unit.Do(func() error { 122 return e.process(e.me.NodeID(), event) 123 }) 124 } 125 126 // Process processes the given event from the node with the given origin ID in 127 // a blocking manner. It returns the potential processing error when done. 128 func (e *Engine) Process(channel channels.Channel, originID flow.Identifier, event interface{}) error { 129 return e.unit.Do(func() error { 130 return e.process(originID, event) 131 }) 132 } 133 134 // process receives verifiable chunks, evaluate them and send them for chunk verifier 135 func (e *Engine) process(originID flow.Identifier, event interface{}) error { 136 var err error 137 138 switch resource := event.(type) { 139 case *verification.VerifiableChunkData: 140 err = e.verifiableChunkHandler(originID, resource) 141 case *messages.ApprovalRequest: 142 err = e.approvalRequestHandler(originID, resource) 143 default: 144 return fmt.Errorf("invalid event type (%T)", event) 145 } 146 147 if err != nil { 148 // logs the error instead of returning that. 149 // returning error would be projected at a higher level by network layer. 150 // however, this is an engine-level error, and not network layer error. 151 e.log.Warn().Err(err).Msg("engine could not process event successfully") 152 } 153 154 return nil 155 } 156 157 // verify handles the core verification process. It accepts a verifiable chunk 158 // and all dependent resources, verifies the chunk, and emits a 159 // result approval if applicable. 160 // 161 // If any part of verification fails, an error is returned, indicating to the 162 // initiating engine that the verification must be re-tried. 163 func (e *Engine) verify(ctx context.Context, originID flow.Identifier, 164 vc *verification.VerifiableChunkData) error { 165 // log it first 166 log := e.log.With().Timestamp(). 167 Hex("origin", logging.ID(originID)). 168 Uint64("chunk_index", vc.Chunk.Index). 169 Hex("result_id", logging.Entity(vc.Result)). 170 Logger() 171 172 log.Info().Msg("verifiable chunk received by verifier engine") 173 174 // only accept internal calls 175 if originID != e.me.NodeID() { 176 return fmt.Errorf("invalid remote origin for verify") 177 } 178 179 var err error 180 181 // extracts chunk ID 182 ch, ok := vc.Result.Chunks.ByIndex(vc.Chunk.Index) 183 if !ok { 184 return engine.NewInvalidInputErrorf("chunk out of range requested: %v", vc.Chunk.Index) 185 } 186 log.With().Hex("chunk_id", logging.Entity(ch)).Logger() 187 188 // execute the assigned chunk 189 span, _ := e.tracer.StartSpanFromContext(ctx, trace.VERVerChunkVerify) 190 191 spockSecret, err := e.chVerif.Verify(vc) 192 span.End() 193 194 if err != nil { 195 // any error besides a ChunkFaultError is a system error 196 if !chmodels.IsChunkFaultError(err) { 197 return fmt.Errorf("cannot verify chunk: %w", err) 198 } 199 200 // if any fault found with the chunk 201 switch chFault := err.(type) { 202 case *chmodels.CFMissingRegisterTouch: 203 e.log.Warn(). 204 Str("chunk_fault_type", "missing_register_touch"). 205 Str("chunk_fault", chFault.Error()). 206 Msg("chunk fault found, could not verify chunk") 207 // still create approvals for this case 208 case *chmodels.CFNonMatchingFinalState: 209 // TODO raise challenge 210 e.log.Warn(). 211 Str("chunk_fault_type", "final_state_mismatch"). 212 Str("chunk_fault", chFault.Error()). 213 Msg("chunk fault found, could not verify chunk") 214 return nil 215 case *chmodels.CFInvalidVerifiableChunk: 216 // TODO raise challenge 217 e.log.Error(). 218 Str("chunk_fault_type", "invalid_verifiable_chunk"). 219 Str("chunk_fault", chFault.Error()). 220 Msg("chunk fault found, could not verify chunk") 221 return nil 222 case *chmodels.CFInvalidEventsCollection: 223 // TODO raise challenge 224 e.log.Error(). 225 Str("chunk_fault_type", "invalid_event_collection"). 226 Str("chunk_fault", chFault.Error()). 227 Msg("chunk fault found, could not verify chunk") 228 return nil 229 case *chmodels.CFSystemChunkIncludedCollection: 230 e.log.Error(). 231 Str("chunk_fault_type", "system_chunk_includes_collection"). 232 Str("chunk_fault", chFault.Error()). 233 Msg("chunk fault found, could not verify chunk") 234 return nil 235 case *chmodels.CFExecutionDataBlockIDMismatch: 236 e.log.Error(). 237 Str("chunk_fault_type", "execution_data_block_id_mismatch"). 238 Str("chunk_fault", chFault.Error()). 239 Msg("chunk fault found, could not verify chunk") 240 return nil 241 case *chmodels.CFExecutionDataChunksLengthMismatch: 242 e.log.Error(). 243 Str("chunk_fault_type", "execution_data_chunks_count_mismatch"). 244 Str("chunk_fault", chFault.Error()). 245 Msg("chunk fault found, could not verify chunk") 246 return nil 247 case *chmodels.CFExecutionDataInvalidChunkCID: 248 e.log.Error(). 249 Str("chunk_fault_type", "execution_data_chunk_cid_mismatch"). 250 Str("chunk_fault", chFault.Error()). 251 Msg("chunk fault found, could not verify chunk") 252 return nil 253 case *chmodels.CFInvalidExecutionDataID: 254 e.log.Error(). 255 Str("chunk_fault_type", "execution_data_root_cid_mismatch"). 256 Str("chunk_fault", chFault.Error()). 257 Msg("chunk fault found, could not verify chunk") 258 return nil 259 default: 260 return engine.NewInvalidInputErrorf("unknown type of chunk fault is received (type: %T) : %v", 261 chFault, chFault.Error()) 262 } 263 } 264 265 // Generate result approval 266 span, _ = e.tracer.StartSpanFromContext(ctx, trace.VERVerGenerateResultApproval) 267 attestation := &flow.Attestation{ 268 BlockID: vc.Header.ID(), 269 ExecutionResultID: vc.Result.ID(), 270 ChunkIndex: vc.Chunk.Index, 271 } 272 approval, err := GenerateResultApproval( 273 e.me, 274 e.approvalHasher, 275 e.spockHasher, 276 attestation, 277 spockSecret) 278 279 span.End() 280 if err != nil { 281 return fmt.Errorf("couldn't generate a result approval: %w", err) 282 } 283 284 err = e.approvals.Store(approval) 285 if err != nil { 286 return fmt.Errorf("could not store approval: %w", err) 287 } 288 289 err = e.approvals.Index(approval.Body.ExecutionResultID, approval.Body.ChunkIndex, approval.ID()) 290 if err != nil { 291 return fmt.Errorf("could not index approval: %w", err) 292 } 293 294 // Extracting consensus node ids 295 // TODO state extraction should be done based on block references 296 consensusNodes, err := e.state.Final(). 297 Identities(filter.HasRole[flow.Identity](flow.RoleConsensus)) 298 if err != nil { 299 // TODO this error needs more advance handling after MVP 300 return fmt.Errorf("could not load consensus node IDs: %w", err) 301 } 302 303 // broadcast result approval to the consensus nodes 304 err = e.pushConduit.Publish(approval, consensusNodes.NodeIDs()...) 305 if err != nil { 306 // TODO this error needs more advance handling after MVP 307 return fmt.Errorf("could not submit result approval: %w", err) 308 } 309 log.Info().Msg("result approval submitted") 310 // increases number of sent result approvals for sake of metrics 311 e.metrics.OnResultApprovalDispatchedInNetworkByVerifier() 312 313 return nil 314 } 315 316 // GenerateResultApproval generates result approval for specific chunk of an execution receipt. 317 func GenerateResultApproval( 318 me module.Local, 319 approvalHasher hash.Hasher, 320 spockHasher hash.Hasher, 321 attestation *flow.Attestation, 322 spockSecret []byte, 323 ) (*flow.ResultApproval, error) { 324 325 // generates a signature over the attestation part of approval 326 atstID := attestation.ID() 327 atstSign, err := me.Sign(atstID[:], approvalHasher) 328 if err != nil { 329 return nil, fmt.Errorf("could not sign attestation: %w", err) 330 } 331 332 // generates spock 333 spock, err := me.SignFunc(spockSecret, spockHasher, crypto.SPOCKProve) 334 if err != nil { 335 return nil, fmt.Errorf("could not generate SPoCK: %w", err) 336 } 337 338 // result approval body 339 body := flow.ResultApprovalBody{ 340 Attestation: *attestation, 341 ApproverID: me.NodeID(), 342 AttestationSignature: atstSign, 343 Spock: spock, 344 } 345 346 // generates a signature over result approval body 347 bodyID := body.ID() 348 bodySign, err := me.Sign(bodyID[:], approvalHasher) 349 if err != nil { 350 return nil, fmt.Errorf("could not sign result approval body: %w", err) 351 } 352 353 return &flow.ResultApproval{ 354 Body: body, 355 VerifierSignature: bodySign, 356 }, nil 357 } 358 359 // verifiableChunkHandler acts as a wrapper around the verify method that captures its performance-related metrics 360 func (e *Engine) verifiableChunkHandler(originID flow.Identifier, ch *verification.VerifiableChunkData) error { 361 362 span, ctx := e.tracer.StartBlockSpan(context.Background(), ch.Chunk.BlockID, trace.VERVerVerifyWithMetrics) 363 span.SetAttributes( 364 attribute.Int64("chunk_index", int64(ch.Chunk.Index)), 365 attribute.String("result_id", ch.Result.ID().String()), 366 attribute.String("origin_id", originID.String()), 367 ) 368 defer span.End() 369 370 // increments number of received verifiable chunks 371 // for sake of metrics 372 e.metrics.OnVerifiableChunkReceivedAtVerifierEngine() 373 374 log := e.log.With(). 375 Hex("result_id", logging.ID(ch.Result.ID())). 376 Hex("chunk_id", logging.ID(ch.Chunk.ID())). 377 Uint64("chunk_index", ch.Chunk.Index).Logger() 378 379 log.Info().Msg("verifiable chunk received") 380 381 // starts verification of chunk 382 err := e.verify(ctx, originID, ch) 383 384 if err != nil { 385 log.Info().Err(err).Msg("could not verify chunk") 386 } 387 388 // closes verification performance metrics trackers 389 return nil 390 } 391 392 func (e *Engine) approvalRequestHandler(originID flow.Identifier, req *messages.ApprovalRequest) error { 393 394 log := e.log.With(). 395 Hex("origin_id", logging.ID(originID)). 396 Hex("result_id", logging.ID(req.ResultID)). 397 Uint64("chunk_index", req.ChunkIndex). 398 Logger() 399 400 origin, err := e.state.Final().Identity(originID) 401 if err != nil { 402 return engine.NewInvalidInputErrorf("invalid origin id (%s): %w", originID, err) 403 } 404 405 if origin.Role != flow.RoleConsensus { 406 return engine.NewInvalidInputErrorf("invalid role for requesting approvals: %s", origin.Role) 407 } 408 409 approval, err := e.approvals.ByChunk(req.ResultID, req.ChunkIndex) 410 if err != nil { 411 return fmt.Errorf("could not retrieve approval for chunk (result: %s, chunk index: %d): %w", 412 req.ResultID, 413 req.ChunkIndex, 414 err) 415 } 416 417 response := &messages.ApprovalResponse{ 418 Nonce: req.Nonce, 419 Approval: *approval, 420 } 421 422 err = e.pullConduit.Unicast(response, originID) 423 if err != nil { 424 return fmt.Errorf("could not send requested approval to %s: %w", 425 originID, 426 err) 427 } 428 429 log.Debug().Msg("succesfully replied to approval request") 430 431 return nil 432 }