github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/approvals/approval_collector.go (about) 1 package approvals 2 3 import ( 4 "fmt" 5 6 "github.com/rs/zerolog" 7 8 "github.com/koko1123/flow-go-1/engine" 9 "github.com/koko1123/flow-go-1/model/chunks" 10 "github.com/koko1123/flow-go-1/model/flow" 11 "github.com/koko1123/flow-go-1/module/mempool" 12 ) 13 14 // ApprovalCollector is responsible for distributing work to chunk collectorTree, 15 // collecting aggregated signatures for chunks that reached seal construction threshold, 16 // creating and submitting seal candidates once signatures for every chunk are aggregated. 17 type ApprovalCollector struct { 18 log zerolog.Logger 19 incorporatedBlock *flow.Header // block that incorporates execution result 20 executedBlock *flow.Header // block that was executed 21 incorporatedResult *flow.IncorporatedResult // incorporated result that is being sealed 22 chunkCollectors []*ChunkApprovalCollector // slice of chunk collectorTree that is created on construction and doesn't change 23 aggregatedSignatures *AggregatedSignatures // aggregated signature for each chunk 24 seals mempool.IncorporatedResultSeals // holds candidate seals for incorporated results that have acquired sufficient approvals; candidate seals are constructed without consideration of the sealability of parent results 25 numberOfChunks uint64 // number of chunks for execution result, remains constant 26 } 27 28 func NewApprovalCollector( 29 log zerolog.Logger, 30 result *flow.IncorporatedResult, 31 incorporatedBlock *flow.Header, 32 executedBlock *flow.Header, 33 assignment *chunks.Assignment, 34 seals mempool.IncorporatedResultSeals, 35 requiredApprovalsForSealConstruction uint, 36 ) (*ApprovalCollector, error) { 37 chunkCollectors := make([]*ChunkApprovalCollector, 0, result.Result.Chunks.Len()) 38 for _, chunk := range result.Result.Chunks { 39 chunkAssignment := assignment.Verifiers(chunk).Lookup() 40 collector := NewChunkApprovalCollector(chunkAssignment, requiredApprovalsForSealConstruction) 41 chunkCollectors = append(chunkCollectors, collector) 42 } 43 44 numberOfChunks := uint64(result.Result.Chunks.Len()) 45 aggSigs, err := NewAggregatedSignatures(numberOfChunks) 46 if err != nil { 47 return nil, fmt.Errorf("instantiation of AggregatedSignatures failed: %w", err) 48 } 49 collector := ApprovalCollector{ 50 log: log.With(). 51 Str("component", "approval_collector"). 52 Str("incorporated_block", incorporatedBlock.ID().String()). 53 Str("executed_block", executedBlock.ID().String()). 54 Logger(), 55 incorporatedResult: result, 56 incorporatedBlock: incorporatedBlock, 57 executedBlock: executedBlock, 58 numberOfChunks: numberOfChunks, 59 chunkCollectors: chunkCollectors, 60 aggregatedSignatures: aggSigs, 61 seals: seals, 62 } 63 64 // The following code implements a TEMPORARY SHORTCUT: In case no approvals are required 65 // to seal an incorporated result, we seal right away when creating the ApprovalCollector. 66 if requiredApprovalsForSealConstruction == 0 { 67 // The high-level logic is: as soon as we have collected enough approvals, we aggregate 68 // them and store them in collector.aggregatedSignatures. If we don't require any signatures, 69 // this condition is satisfied right away. Hence, we add aggregated signature for each chunk. 70 for i := uint64(0); i < numberOfChunks; i++ { 71 _, err := collector.aggregatedSignatures.PutSignature(i, flow.AggregatedSignature{}) 72 if err != nil { 73 return nil, fmt.Errorf("sealing result %x failed: %w", result.ID(), err) 74 } 75 } 76 err := collector.SealResult() 77 if err != nil { 78 return nil, fmt.Errorf("sealing result %x failed: %w", result.ID(), err) 79 } 80 } 81 82 return &collector, nil 83 } 84 85 // IncorporatedBlockID returns the ID of block which incorporates execution result 86 func (c *ApprovalCollector) IncorporatedBlockID() flow.Identifier { 87 return c.incorporatedResult.IncorporatedBlockID 88 } 89 90 // IncorporatedBlock returns the block which incorporates execution result 91 func (c *ApprovalCollector) IncorporatedBlock() *flow.Header { 92 return c.incorporatedBlock 93 } 94 95 // IncorporatedResult returns the incorporated Result this ApprovalCollector is for 96 func (c *ApprovalCollector) IncorporatedResult() *flow.IncorporatedResult { 97 return c.incorporatedResult 98 } 99 100 func (c *ApprovalCollector) SealResult() error { 101 // get final state of execution result 102 finalState, err := c.incorporatedResult.Result.FinalStateCommitment() 103 if err != nil { 104 // message correctness should have been checked before: failure here is an internal implementation bug 105 return fmt.Errorf("failed to get final state commitment from Execution Result: %w", err) 106 } 107 108 // TODO: Check SPoCK proofs 109 110 // generate & store seal 111 seal := &flow.Seal{ 112 BlockID: c.incorporatedResult.Result.BlockID, 113 ResultID: c.incorporatedResult.Result.ID(), 114 FinalState: finalState, 115 AggregatedApprovalSigs: c.aggregatedSignatures.Collect(), 116 } 117 118 // Adding a seal that already exists in the mempool is a NoOp. But to reduce log 119 // congestion, we only log when a seal is added that previously did not exist. 120 added, err := c.seals.Add(&flow.IncorporatedResultSeal{ 121 IncorporatedResult: c.incorporatedResult, 122 Seal: seal, 123 Header: c.executedBlock, 124 }) 125 if err != nil { 126 return fmt.Errorf("failed to store IncorporatedResultSeal in mempool: %w", err) 127 } 128 if added { 129 c.log.Info(). 130 Str("executed_block_id", seal.BlockID.String()). 131 Uint64("executed_block_height", c.executedBlock.Height). 132 Str("result_id", seal.ResultID.String()). 133 Str("incorporating_block", c.IncorporatedBlockID().String()). 134 Msg("added candidate seal to IncorporatedResultSeals mempool") 135 } 136 return nil 137 } 138 139 // ProcessApproval performs processing of result approvals and bookkeeping of aggregated signatures 140 // for every chunk. Triggers sealing of execution result when processed last result approval needed for sealing. 141 // Returns: 142 // - engine.InvalidInputError - result approval is invalid 143 // - exception in case of any other error, usually this is not expected 144 // - nil on success 145 func (c *ApprovalCollector) ProcessApproval(approval *flow.ResultApproval) error { 146 c.log.Debug(). 147 Str("result_id", approval.Body.ExecutionResultID.String()). 148 Str("verifier_id", approval.Body.ApproverID.String()). 149 Msg("processing result approval") 150 151 chunkIndex := approval.Body.ChunkIndex 152 if chunkIndex >= uint64(len(c.chunkCollectors)) { 153 return engine.NewInvalidInputErrorf("approval collector chunk index out of range: %v", chunkIndex) 154 } 155 // there is no need to process approval if we have already enough info for sealing 156 if c.aggregatedSignatures.HasSignature(chunkIndex) { 157 return nil 158 } 159 160 collector := c.chunkCollectors[chunkIndex] 161 aggregatedSignature, collected := collector.ProcessApproval(approval) 162 if !collected { 163 return nil 164 } 165 166 approvedChunks, err := c.aggregatedSignatures.PutSignature(chunkIndex, aggregatedSignature) 167 if err != nil { 168 return fmt.Errorf("adding aggregated signature failed: %w", err) 169 } 170 if approvedChunks < c.numberOfChunks { 171 return nil // still missing approvals for some chunks 172 } 173 174 return c.SealResult() 175 } 176 177 // CollectMissingVerifiers collects ids of verifiers who haven't provided an approval for particular chunk 178 // Returns: map { ChunkIndex -> []VerifierId } 179 func (c *ApprovalCollector) CollectMissingVerifiers() map[uint64]flow.IdentifierList { 180 targetIDs := make(map[uint64]flow.IdentifierList) 181 for _, chunkIndex := range c.aggregatedSignatures.ChunksWithoutAggregatedSignature() { 182 missingSigners := c.chunkCollectors[chunkIndex].GetMissingSigners() 183 if missingSigners.Len() > 0 { 184 targetIDs[chunkIndex] = missingSigners 185 } 186 } 187 188 return targetIDs 189 }