github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/approvals/verifying_assignment_collector.go (about) 1 package approvals 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/rs/zerolog" 8 "github.com/rs/zerolog/log" 9 10 "github.com/onflow/flow-go/engine" 11 "github.com/onflow/flow-go/engine/consensus" 12 "github.com/onflow/flow-go/model/flow" 13 "github.com/onflow/flow-go/model/flow/filter" 14 "github.com/onflow/flow-go/model/messages" 15 "github.com/onflow/flow-go/module/mempool" 16 "github.com/onflow/flow-go/state/protocol" 17 "github.com/onflow/flow-go/utils/rand" 18 ) 19 20 // **Emergency-sealing parameters** 21 22 // DefaultEmergencySealingThresholdForFinalization is the minimal number of unsealed but finalized descendants that a 23 // block must have in order to be eligible for emergency sealing (further conditions apply for emergency sealing). 24 const DefaultEmergencySealingThresholdForFinalization = 100 25 26 // DefaultEmergencySealingThresholdForVerification is the minimal number of finalized descendants 27 // that the block _incorporating_ an Execution Result [ER] must have for the ER to be eligible for 28 // emergency sealing (further conditions apply for emergency sealing). 29 const DefaultEmergencySealingThresholdForVerification = 25 30 31 // VerifyingAssignmentCollector 32 // Context: 33 // - When the same result is incorporated in multiple different forks, 34 // unique verifier assignment is determined for each fork. 35 // - The assignment collector is intended to encapsulate the known 36 // assignments for a particular execution result. 37 // 38 // VerifyingAssignmentCollector has a strict ordering of processing, before processing 39 // approvals at least one incorporated result has to be processed. 40 // VerifyingAssignmentCollector takes advantage of internal caching to speed up processing approvals for different assignments 41 // VerifyingAssignmentCollector is responsible for validating approvals on result-level (checking signature, identity). 42 type VerifyingAssignmentCollector struct { 43 AssignmentCollectorBase 44 45 log zerolog.Logger 46 lock sync.RWMutex 47 collectors map[flow.Identifier]*ApprovalCollector // collectors is a mapping IncorporatedBlockID -> ApprovalCollector 48 authorizedApprovers map[flow.Identifier]*flow.Identity // map of approvers pre-selected at block that is being sealed 49 verifiedApprovalsCache *ApprovalsCache // in-memory cache of approvals (already verified) 50 } 51 52 // NewVerifyingAssignmentCollector instantiates a new VerifyingAssignmentCollector. 53 // All errors are unexpected and potential symptoms of internal bugs or state corruption (fatal). 54 func NewVerifyingAssignmentCollector(collectorBase AssignmentCollectorBase) (*VerifyingAssignmentCollector, error) { 55 // pre-select all authorized verifiers at the block that is being sealed 56 authorizedApprovers, err := authorizedVerifiersAtBlock(collectorBase.state, collectorBase.BlockID()) 57 if err != nil { 58 return nil, fmt.Errorf("could not determine authorized verifiers for sealing candidate: %w", err) 59 } 60 numberChunks := collectorBase.result.Chunks.Len() 61 62 return &VerifyingAssignmentCollector{ 63 AssignmentCollectorBase: collectorBase, 64 log: collectorBase.log.With().Str("component", "verifying_assignment_collector").Logger(), 65 lock: sync.RWMutex{}, 66 collectors: make(map[flow.Identifier]*ApprovalCollector), 67 authorizedApprovers: authorizedApprovers, 68 verifiedApprovalsCache: NewApprovalsCache(uint(numberChunks * len(authorizedApprovers))), 69 }, nil 70 } 71 72 func (ac *VerifyingAssignmentCollector) collectorByBlockID(incorporatedBlockID flow.Identifier) *ApprovalCollector { 73 ac.lock.RLock() 74 defer ac.lock.RUnlock() 75 return ac.collectors[incorporatedBlockID] 76 } 77 78 // emergencySealable determines whether an incorporated Result qualifies for "emergency sealing". 79 // ATTENTION: this is a temporary solution, which is NOT BFT compatible. When the approval process 80 // hangs far enough behind finalization (measured in finalized but unsealed blocks), emergency 81 // sealing kicks in. This will be removed when implementation of Sealing & Verification is finished. 82 func (ac *VerifyingAssignmentCollector) emergencySealable(collector *ApprovalCollector, finalizedBlockHeight uint64) bool { 83 // Criterion for emergency sealing, both of the following condition need to be true for trigger emergency sealing: 84 // 1. There must be at least DefaultEmergencySealingThresholdForFinalization number of blocks between 85 // the executed block and the latest finalized block 86 // 2. there must be at least DefaultEmergencySealingThresholdForVerification number of blocks between 87 // the block that _incorporates_ result and the latest finalized block 88 return collector.executedBlock.Height+DefaultEmergencySealingThresholdForFinalization <= finalizedBlockHeight && 89 collector.IncorporatedBlock().Height+DefaultEmergencySealingThresholdForVerification <= finalizedBlockHeight 90 } 91 92 // CheckEmergencySealing checks the managed assignments whether their result can be emergency 93 // sealed. Seals the results where possible. 94 // It returns error when running into any exception 95 // It returns nil when it's done the checking regardless whether there is any results being emergency sealed or not 96 func (ac *VerifyingAssignmentCollector) CheckEmergencySealing(observer consensus.SealingObservation, finalizedBlockHeight uint64) error { 97 for _, collector := range ac.allCollectors() { 98 sealable := ac.emergencySealable(collector, finalizedBlockHeight) 99 observer.QualifiesForEmergencySealing(collector.IncorporatedResult(), sealable) 100 if sealable { 101 err := collector.SealResult() 102 if err != nil { 103 return fmt.Errorf("could not create emergency seal for result %x incorporated at %x: %w", 104 ac.ResultID(), collector.IncorporatedBlockID(), err) 105 } 106 } 107 } 108 109 return nil 110 } 111 112 func (ac *VerifyingAssignmentCollector) ProcessingStatus() ProcessingStatus { 113 return VerifyingApprovals 114 } 115 116 // ProcessIncorporatedResult starts tracking the approval for IncorporatedResult. 117 // Method is idempotent. 118 // Error Returns: 119 // - no errors expected during normal operation; 120 // errors might be symptoms of bugs or internal state corruption (fatal) 121 func (ac *VerifyingAssignmentCollector) ProcessIncorporatedResult(incorporatedResult *flow.IncorporatedResult) error { 122 ac.log.Debug(). 123 Str("result_id", incorporatedResult.Result.ID().String()). 124 Str("incorporated_block_id", incorporatedResult.IncorporatedBlockID.String()). 125 Str("block_id", incorporatedResult.Result.BlockID.String()). 126 Msg("processing incorporated result") 127 128 // check that result is the one that this VerifyingAssignmentCollector manages 129 if irID := incorporatedResult.Result.ID(); irID != ac.ResultID() { 130 return fmt.Errorf("this VerifyingAssignmentCollector manages result %x but got %x", ac.ResultID(), irID) 131 } 132 133 // NoOp, if we already have a collector for this incorporatedResult 134 incorporatedBlockID := incorporatedResult.IncorporatedBlockID 135 if collector := ac.collectorByBlockID(incorporatedBlockID); collector != nil { 136 return nil 137 } 138 139 // Constructing ApprovalCollector for IncorporatedResult 140 // The VerifyingAssignmentCollector is not locked while instantiating the ApprovalCollector. Hence, it is possible that 141 // multiple threads simultaneously compute the verifier assignment. Nevertheless, the implementation is safe in 142 // that only one of the instantiated ApprovalCollectors will be stored in the cache. In terms of locking duration, 143 // it's better to perform extra computation in edge cases than lock this logic with a mutex, 144 // since it's quite unlikely that same incorporated result will be processed by multiple goroutines simultaneously. 145 assignment, err := ac.assigner.Assign(incorporatedResult.Result, incorporatedBlockID) 146 if err != nil { 147 return fmt.Errorf("could not determine chunk assignment: %w", err) 148 } 149 incorporatedBlock, err := ac.headers.ByBlockID(incorporatedBlockID) 150 if err != nil { 151 return fmt.Errorf("failed to retrieve header of incorporated block %s: %w", 152 incorporatedBlockID, err) 153 } 154 executedBlock, err := ac.headers.ByBlockID(incorporatedResult.Result.BlockID) 155 if err != nil { 156 return fmt.Errorf("failed to retrieve header of incorporatedResult %s: %w", 157 incorporatedResult.Result.BlockID, err) 158 } 159 collector, err := NewApprovalCollector(ac.log, incorporatedResult, incorporatedBlock, executedBlock, assignment, ac.seals, ac.requiredApprovalsForSealConstruction) 160 if err != nil { 161 return fmt.Errorf("instantiation of ApprovalCollector failed: %w", err) 162 } 163 164 // Now, we add the ApprovalCollector to the VerifyingAssignmentCollector: 165 // no-op if an ApprovalCollector has already been added by a different routine 166 isDuplicate := ac.putCollector(incorporatedBlockID, collector) 167 if isDuplicate { 168 return nil 169 } 170 171 // process approvals that have passed needed checks and are ready to be processed 172 for _, approval := range ac.verifiedApprovalsCache.All() { 173 // those approvals are verified already and shouldn't yield any errors 174 err = collector.ProcessApproval(approval) 175 if err != nil { 176 return fmt.Errorf("processing already validated approval %x failed: %w", approval.ID(), err) 177 } 178 } 179 180 return nil 181 } 182 183 // putCollector stores the collector if it is not already present in the collectors map 184 // and returns false (no duplicate). NoOp if a collector for the incorporatedBlockID is 185 // already stored, in which case true is returned (indicating a duplicate). 186 func (ac *VerifyingAssignmentCollector) putCollector(incorporatedBlockID flow.Identifier, collector *ApprovalCollector) bool { 187 ac.lock.Lock() 188 defer ac.lock.Unlock() 189 if _, ok := ac.collectors[incorporatedBlockID]; ok { 190 return true 191 } 192 ac.collectors[incorporatedBlockID] = collector 193 return false 194 } 195 196 func (ac *VerifyingAssignmentCollector) allCollectors() []*ApprovalCollector { 197 ac.lock.RLock() 198 defer ac.lock.RUnlock() 199 collectors := make([]*ApprovalCollector, 0, len(ac.collectors)) 200 for _, collector := range ac.collectors { 201 collectors = append(collectors, collector) 202 } 203 return collectors 204 } 205 206 func (ac *VerifyingAssignmentCollector) verifyAttestationSignature(approval *flow.ResultApprovalBody, nodeIdentity *flow.Identity) error { 207 id := approval.Attestation.ID() 208 valid, err := nodeIdentity.StakingPubKey.Verify(approval.AttestationSignature, id[:], ac.sigHasher) 209 if err != nil { 210 return fmt.Errorf("failed to verify attestation signature: %w", err) 211 } 212 213 if !valid { 214 return engine.NewInvalidInputErrorf("invalid attestation signature for (%x)", nodeIdentity.NodeID) 215 } 216 217 return nil 218 } 219 220 func (ac *VerifyingAssignmentCollector) verifySignature(approval *flow.ResultApproval, nodeIdentity *flow.Identity) error { 221 id := approval.Body.ID() 222 valid, err := nodeIdentity.StakingPubKey.Verify(approval.VerifierSignature, id[:], ac.sigHasher) 223 if err != nil { 224 return fmt.Errorf("failed to verify approval signature: %w", err) 225 } 226 227 if !valid { 228 return engine.NewInvalidInputErrorf("invalid signature for (%x)", nodeIdentity.NodeID) 229 } 230 231 return nil 232 } 233 234 // validateApproval performs result level checks of flow.ResultApproval 235 // checks: 236 // - verification node identity 237 // - attestation signature 238 // - signature of verification node 239 // - chunk index sanity check 240 // - block ID sanity check 241 // Returns: 242 // - engine.InvalidInputError - result approval is invalid 243 // - exception in case of any other error, usually this is not expected 244 // - nil on successful check 245 func (ac *VerifyingAssignmentCollector) validateApproval(approval *flow.ResultApproval) error { 246 // check that approval is for the expected result to reject incompatible inputs 247 if approval.Body.ExecutionResultID != ac.ResultID() { 248 return fmt.Errorf("AssignmentCollector processes only approvals for result (%x) but got one for (%x)", ac.ResultID(), approval.Body.ExecutionResultID) 249 } 250 251 // approval has to refer same block as execution result 252 if approval.Body.BlockID != ac.BlockID() { 253 return engine.NewInvalidInputErrorf("result approval for invalid block, expected (%x) vs (%x)", 254 ac.BlockID(), approval.Body.BlockID) 255 } 256 257 chunkIndex := approval.Body.ChunkIndex 258 if chunkIndex >= uint64(ac.result.Chunks.Len()) { 259 return engine.NewInvalidInputErrorf("chunk index out of range: %v", chunkIndex) 260 } 261 262 identity, found := ac.authorizedApprovers[approval.Body.ApproverID] 263 if !found { 264 return engine.NewInvalidInputErrorf("approval not from authorized verifier") 265 } 266 267 err := ac.verifyAttestationSignature(&approval.Body, identity) 268 if err != nil { 269 return fmt.Errorf("validating attestation signature failed: %w", err) 270 } 271 272 err = ac.verifySignature(approval, identity) 273 if err != nil { 274 return fmt.Errorf("validating approval signature failed: %w", err) 275 } 276 277 return nil 278 } 279 280 // ProcessApproval ingests Result Approvals and triggers sealing of execution result 281 // when sufficient approvals have arrived. 282 // Error Returns: 283 // - nil in case of success (outdated approvals might be silently discarded) 284 // - engine.InvalidInputError if the result approval is invalid 285 // - any other errors might be symptoms of bugs or internal state corruption (fatal) 286 func (ac *VerifyingAssignmentCollector) ProcessApproval(approval *flow.ResultApproval) error { 287 ac.log.Debug(). 288 Str("result_id", approval.Body.ExecutionResultID.String()). 289 Str("verifier_id", approval.Body.ApproverID.String()). 290 Msg("processing result approval") 291 292 // we have this approval cached already, no need to process it again 293 // here we need to use PartialID to have a hash over Attestation + ApproverID 294 // there is no need to use hash over full approval since it contains extra information 295 // and we are only interested in approval body. 296 approvalCacheID := approval.Body.PartialID() 297 if cached := ac.verifiedApprovalsCache.Get(approvalCacheID); cached != nil { 298 return nil 299 } 300 301 err := ac.validateApproval(approval) 302 if err != nil { 303 return fmt.Errorf("could not validate approval: %w", err) 304 } 305 306 newlyAdded := ac.verifiedApprovalsCache.Put(approvalCacheID, approval) 307 if !newlyAdded { 308 return nil 309 } 310 311 for _, collector := range ac.allCollectors() { 312 // approvals are verified already and shouldn't yield any errors 313 err := collector.ProcessApproval(approval) 314 if err != nil { 315 return fmt.Errorf("could not process approval: %w", err) 316 } 317 } 318 319 return nil 320 } 321 322 // RequestMissingApprovals traverses all collectors and requests missing approval 323 // for every chunk that didn't get enough approvals from verifiers. 324 // Returns number of requests made and error in case something goes wrong. 325 func (ac *VerifyingAssignmentCollector) RequestMissingApprovals(observation consensus.SealingObservation, maxHeightForRequesting uint64) (uint, error) { 326 overallRequestCount := uint(0) // number of approval requests for all different assignments for this result 327 for _, collector := range ac.allCollectors() { 328 if collector.IncorporatedBlock().Height > maxHeightForRequesting { 329 continue 330 } 331 332 missingChunks := collector.CollectMissingVerifiers() 333 observation.ApprovalsMissing(collector.IncorporatedResult(), missingChunks) 334 requestCount := uint(0) 335 for chunkIndex, verifiers := range missingChunks { 336 // Retrieve information about requests made for this chunk. Skip 337 // requesting if the blackout period hasn't expired. Otherwise, 338 // update request count and reset blackout period. 339 requestTrackerItem, updated, err := ac.requestTracker.TryUpdate(ac.result, collector.IncorporatedBlockID(), chunkIndex) 340 if err != nil { 341 // it could happen that other gorotuine will prune request tracker because of sealing progress 342 // in this case we should just stop requesting approvals as block was already sealed 343 if mempool.IsBelowPrunedThresholdError(err) { 344 return 0, nil 345 } 346 return 0, err 347 } 348 if !updated { 349 continue 350 } 351 352 // for monitoring/debugging purposes, log requests if we start 353 // making more than 10 354 if requestTrackerItem.Requests >= 10 { 355 log.Debug().Msgf("requesting approvals for result %v, incorporatedBlockID %v chunk %d: %d requests", 356 ac.ResultID(), 357 collector.IncorporatedBlockID(), 358 chunkIndex, 359 requestTrackerItem.Requests, 360 ) 361 } 362 363 nonce, err := rand.Uint64() 364 if err != nil { 365 return 0, fmt.Errorf("nonce generation failed during request missing approvals: %w", err) 366 } 367 368 // prepare the request 369 req := &messages.ApprovalRequest{ 370 Nonce: nonce, 371 ResultID: ac.ResultID(), 372 ChunkIndex: chunkIndex, 373 } 374 375 requestCount++ 376 err = ac.approvalConduit.Publish(req, verifiers...) 377 if err != nil { 378 log.Error().Err(err). 379 Msgf("could not publish approval request for chunk %d", chunkIndex) 380 } 381 } 382 383 observation.ApprovalsRequested(collector.IncorporatedResult(), requestCount) 384 overallRequestCount += requestCount 385 } 386 387 return overallRequestCount, nil 388 } 389 390 // authorizedVerifiersAtBlock pre-select all authorized Verifiers at the block that incorporates the result. 391 // The method returns the set of all node IDs that: 392 // - are authorized members of the network at the given block and 393 // - have the Verification role and 394 // - have _positive_ weight and 395 // - are not ejected 396 func authorizedVerifiersAtBlock(state protocol.State, blockID flow.Identifier) (map[flow.Identifier]*flow.Identity, error) { 397 authorizedVerifierList, err := state.AtBlockID(blockID).Identities( 398 filter.And( 399 filter.HasRole[flow.Identity](flow.RoleVerification), 400 filter.HasInitialWeight[flow.Identity](true), 401 filter.IsValidCurrentEpochParticipant, 402 )) 403 if err != nil { 404 return nil, fmt.Errorf("failed to retrieve Identities for block %v: %w", blockID, err) 405 } 406 if len(authorizedVerifierList) == 0 { 407 return nil, fmt.Errorf("no authorized verifiers found for block %v", blockID) 408 } 409 410 return authorizedVerifierList.Lookup(), nil 411 }