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 }