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