@@ -19,26 +19,24 @@ type signerInfo struct {
1919 index int
2020}
2121
22- // WeightedSignatureAggregator implements consensus/ hotstuff.WeightedSignatureAggregator.
23- // It is a wrapper around module/ signature.SignatureAggregatorSameMessage, which implements a
22+ // WeightedSignatureAggregator implements [ hotstuff.WeightedSignatureAggregator] .
23+ // It is a wrapper around [ signature.SignatureAggregatorSameMessage], implementing a
2424// mapping from node IDs (as used by HotStuff) to index-based addressing of authorized
25- // signers (as used by SignatureAggregatorSameMessage).
25+ // signers (as used by [signature. SignatureAggregatorSameMessage] ).
2626//
27- // Similarly to module/signature.SignatureAggregatorSameMessage, this module assumes proofs of possession (PoP)
28- // of all identity public keys are valid.
27+ // We delegate the handling of duplicate signatures to the underlying [signature.SignatureAggregatorSameMessage].
28+ // NOTE: This is possible, because [signature.SignatureAggregatorSameMessage] does not support signatures with
29+ // multiplicity higher than 1, i.e. each signer is allowed to sign at most once. Should this constraint every be
30+ // changed in [signature.SignatureAggregatorSameMessage], this module would need to be updated accordingly.
31+ //
32+ // Similarly to [signature.SignatureAggregatorSameMessage], this module assumes proofs
33+ // of possession (PoP) of all identity public keys are valid.
2934type WeightedSignatureAggregator struct {
3035 aggregator * signature.SignatureAggregatorSameMessage // low level crypto BLS aggregator, agnostic of weights and flow IDs
3136 ids flow.IdentityList // all possible ids (only gets updated by constructor)
3237 idToInfo map [flow.Identifier ]signerInfo // auxiliary map to lookup signer weight and index by ID (only gets updated by constructor)
3338 totalWeight uint64 // weight collected (gets updated)
3439 lock sync.RWMutex // lock for atomic updates to totalWeight and collectedIDs
35-
36- // collectedIDs tracks the Identities of all nodes whose signatures have been collected so far.
37- // The reason for tracking the duplicate signers at this module level is that having no duplicates
38- // is a Hotstuff constraint, rather than a cryptographic aggregation constraint. We are planning to
39- // extend the cryptographic primitives to support multiplicity higher than 1 in the future.
40- // Therefore, we already add the logic for identifying duplicates here.
41- collectedIDs map [flow.Identifier ]struct {} // map of collected IDs (gets updated)
4240}
4341
4442var _ hotstuff.WeightedSignatureAggregator = (* WeightedSignatureAggregator )(nil )
@@ -81,17 +79,16 @@ func NewWeightedSignatureAggregator(
8179 }
8280
8381 return & WeightedSignatureAggregator {
84- aggregator : agg ,
85- ids : ids ,
86- idToInfo : idToInfo ,
87- collectedIDs : make (map [flow.Identifier ]struct {}),
82+ aggregator : agg ,
83+ ids : ids ,
84+ idToInfo : idToInfo ,
8885 }, nil
8986}
9087
9188// Verify verifies the signature under the stored public keys and message.
9289// Expected errors during normal operations:
93- // - model.InvalidSignerError if signerID is invalid (not a consensus participant)
94- // - model.ErrInvalidSignature if signerID is valid but signature is cryptographically invalid
90+ // - [ model.InvalidSignerError] if signerID is invalid (not a consensus participant)
91+ // - [ model.ErrInvalidSignature] if signerID is valid but signature is cryptographically invalid
9592//
9693// The function is thread-safe.
9794func (w * WeightedSignatureAggregator ) Verify (signerID flow.Identifier , sig crypto.Signature ) error {
@@ -116,8 +113,8 @@ func (w *WeightedSignatureAggregator) Verify(signerID flow.Identifier, sig crypt
116113// The total weight of all collected signatures (excluding duplicates) is returned regardless
117114// of any returned error.
118115// The function errors with:
119- // - model.InvalidSignerError if signerID is invalid (not a consensus participant)
120- // - model.DuplicatedSignerError if the signer has been already added
116+ // - [ model.InvalidSignerError] if signerID is invalid (not a consensus participant)
117+ // - [ model.DuplicatedSignerError] if the signer has been already added
121118//
122119// The function is thread-safe.
123120func (w * WeightedSignatureAggregator ) TrustedAdd (signerID flow.Identifier , sig crypto.Signature ) (uint64 , error ) {
@@ -130,19 +127,20 @@ func (w *WeightedSignatureAggregator) TrustedAdd(signerID flow.Identifier, sig c
130127 w .lock .Lock ()
131128 defer w .lock .Unlock ()
132129
133- // check for repeated occurrence of signerID (in anticipation of aggregator supporting multiplicities larger than 1 in the future)
134- if _ , duplicate := w . collectedIDs [ signerID ]; duplicate {
135- return w . totalWeight , model . NewDuplicatedSignerErrorf ( "signature from %v was already added" , signerID )
136- }
137-
130+ // NOTE: We delegate the handling of duplicate signatures to the underlying [signature.SignatureAggregatorSameMessage].
131+ // This is valid only because [signature.SignatureAggregatorSameMessage] does not support signatures with multiplicity
132+ // higher than 1, i.e. each signer is allowed to sign at most once. Should this constraint every be relaxed in
133+ // [signature.SignatureAggregatorSameMessage], we need to implement it here in the `WeightedSignatureAggregator`,
134+ // because in the context of HotStuff each consensus replica is allowed to vote at most once.
138135 err := w .aggregator .TrustedAdd (info .index , sig )
139136 if err != nil {
140- // During normal operations, signature.InvalidSignerIdxError or signature.DuplicatedSignerIdxError should never occur.
137+ if signature .IsDuplicatedSignerIdxError (err ) {
138+ return w .totalWeight , model .NewDuplicatedSignerErrorf ("signature from %v was already added" , signerID )
139+ }
140+ // During normal operations, signature.InvalidSignerIdxError should never occur.
141141 return w .totalWeight , fmt .Errorf ("unexpected exception while trusted add of signature from %v: %w" , signerID , err )
142142 }
143143 w .totalWeight += info .weight
144- w .collectedIDs [signerID ] = struct {}{}
145-
146144 return w .totalWeight , nil
147145}
148146
@@ -158,12 +156,12 @@ func (w *WeightedSignatureAggregator) TotalWeight() uint64 {
158156// The function performs a final verification and errors if the aggregated signature is invalid. This is
159157// required for the function safety since `TrustedAdd` allows adding invalid signatures.
160158// The function errors with:
161- // - model.InsufficientSignaturesError if no signatures have been added yet
162- // - model.InvalidSignatureIncludedError if:
159+ // - [ model.InsufficientSignaturesError] if no signatures have been added yet
160+ // - [ model.InvalidSignatureIncludedError] if:
163161// - some signature(s), included via TrustedAdd, fail to deserialize (regardless of the aggregated public key)
164162// -- or all signatures deserialize correctly but some signature(s), included via TrustedAdd, are
165163// invalid (while aggregated public key is valid)
166- // -- model.InvalidAggregatedKeyError if all signatures deserialize correctly but the signer's
164+ // -- [ model.InvalidAggregatedKeyError] if all signatures deserialize correctly but the signer's
167165// staking public keys sum up to an invalid key (BLS identity public key).
168166// Any aggregated signature would fail the cryptographic verification under the identity public
169167// key and therefore such signature is considered invalid. Such scenario can only happen if
0 commit comments