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