github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/verification/combined_verifier_v2.go (about)

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