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