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

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