github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/verification/combined_verifier_v2.go (about)

     1  //go:build relic
     2  // +build relic
     3  
     4  package verification
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    12  	"github.com/koko1123/flow-go-1/model/flow"
    13  	"github.com/koko1123/flow-go-1/module/signature"
    14  	"github.com/koko1123/flow-go-1/state/protocol"
    15  	"github.com/onflow/flow-go/crypto"
    16  	"github.com/onflow/flow-go/crypto/hash"
    17  )
    18  
    19  // CombinedVerifier is a verifier capable of verifying two signatures, one for each
    20  // scheme. The first type is a signature from a staking signer,
    21  // which verifies either a single or an aggregated signature. The second type is
    22  // a signature from a random beacon signer, which verifies either the signature share or
    23  // the reconstructed threshold signature.
    24  type CombinedVerifier struct {
    25  	committee     hotstuff.Committee
    26  	stakingHasher hash.Hasher
    27  	beaconHasher  hash.Hasher
    28  	packer        hotstuff.Packer
    29  }
    30  
    31  var _ hotstuff.Verifier = (*CombinedVerifier)(nil)
    32  
    33  // NewCombinedVerifier creates a new combined verifier with the given dependencies.
    34  // - the hotstuff committee's state is used to retrieve the public keys for the staking signature;
    35  // - the merger is used to combine and split staking and random beacon signatures;
    36  // - the packer is used to unpack QC for verification;
    37  func NewCombinedVerifier(committee hotstuff.Committee, packer hotstuff.Packer) *CombinedVerifier {
    38  	return &CombinedVerifier{
    39  		committee:     committee,
    40  		stakingHasher: signature.NewBLSHasher(signature.ConsensusVoteTag),
    41  		beaconHasher:  signature.NewBLSHasher(signature.RandomBeaconTag),
    42  		packer:        packer,
    43  	}
    44  }
    45  
    46  // VerifyVote verifies the validity of a combined signature from a vote.
    47  // Usually this method is only used to verify the proposer's vote, which is
    48  // the vote included in a block proposal.
    49  //   - model.InvalidFormatError if the signature has an incompatible format.
    50  //   - model.ErrInvalidSignature is the signature is invalid
    51  //   - model.InvalidSignerError if signer is _not_ part of the random beacon committee
    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.Identity, sigData []byte, block *model.Block) error {
    55  
    56  	// create the to-be-signed message
    57  	msg := MakeVoteMessage(block.View, block.BlockID)
    58  
    59  	// split the two signatures from the vote
    60  	stakingSig, beaconShare, err := signature.DecodeDoubleSig(sigData)
    61  	if err != nil {
    62  		if errors.Is(err, signature.ErrInvalidSignatureFormat) {
    63  			return model.NewInvalidFormatErrorf("could not split signature for block %v: %w", block.BlockID, err)
    64  		}
    65  		return fmt.Errorf("unexpected internal error while splitting signature for block %v: %w", block.BlockID, err)
    66  	}
    67  
    68  	dkg, err := c.committee.DKG(block.BlockID)
    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, block.BlockID, err)
    79  	}
    80  	if !stakingValid {
    81  		return fmt.Errorf("invalid staking sig for block %v: %w", block.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, block.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  			block.BlockID, err)
   103  	}
   104  	if !beaconValid {
   105  		return fmt.Errorf("invalid beacon sig for block %v: %w", block.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 a 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  //   - error if running into any unexpected exception (i.e. fatal error)
   120  func (c *CombinedVerifier) VerifyQC(signers flow.IdentityList, sigData []byte, block *model.Block) error {
   121  	if len(signers) == 0 {
   122  		return model.NewInsufficientSignaturesErrorf("empty list of signers")
   123  	}
   124  	dkg, err := c.committee.DKG(block.BlockID)
   125  	if err != nil {
   126  		return fmt.Errorf("could not get dkg data: %w", err)
   127  	}
   128  
   129  	// unpack sig data using packer
   130  	blockSigData, err := c.packer.Unpack(signers, sigData)
   131  	if err != nil {
   132  		return fmt.Errorf("could not split signature: %w", err)
   133  	}
   134  
   135  	msg := MakeVoteMessage(block.View, block.BlockID)
   136  
   137  	// verify the beacon signature first since it is faster to verify (no public key aggregation needed)
   138  	beaconValid, err := dkg.GroupKey().Verify(blockSigData.ReconstructedRandomBeaconSig, msg, c.beaconHasher)
   139  	if err != nil {
   140  		return fmt.Errorf("internal error while verifying beacon signature: %w", err)
   141  	}
   142  	if !beaconValid {
   143  		return fmt.Errorf("invalid reconstructed random beacon sig for block (%x): %w", block.BlockID, model.ErrInvalidSignature)
   144  	}
   145  
   146  	// aggregate public staking keys of all signers (more costly)
   147  	// TODO: update to use module/signature.PublicKeyAggregator
   148  	aggregatedKey, err := crypto.AggregateBLSPublicKeys(signers.PublicStakingKeys()) // caution: requires non-empty slice of keys!
   149  	if err != nil {
   150  		// `AggregateBLSPublicKeys` returns a `crypto.invalidInputsError` in two distinct cases:
   151  		//  (i) In case no keys are provided, i.e.  `len(signers) == 0`.
   152  		//      This scenario _is expected_ during normal operations, because a byzantine
   153  		//      proposer might construct an (invalid) QC with an empty list of signers.
   154  		// (ii) In case some provided public keys type is not BLS.
   155  		//      This scenario is _not expected_ during normal operations, because all keys are
   156  		//      guaranteed by the protocol to be BLS keys.
   157  		//
   158  		// By checking `len(signers) == 0` upfront , we can rule out case (i) as a source of error.
   159  		// Hence, if we encounter an error here, we know it is case (ii). Thereby, we can clearly
   160  		// distinguish a faulty _external_ input from an _internal_ uncovered edge-case.
   161  		return fmt.Errorf("could not compute aggregated key for block %x: %w", block.BlockID, err)
   162  	}
   163  
   164  	// verify aggregated signature with aggregated keys from last step
   165  	stakingValid, err := aggregatedKey.Verify(blockSigData.AggregatedStakingSig, msg, c.stakingHasher)
   166  	if err != nil {
   167  		return fmt.Errorf("internal error while verifying staking signature for block %x: %w", block.BlockID, err)
   168  	}
   169  	if !stakingValid {
   170  		return fmt.Errorf("invalid aggregated staking sig for block %v: %w", block.BlockID, model.ErrInvalidSignature)
   171  	}
   172  
   173  	return nil
   174  }