github.com/onflow/flow-go@v0.33.17/consensus/hotstuff/verification/combined_verifier_v2.go (about)

     1  package verification
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/onflow/flow-go/consensus/hotstuff"
     8  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     9  	"github.com/onflow/flow-go/crypto/hash"
    10  	"github.com/onflow/flow-go/model/flow"
    11  	msig "github.com/onflow/flow-go/module/signature"
    12  	"github.com/onflow/flow-go/state/protocol"
    13  )
    14  
    15  // CombinedVerifier is a verifier capable of verifying two signatures, one for each
    16  // scheme. The first type is a signature from a staking signer,
    17  // which verifies either a single or an aggregated signature. The second type is
    18  // a signature from a random beacon signer, which verifies either the signature share or
    19  // the reconstructed threshold signature.
    20  type CombinedVerifier struct {
    21  	committee           hotstuff.Replicas
    22  	stakingHasher       hash.Hasher
    23  	timeoutObjectHasher hash.Hasher
    24  	beaconHasher        hash.Hasher
    25  	packer              hotstuff.Packer
    26  }
    27  
    28  var _ hotstuff.Verifier = (*CombinedVerifier)(nil)
    29  
    30  // NewCombinedVerifier creates a new combined verifier with the given dependencies.
    31  // - the hotstuff committee's state is used to retrieve the public keys for the staking signature;
    32  // - the merger is used to combine and split staking and random beacon signatures;
    33  // - the packer is used to unpack QC for verification;
    34  func NewCombinedVerifier(committee hotstuff.Replicas, packer hotstuff.Packer) *CombinedVerifier {
    35  	return &CombinedVerifier{
    36  		committee:           committee,
    37  		stakingHasher:       msig.NewBLSHasher(msig.ConsensusVoteTag),
    38  		timeoutObjectHasher: msig.NewBLSHasher(msig.ConsensusTimeoutTag),
    39  		beaconHasher:        msig.NewBLSHasher(msig.RandomBeaconTag),
    40  		packer:              packer,
    41  	}
    42  }
    43  
    44  // VerifyVote verifies the validity of a combined signature from a vote.
    45  // Usually this method is only used to verify the proposer's vote, which is
    46  // the vote included in a block proposal.
    47  //   - model.InvalidFormatError if the signature has an incompatible format.
    48  //   - model.ErrInvalidSignature is the signature is invalid
    49  //   - model.InvalidSignerError if signer is _not_ part of the random beacon committee
    50  //   - model.ErrViewForUnknownEpoch if no epoch containing the given view is known
    51  //   - unexpected errors should be treated as symptoms of bugs or uncovered
    52  //     edge cases in the logic (i.e. as fatal)
    53  func (c *CombinedVerifier) VerifyVote(signer *flow.Identity, sigData []byte, view uint64, blockID flow.Identifier) error {
    54  
    55  	// create the to-be-signed message
    56  	msg := MakeVoteMessage(view, blockID)
    57  
    58  	// split the two signatures from the vote
    59  	stakingSig, beaconShare, err := msig.DecodeDoubleSig(sigData)
    60  	if err != nil {
    61  		if errors.Is(err, msig.ErrInvalidSignatureFormat) {
    62  			return model.NewInvalidFormatErrorf("could not split signature for block %v: %w", blockID, err)
    63  		}
    64  		return fmt.Errorf("unexpected internal error while splitting signature for block %v: %w", blockID, err)
    65  	}
    66  
    67  	dkg, err := c.committee.DKG(view)
    68  	if err != nil {
    69  		return fmt.Errorf("could not get dkg: %w", err)
    70  	}
    71  
    72  	// verify each signature against the message
    73  	// TODO: check if using batch verification is faster (should be yes)
    74  	stakingValid, err := signer.StakingPubKey.Verify(stakingSig, msg, c.stakingHasher)
    75  	if err != nil {
    76  		return fmt.Errorf("internal error while verifying staking signature of node %x at block %v: %w",
    77  			signer.NodeID, blockID, err)
    78  	}
    79  	if !stakingValid {
    80  		return fmt.Errorf("invalid staking sig for block %v: %w", blockID, model.ErrInvalidSignature)
    81  	}
    82  
    83  	// there is no beacon share, no need to verify it
    84  	if beaconShare == nil {
    85  		return nil
    86  	}
    87  
    88  	// if there is beacon share, there should be beacon public key
    89  	beaconPubKey, err := dkg.KeyShare(signer.NodeID)
    90  	if err != nil {
    91  		if protocol.IsIdentityNotFound(err) {
    92  			return model.NewInvalidSignerErrorf("%v is not a random beacon participant: %w", signer.NodeID, err)
    93  		}
    94  		return fmt.Errorf("unexpected error retrieving random beacon key share for node %x at block %v: %w",
    95  			signer.NodeID, blockID, err)
    96  	}
    97  
    98  	beaconValid, err := beaconPubKey.Verify(beaconShare, msg, c.beaconHasher)
    99  	if err != nil {
   100  		return fmt.Errorf("internal error while verifying beacon signature at block %v: %w",
   101  			blockID, err)
   102  	}
   103  	if !beaconValid {
   104  		return fmt.Errorf("invalid beacon sig for block %v: %w", blockID, model.ErrInvalidSignature)
   105  	}
   106  	return nil
   107  }
   108  
   109  // VerifyQC checks the cryptographic validity of the QC's `sigData` for the
   110  // given block. It is the responsibility of the calling code to ensure
   111  // that all `signers` are authorized, without duplicates. Return values:
   112  //   - nil if `sigData` is cryptographically valid
   113  //   - model.InsufficientSignaturesError if `signers` is empty.
   114  //     Depending on the order of checks in the higher-level logic this error might
   115  //     be an indicator of an external byzantine input or an internal bug.
   116  //   - model.InvalidFormatError if `sigData` has an incompatible format
   117  //   - model.ErrInvalidSignature if a signature is invalid
   118  //   - model.ErrViewForUnknownEpoch if no epoch containing the given view is known
   119  //   - error if running into any unexpected exception (i.e. fatal error)
   120  func (c *CombinedVerifier) VerifyQC(signers flow.IdentityList, sigData []byte, view uint64, blockID flow.Identifier) error {
   121  	dkg, err := c.committee.DKG(view)
   122  	if err != nil {
   123  		return fmt.Errorf("could not get dkg data: %w", err)
   124  	}
   125  
   126  	// unpack sig data using packer
   127  	blockSigData, err := c.packer.Unpack(signers, sigData)
   128  	if err != nil {
   129  		return fmt.Errorf("could not split signature: %w", err)
   130  	}
   131  
   132  	msg := MakeVoteMessage(view, blockID)
   133  
   134  	// verify the beacon signature first since it is faster to verify (no public key aggregation needed)
   135  	beaconValid, err := dkg.GroupKey().Verify(blockSigData.ReconstructedRandomBeaconSig, msg, c.beaconHasher)
   136  	if err != nil {
   137  		return fmt.Errorf("internal error while verifying beacon signature: %w", err)
   138  	}
   139  	if !beaconValid {
   140  		return fmt.Errorf("invalid reconstructed random beacon sig for block (%x): %w", blockID, model.ErrInvalidSignature)
   141  	}
   142  
   143  	err = verifyAggregatedSignatureOneMessage(signers.PublicStakingKeys(), blockSigData.AggregatedStakingSig, c.stakingHasher, msg)
   144  	if err != nil {
   145  		return fmt.Errorf("verifying aggregated staking signature failed for block %v: %w", blockID, err)
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  // VerifyTC checks cryptographic validity of the TC's `sigData` w.r.t. the
   152  // given view. It is the responsibility of the calling code to ensure
   153  // that all `signers` are authorized, without duplicates. Return values:
   154  //   - nil if `sigData` is cryptographically valid
   155  //   - model.InsufficientSignaturesError if `signers is empty.
   156  //   - model.InvalidFormatError if `signers`/`highQCViews` have differing lengths
   157  //   - model.ErrInvalidSignature if a signature is invalid
   158  //   - unexpected errors should be treated as symptoms of bugs or uncovered
   159  //     edge cases in the logic (i.e. as fatal)
   160  func (c *CombinedVerifier) VerifyTC(signers flow.IdentityList, sigData []byte, view uint64, highQCViews []uint64) error {
   161  	stakingPks := signers.PublicStakingKeys()
   162  	return verifyTCSignatureManyMessages(stakingPks, sigData, view, highQCViews, c.timeoutObjectHasher)
   163  }