github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/signature/randombeacon_inspector.go (about) 1 package signature 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/crypto" 7 8 "github.com/onflow/flow-go/consensus/hotstuff/model" 9 "github.com/onflow/flow-go/module/signature" 10 ) 11 12 // randomBeaconInspector implements hotstuff.RandomBeaconInspector interface. 13 // All methods of this structure are concurrency-safe. 14 type randomBeaconInspector struct { 15 inspector crypto.ThresholdSignatureInspector 16 } 17 18 // NewRandomBeaconInspector instantiates a new randomBeaconInspector. 19 // The constructor errors with a `model.ConfigurationError` in any of the following cases 20 // - n is not between `ThresholdSignMinSize` and `ThresholdSignMaxSize`, 21 // for n the number of participants `n := len(publicKeyShares)` 22 // - threshold value is not in interval [1, n-1] 23 // - any input public key is not a BLS key 24 func NewRandomBeaconInspector( 25 groupPublicKey crypto.PublicKey, 26 publicKeyShares []crypto.PublicKey, 27 threshold int, 28 message []byte, 29 ) (*randomBeaconInspector, error) { 30 inspector, err := crypto.NewBLSThresholdSignatureInspector( 31 groupPublicKey, 32 publicKeyShares, 33 threshold, 34 message, 35 signature.RandomBeaconTag) 36 if err != nil { 37 if crypto.IsInvalidInputsError(err) || crypto.IsNotBLSKeyError(err) { 38 return nil, model.NewConfigurationErrorf("invalid parametrization for BLS Threshold Signature Inspector: %w", err) 39 } 40 return nil, fmt.Errorf("unexpected exception while instantiating BLS Threshold Signature Inspector: %w", err) 41 } 42 43 return &randomBeaconInspector{ 44 inspector: inspector, 45 }, nil 46 } 47 48 // Verify verifies the signature share under the signer's public key and the message agreed upon. 49 // The function is thread-safe and wait-free (i.e. allowing arbitrary many routines to 50 // execute the business logic, without interfering with each other). 51 // It allows concurrent verification of the given signature. 52 // Returns : 53 // - model.InvalidSignerError if signerIndex is invalid 54 // - model.ErrInvalidSignature if signerID is valid but signature is cryptographically invalid 55 // - other error if there is an unexpected exception. 56 func (r *randomBeaconInspector) Verify(signerIndex int, share crypto.Signature) error { 57 valid, err := r.inspector.VerifyShare(signerIndex, share) 58 if err != nil { 59 if crypto.IsInvalidInputsError(err) { 60 return model.NewInvalidSignerError(err) 61 } 62 return fmt.Errorf("unexpected error verifying beacon signature from %d: %w", signerIndex, err) 63 } 64 65 if !valid { // invalid signature 66 return fmt.Errorf("invalid beacon share from signer Index %d: %w", signerIndex, model.ErrInvalidSignature) 67 } 68 return nil 69 } 70 71 // TrustedAdd adds a share to the internal signature shares store. 72 // There is no pre-check of the signature's validity _before_ adding it. 73 // It is the caller's responsibility to make sure the signature was previously verified. 74 // Nevertheless, the implementation guarantees safety (only correct threshold signatures 75 // are returned) through a post-check (verifying the threshold signature 76 // _after_ reconstruction before returning it). 77 // The function is thread-safe but locks its internal state, thereby permitting only 78 // one routine at a time to add a signature. 79 // Returns: 80 // - (true, nil) if the signature has been added, and enough shares have been collected. 81 // - (false, nil) if the signature has been added, but not enough shares were collected. 82 // 83 // The following errors are expected during normal operations: 84 // - model.InvalidSignerError if signerIndex is invalid (out of the valid range) 85 // - model.DuplicatedSignerError if the signer has been already added 86 // - other error if there is an unexpected exception. 87 func (r *randomBeaconInspector) TrustedAdd(signerIndex int, share crypto.Signature) (bool, error) { 88 // Trusted add to the crypto layer 89 enough, err := r.inspector.TrustedAdd(signerIndex, share) 90 if err != nil { 91 if crypto.IsInvalidInputsError(err) { 92 return false, model.NewInvalidSignerError(err) 93 } 94 if crypto.IsDuplicatedSignerError(err) { 95 return false, model.NewDuplicatedSignerError(err) 96 } 97 return false, fmt.Errorf("unexpected error while adding share from %d: %w", signerIndex, err) 98 } 99 return enough, nil 100 } 101 102 // EnoughShares indicates whether enough shares have been accumulated in order to reconstruct 103 // a group signature. 104 // 105 // The function is write-blocking 106 func (r *randomBeaconInspector) EnoughShares() bool { 107 return r.inspector.EnoughShares() 108 } 109 110 // Reconstruct reconstructs the group signature. The function is thread-safe but locks 111 // its internal state, thereby permitting only one routine at a time. 112 // 113 // Returns: 114 // - (signature, nil) if no error occurred 115 // - (nil, model.InsufficientSignaturesError) if not enough shares were collected 116 // - (nil, model.InvalidSignatureIncluded) if at least one collected share does not serialize to a valid BLS signature, 117 // or if the constructed signature failed to verify against the group public key and stored message. This post-verification 118 // is required for safety, as `TrustedAdd` allows adding invalid signatures. 119 // - (nil, error) for any other unexpected error. 120 func (r *randomBeaconInspector) Reconstruct() (crypto.Signature, error) { 121 sig, err := r.inspector.ThresholdSignature() 122 if err != nil { 123 if crypto.IsInvalidInputsError(err) || crypto.IsInvalidSignatureError(err) { 124 return nil, model.NewInvalidSignatureIncludedError(err) 125 } 126 if crypto.IsNotEnoughSharesError(err) { 127 return nil, model.NewInsufficientSignaturesError(err) 128 } 129 return nil, fmt.Errorf("unexpected error during random beacon sig reconstruction: %w", err) 130 } 131 return sig, nil 132 }