github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/consensus/approvals/aggregated_signatures.go (about) 1 package approvals 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/onflow/flow-go/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 }