github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/approvals/tracker/record.go (about) 1 package tracker 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "fmt" 7 8 "github.com/koko1123/flow-go-1/model/flow" 9 "github.com/koko1123/flow-go-1/storage" 10 ) 11 12 type Rec map[string]interface{} 13 14 // SealingRecord is a record of the sealing status for a specific 15 // incorporated result. It holds information whether the result is sealable, 16 // or what is missing to be sealable. 17 // Not concurrency safe. 18 type SealingRecord struct { 19 *SealingObservation 20 21 // the incorporated result whose sealing status is tracked 22 IncorporatedResult *flow.IncorporatedResult 23 24 // entries holds the individual entries of the sealing record 25 entries Rec 26 } 27 28 func (r *SealingRecord) QualifiesForEmergencySealing(emergencySealable bool) { 29 r.entries["qualifies_for_emergency_sealing"] = emergencySealable 30 } 31 32 func (r *SealingRecord) ApprovalsMissing(chunksWithMissingApprovals map[uint64]flow.IdentifierList) { 33 sufficientApprovals := len(chunksWithMissingApprovals) == 0 34 r.entries["sufficient_approvals_for_sealing"] = sufficientApprovals 35 if !sufficientApprovals { 36 chunksInfo := make([]map[string]interface{}, 0, len(chunksWithMissingApprovals)) 37 for i, list := range chunksWithMissingApprovals { 38 chunk := make(map[string]interface{}) 39 chunk["chunk_index"] = i 40 chunk["missing_approvals_from_verifiers"] = list 41 chunksInfo = append(chunksInfo, chunk) 42 } 43 bytes, err := json.Marshal(chunksInfo) 44 if err != nil { 45 bytes = []byte("failed to marshal data about chunks with missing approvals") 46 } 47 r.entries["chunks_with_insufficient_approvals"] = string(bytes) 48 } 49 } 50 51 func (r *SealingRecord) ApprovalsRequested(requestCount uint) { 52 r.entries["number_requested_approvals"] = requestCount 53 } 54 55 // Generate generates a key-value map capturing the application-submitted data 56 // plus auxiliary data. 57 func (r *SealingRecord) Generate() (Rec, error) { 58 rec := make(Rec) 59 for k, v := range r.entries { 60 rec[k] = v 61 } 62 63 irID := r.IncorporatedResult.ID() 64 result := r.IncorporatedResult.Result 65 resultID := result.ID() 66 executedBlock, err := r.headersDB.ByBlockID(result.BlockID) 67 if err != nil { 68 return nil, fmt.Errorf("failed to retrieve executed block %v: %w", result.BlockID, err) 69 } 70 incorporatingBlock, err := r.headersDB.ByBlockID(r.IncorporatedResult.IncorporatedBlockID) 71 if err != nil { 72 return nil, fmt.Errorf("failed to retrieve incorporating block %v: %w", r.IncorporatedResult.IncorporatedBlockID, err) 73 } 74 initialState, err := result.InitialStateCommit() 75 if err != nil { 76 return nil, fmt.Errorf("failed to retrieve initial state from result %v: %w", resultID, err) 77 } 78 finalizationStatus, err := r.assignmentFinalizationStatus(incorporatingBlock) 79 if err != nil { 80 return nil, fmt.Errorf("failed to determine finalization status of incorporating block %v: %w", r.IncorporatedResult.IncorporatedBlockID, err) 81 } 82 numberReceipts, err := numberExecutionReceipts(r.receiptsDB, resultID, r.IncorporatedResult.Result.BlockID) 83 if err != nil { 84 return nil, fmt.Errorf("failed to determine whether result %v has multiple receipt: %w", resultID, err) 85 } 86 87 rec["executed_block_id"] = result.BlockID.String() 88 rec["executed_block_height"] = executedBlock.Height 89 rec["result_id"] = resultID.String() 90 rec["result_incorporated_at_height"] = incorporatingBlock.Height 91 rec["incorporated_result_id"] = irID.String() 92 rec["result_initial_state"] = hex.EncodeToString(initialState[:]) 93 rec["number_chunks"] = len(result.Chunks) 94 rec["number_receipts"] = numberReceipts 95 _, rec["candidate_seal_in_mempool"] = r.sealsPl.ByID(irID) 96 97 if finalizationStatus != nil { 98 rec["incorporating_block"] = *finalizationStatus 99 } 100 101 return rec, nil 102 } 103 104 // assignmentFinalizationStatus check whether the verifier assignment is finalized. 105 // This information can only be obtained without traversing the forks, if the result 106 // is incorporated at a height that was already finalized. 107 // Convention for return values: 108 // - nil if result is incorporated at an unfinalized height 109 // - "finalized" if result is incorporated at a finalized block 110 // - "orphaned" if result is incorporated in an orphaned block 111 func (r *SealingRecord) assignmentFinalizationStatus(incorporatingBlock *flow.Header) (*string, error) { 112 if incorporatingBlock.Height > r.finalizedBlock.Height { 113 return nil, nil // result is incorporated at an unfinalized height. 114 } 115 finalizedBlockAtSameHeight, err := r.headersDB.ByHeight(incorporatingBlock.Height) 116 if err != nil { 117 return nil, fmt.Errorf("failed to retrieve incorporating block %v: %w", r.IncorporatedResult.IncorporatedBlockID, err) 118 } 119 var stat string 120 if finalizedBlockAtSameHeight.ID() == r.IncorporatedResult.IncorporatedBlockID { 121 stat = "finalized" 122 } else { 123 stat = "orphaned" 124 } 125 return &stat, nil 126 } 127 128 // numberExecutionReceipts determines how many receipts from _different_ ENs are committing to this result. 129 func numberExecutionReceipts(receiptsDB storage.ExecutionReceipts, resultID, executedBlockID flow.Identifier) (int, error) { 130 // get all receipts that are known for the block 131 receipts, err := receiptsDB.ByBlockID(executedBlockID) 132 if err != nil { 133 return -1, fmt.Errorf("internal error querying receipts for block %v: %w", executedBlockID, err) 134 } 135 136 // Index receipts for given incorporatedResult by their executor. In case 137 // there are multiple receipts from the same executor, we keep the last one. 138 receiptsForIncorporatedResults := receipts.GroupByResultID().GetGroup(resultID) 139 return receiptsForIncorporatedResults.GroupByExecutorID().NumberGroups(), nil 140 }