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

     1  package verification
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
     8  	"github.com/koko1123/flow-go-1/module"
     9  	"github.com/koko1123/flow-go-1/module/signature"
    10  	"github.com/onflow/flow-go/crypto/hash"
    11  )
    12  
    13  // CombinedSigner creates votes for the main consensus.
    14  // When a participant votes for a block, it _always_ provide the staking signature
    15  // as part of their vote. Furthermore, the participant can _optionally_
    16  // also provide a random beacon signature. Through their staking signature, a
    17  // participant always contributes to HotStuff's progress. Participation in the random
    18  // beacon is optional (but encouraged). This allows nodes that failed the DKG to
    19  // still contribute only to consensus (as fallback).
    20  // TODO: to be replaced by CombinedSignerV3 for mature V2 solution.
    21  // The difference between V2 and V3 is that V2 will sign 2 sigs, whereas
    22  // V3 only sign 1 sig.
    23  type CombinedSigner struct {
    24  	staking        module.Local
    25  	stakingHasher  hash.Hasher
    26  	beaconKeyStore module.RandomBeaconKeyStore
    27  	beaconHasher   hash.Hasher
    28  }
    29  
    30  // NewCombinedSigner creates a new combined signer with the given dependencies:
    31  // - the staking signer is used to create and verify aggregatable signatures for Hotstuff
    32  // - the beaconKeyStore is used to get threshold-signers by epoch/view;
    33  // - the signer ID is used as the identity when creating signatures;
    34  func NewCombinedSigner(
    35  	staking module.Local,
    36  	beaconKeyStore module.RandomBeaconKeyStore,
    37  ) *CombinedSigner {
    38  
    39  	sc := &CombinedSigner{
    40  		staking:        staking,
    41  		stakingHasher:  signature.NewBLSHasher(signature.ConsensusVoteTag),
    42  		beaconKeyStore: beaconKeyStore,
    43  		beaconHasher:   signature.NewBLSHasher(signature.RandomBeaconTag),
    44  	}
    45  	return sc
    46  }
    47  
    48  // CreateProposal will create a proposal with a combined signature for the given block.
    49  func (c *CombinedSigner) CreateProposal(block *model.Block) (*model.Proposal, error) {
    50  
    51  	// check that the block is created by us
    52  	if block.ProposerID != c.staking.NodeID() {
    53  		return nil, fmt.Errorf("can't create proposal for someone else's block")
    54  	}
    55  
    56  	// create the signature data
    57  	sigData, err := c.genSigData(block)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("signing my proposal failed: %w", err)
    60  	}
    61  
    62  	// create the proposal
    63  	proposal := &model.Proposal{
    64  		Block:   block,
    65  		SigData: sigData,
    66  	}
    67  
    68  	return proposal, nil
    69  }
    70  
    71  // CreateVote will create a vote with a combined signature for the given block.
    72  func (c *CombinedSigner) CreateVote(block *model.Block) (*model.Vote, error) {
    73  
    74  	// create the signature data
    75  	sigData, err := c.genSigData(block)
    76  	if err != nil {
    77  		return nil, fmt.Errorf("could not create signature: %w", err)
    78  	}
    79  
    80  	// create the vote
    81  	vote := &model.Vote{
    82  		View:     block.View,
    83  		BlockID:  block.BlockID,
    84  		SignerID: c.staking.NodeID(),
    85  		SigData:  sigData,
    86  	}
    87  
    88  	return vote, nil
    89  }
    90  
    91  // genSigData generates the signature data for our local node for the given block.
    92  // It returns:
    93  //   - (stakingSig, nil) if there is no random beacon private key.
    94  //   - (stakingSig+randomBeaconSig, nil) if there is a random beacon private key.
    95  //   - (nil, error) if there is any exception
    96  func (c *CombinedSigner) genSigData(block *model.Block) ([]byte, error) {
    97  
    98  	// create the message to be signed and generate signatures
    99  	msg := MakeVoteMessage(block.View, block.BlockID)
   100  
   101  	stakingSig, err := c.staking.Sign(msg, c.stakingHasher)
   102  	if err != nil {
   103  		return nil, fmt.Errorf("could not generate staking signature: %w", err)
   104  	}
   105  
   106  	beaconKey, err := c.beaconKeyStore.ByView(block.View)
   107  	if err != nil {
   108  		if errors.Is(err, module.DKGFailError) {
   109  			return stakingSig, nil
   110  		}
   111  		return nil, fmt.Errorf("could not get random beacon private key for view %d: %w", block.View, err)
   112  	}
   113  
   114  	// if the node is a Random Beacon participant and has succeeded DKG, then using the random beacon key
   115  	// to sign the block
   116  	beaconShare, err := beaconKey.Sign(msg, c.beaconHasher)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("could not generate beacon signature: %w", err)
   119  	}
   120  
   121  	return signature.EncodeDoubleSig(stakingSig, beaconShare), nil
   122  }