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  }