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  }