github.com/koko1123/flow-go-1@v0.29.6/engine/consensus/approvals/aggregated_signatures.go (about)

     1  package approvals
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/koko1123/flow-go-1/model/flow"
     8  )
     9  
    10  // AggregatedSignatures is an utility struct that provides concurrency safe access
    11  // to map of aggregated signatures indexed by chunk index
    12  type AggregatedSignatures struct {
    13  	signatures     map[uint64]flow.AggregatedSignature // aggregated signature for each chunk
    14  	lock           sync.RWMutex                        // lock for modifying aggregatedSignatures
    15  	numberOfChunks uint64
    16  }
    17  
    18  // NewAggregatedSignatures instantiates a AggregatedSignatures. Requires that
    19  // number of chunks is positive integer. Errors otherwise.
    20  func NewAggregatedSignatures(chunks uint64) (*AggregatedSignatures, error) {
    21  	if chunks < 1 {
    22  		return nil, fmt.Errorf("number of chunks must be positive but got %d", chunks)
    23  	}
    24  	return &AggregatedSignatures{
    25  		signatures:     make(map[uint64]flow.AggregatedSignature, chunks),
    26  		lock:           sync.RWMutex{},
    27  		numberOfChunks: chunks,
    28  	}, nil
    29  }
    30  
    31  // PutSignature adds the AggregatedSignature from the collector to `aggregatedSignatures`.
    32  // The returned int is the resulting number of approved chunks.
    33  // Errors if chunk index exceeds valid range.
    34  func (as *AggregatedSignatures) PutSignature(chunkIndex uint64, aggregatedSignature flow.AggregatedSignature) (uint64, error) {
    35  	if chunkIndex >= as.numberOfChunks {
    36  		return uint64(len(as.signatures)), fmt.Errorf("chunk index must be in range [0, %d] but is %d", as.numberOfChunks-1, chunkIndex)
    37  	}
    38  
    39  	as.lock.Lock()
    40  	defer as.lock.Unlock()
    41  	if _, found := as.signatures[chunkIndex]; !found {
    42  		as.signatures[chunkIndex] = aggregatedSignature
    43  	}
    44  	return uint64(len(as.signatures)), nil
    45  }
    46  
    47  // HasSignature returns boolean depending if we have signature for particular chunk
    48  func (as *AggregatedSignatures) HasSignature(chunkIndex uint64) bool {
    49  	as.lock.RLock()
    50  	defer as.lock.RUnlock()
    51  	_, found := as.signatures[chunkIndex]
    52  	return found
    53  }
    54  
    55  // Collect returns array with aggregated signature for each chunk
    56  func (as *AggregatedSignatures) Collect() []flow.AggregatedSignature {
    57  	aggregatedSigs := make([]flow.AggregatedSignature, as.numberOfChunks)
    58  
    59  	as.lock.RLock()
    60  	defer as.lock.RUnlock()
    61  	for chunkIndex, sig := range as.signatures {
    62  		aggregatedSigs[chunkIndex] = sig
    63  	}
    64  
    65  	return aggregatedSigs
    66  }
    67  
    68  // ChunksWithoutAggregatedSignature returns indexes of chunks that don't have an aggregated signature
    69  func (as *AggregatedSignatures) ChunksWithoutAggregatedSignature() []uint64 {
    70  	// provide enough capacity to avoid allocations while we hold the lock
    71  	missingChunks := make([]uint64, 0, as.numberOfChunks)
    72  	as.lock.RLock()
    73  	defer as.lock.RUnlock()
    74  	for i := uint64(0); i < as.numberOfChunks; i++ {
    75  		chunkIndex := uint64(i)
    76  		if _, found := as.signatures[chunkIndex]; found {
    77  			// skip if we already have enough valid approvals for this chunk
    78  			continue
    79  		}
    80  		missingChunks = append(missingChunks, chunkIndex)
    81  	}
    82  	return missingChunks
    83  }