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

     1  package verification
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/consensus/hotstuff"
     7  	"github.com/onflow/flow-go/consensus/hotstuff/model"
     8  	"github.com/onflow/flow-go/crypto/hash"
     9  	"github.com/onflow/flow-go/model/flow"
    10  	"github.com/onflow/flow-go/module"
    11  	msig "github.com/onflow/flow-go/module/signature"
    12  )
    13  
    14  // StakingSigner creates votes for the collector clusters consensus.
    15  // When a participant votes for a block, it _always_ provide the staking signature
    16  // as part of their vote. StakingSigner is responsible for creating correctly
    17  // signed proposals and votes.
    18  type StakingSigner struct {
    19  	me                  module.Local
    20  	stakingHasher       hash.Hasher
    21  	timeoutObjectHasher hash.Hasher
    22  	signerID            flow.Identifier
    23  }
    24  
    25  var _ hotstuff.Signer = (*StakingSigner)(nil)
    26  
    27  // NewStakingSigner instantiates a StakingSigner, which signs votes and
    28  // proposals with the staking key.  The generated signatures are aggregatable.
    29  func NewStakingSigner(
    30  	me module.Local,
    31  ) *StakingSigner {
    32  
    33  	sc := &StakingSigner{
    34  		me:                  me,
    35  		stakingHasher:       msig.NewBLSHasher(msig.CollectorVoteTag),
    36  		timeoutObjectHasher: msig.NewBLSHasher(msig.CollectorTimeoutTag),
    37  		signerID:            me.NodeID(),
    38  	}
    39  	return sc
    40  }
    41  
    42  // CreateProposal will create a proposal with a staking signature for the given block.
    43  func (c *StakingSigner) CreateProposal(block *model.Block) (*model.Proposal, error) {
    44  
    45  	// check that the block is created by us
    46  	if block.ProposerID != c.signerID {
    47  		return nil, fmt.Errorf("can't create proposal for someone else's block")
    48  	}
    49  
    50  	// create the signature data
    51  	sigData, err := c.genSigData(block)
    52  	if err != nil {
    53  		return nil, fmt.Errorf("signing my proposal failed: %w", err)
    54  	}
    55  
    56  	// create the proposal
    57  	proposal := &model.Proposal{
    58  		Block:   block,
    59  		SigData: sigData,
    60  	}
    61  
    62  	return proposal, nil
    63  }
    64  
    65  // CreateVote will create a vote with a staking signature for the given block.
    66  func (c *StakingSigner) CreateVote(block *model.Block) (*model.Vote, error) {
    67  
    68  	// create the signature data
    69  	sigData, err := c.genSigData(block)
    70  	if err != nil {
    71  		return nil, fmt.Errorf("could not create signature: %w", err)
    72  	}
    73  
    74  	// create the vote
    75  	vote := &model.Vote{
    76  		View:     block.View,
    77  		BlockID:  block.BlockID,
    78  		SignerID: c.signerID,
    79  		SigData:  sigData,
    80  	}
    81  
    82  	return vote, nil
    83  }
    84  
    85  // CreateTimeout will create a signed timeout object for the given view.
    86  func (c *StakingSigner) CreateTimeout(curView uint64, newestQC *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) (*model.TimeoutObject, error) {
    87  	// create timeout object specific message
    88  	msg := MakeTimeoutMessage(curView, newestQC.View)
    89  	sigData, err := c.me.Sign(msg, c.timeoutObjectHasher)
    90  	if err != nil {
    91  		return nil, fmt.Errorf("could not generate signature for timeout object at view %d: %w", curView, err)
    92  	}
    93  
    94  	timeout := &model.TimeoutObject{
    95  		View:       curView,
    96  		NewestQC:   newestQC,
    97  		LastViewTC: lastViewTC,
    98  		SignerID:   c.signerID,
    99  		SigData:    sigData,
   100  	}
   101  	return timeout, nil
   102  }
   103  
   104  // genSigData generates the signature data for our local node for the given block.
   105  // It returns:
   106  //   - (stakingSig, nil) signature signed with staking key.  The sig is 48 bytes long
   107  //   - (nil, error) if there is any exception
   108  func (c *StakingSigner) genSigData(block *model.Block) ([]byte, error) {
   109  	// create the message to be signed and generate signatures
   110  	msg := MakeVoteMessage(block.View, block.BlockID)
   111  
   112  	stakingSig, err := c.me.Sign(msg, c.stakingHasher)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("could not generate staking signature for block (%v) at view %v: %w", block.BlockID, block.View, err)
   115  	}
   116  
   117  	return stakingSig, nil
   118  }