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 }