github.com/koko1123/flow-go-1@v0.29.6/cmd/bootstrap/run/qc.go (about) 1 package run 2 3 import ( 4 "fmt" 5 6 "github.com/rs/zerolog" 7 8 "github.com/koko1123/flow-go-1/consensus/hotstuff" 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/committees" 10 "github.com/koko1123/flow-go-1/consensus/hotstuff/mocks" 11 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 12 hotstuffSig "github.com/koko1123/flow-go-1/consensus/hotstuff/signature" 13 "github.com/koko1123/flow-go-1/consensus/hotstuff/validator" 14 "github.com/koko1123/flow-go-1/consensus/hotstuff/verification" 15 "github.com/koko1123/flow-go-1/consensus/hotstuff/votecollector" 16 "github.com/koko1123/flow-go-1/model/bootstrap" 17 "github.com/koko1123/flow-go-1/model/dkg" 18 "github.com/koko1123/flow-go-1/model/flow" 19 "github.com/koko1123/flow-go-1/module/local" 20 "github.com/onflow/flow-go/crypto" 21 ) 22 23 type Participant struct { 24 bootstrap.NodeInfo 25 RandomBeaconPrivKey crypto.PrivateKey 26 } 27 28 type ParticipantData struct { 29 Participants []Participant 30 Lookup map[flow.Identifier]flow.DKGParticipant 31 GroupKey crypto.PublicKey 32 } 33 34 func (pd *ParticipantData) Identities() flow.IdentityList { 35 nodes := make([]bootstrap.NodeInfo, 0, len(pd.Participants)) 36 for _, participant := range pd.Participants { 37 nodes = append(nodes, participant.NodeInfo) 38 } 39 return bootstrap.ToIdentityList(nodes) 40 } 41 42 // GenerateRootQC generates QC for root block, caller needs to provide votes for root QC and 43 // participantData to build the QC. 44 // NOTE: at the moment, we require private keys for one node because we we re-using the full business logic, 45 // which assumes that only consensus participants construct QCs, which also have produce votes. 46 // 47 // TODO: modularize QC construction code (and code to verify QC) to be instantiated without needing private keys. 48 func GenerateRootQC(block *flow.Block, votes []*model.Vote, participantData *ParticipantData, identities flow.IdentityList) (*flow.QuorumCertificate, error) { 49 // create consensus committee's state 50 committee, err := committees.NewStaticCommittee(identities, flow.Identifier{}, participantData.Lookup, participantData.GroupKey) 51 if err != nil { 52 return nil, err 53 } 54 55 // STEP 1: create VoteProcessor 56 var createdQC *flow.QuorumCertificate 57 hotBlock := model.GenesisBlockFromFlow(block.Header) 58 processor, err := votecollector.NewBootstrapCombinedVoteProcessor(zerolog.Logger{}, committee, hotBlock, func(qc *flow.QuorumCertificate) { 59 createdQC = qc 60 }) 61 if err != nil { 62 return nil, fmt.Errorf("could not CombinedVoteProcessor processor: %w", err) 63 } 64 65 // STEP 2: feed the votes into the vote processor to create QC 66 for _, vote := range votes { 67 err := processor.Process(vote) 68 if err != nil { 69 return nil, fmt.Errorf("fail to process vote %v for block %v from signer %v: %w", 70 vote.ID(), 71 block.ID(), 72 vote.SignerID, 73 err) 74 } 75 } 76 77 if createdQC == nil { 78 return nil, fmt.Errorf("QC is not created, total number of votes %v, expect to have 2/3 votes of %v participants", 79 len(votes), len(identities)) 80 } 81 82 // STEP 3: validate constructed QC 83 val, err := createValidator(committee) 84 if err != nil { 85 return nil, err 86 } 87 err = val.ValidateQC(createdQC, hotBlock) 88 89 return createdQC, err 90 } 91 92 // GenerateRootBlockVotes generates votes for root block based on participantData 93 func GenerateRootBlockVotes(block *flow.Block, participantData *ParticipantData) ([]*model.Vote, error) { 94 hotBlock := model.GenesisBlockFromFlow(block.Header) 95 n := len(participantData.Participants) 96 fmt.Println("Number of staked consensus nodes: ", n) 97 98 votes := make([]*model.Vote, 0, n) 99 for _, p := range participantData.Participants { 100 fmt.Println("generating votes from consensus participants: ", p.NodeID, p.Address, p.StakingPubKey().String()) 101 102 // create the participant's local identity 103 keys, err := p.PrivateKeys() 104 if err != nil { 105 return nil, fmt.Errorf("could not get private keys for participant: %w", err) 106 } 107 me, err := local.New(p.Identity(), keys.StakingKey) 108 if err != nil { 109 return nil, err 110 } 111 112 // create signer and use it to generate vote 113 beaconStore := hotstuffSig.NewStaticRandomBeaconSignerStore(p.RandomBeaconPrivKey) 114 vote, err := verification.NewCombinedSigner(me, beaconStore).CreateVote(hotBlock) 115 if err != nil { 116 return nil, err 117 } 118 votes = append(votes, vote) 119 } 120 return votes, nil 121 } 122 123 // createValidator creates validator that can validate votes and QC 124 func createValidator(committee hotstuff.Committee) (hotstuff.Validator, error) { 125 packer := hotstuffSig.NewConsensusSigDataPacker(committee) 126 verifier := verification.NewCombinedVerifier(committee, packer) 127 128 forks := &mocks.ForksReader{} 129 hotstuffValidator := validator.New(committee, forks, verifier) 130 return hotstuffValidator, nil 131 } 132 133 // GenerateQCParticipantData generates QC participant data used to create the 134 // random beacon and staking signatures on the QC. 135 // 136 // allNodes must be in the same order that was used when running the DKG. 137 func GenerateQCParticipantData(allNodes, internalNodes []bootstrap.NodeInfo, dkgData dkg.DKGData) (*ParticipantData, error) { 138 139 // stakingNodes can include external validators, so it can be longer than internalNodes 140 if len(allNodes) < len(internalNodes) { 141 return nil, fmt.Errorf("need at least as many staking public keys as private keys (pub=%d, priv=%d)", len(allNodes), len(internalNodes)) 142 } 143 144 // length of DKG participants needs to match stakingNodes, since we run DKG for external and internal validators 145 if len(allNodes) != len(dkgData.PrivKeyShares) { 146 return nil, fmt.Errorf("need exactly the same number of staking public keys as DKG private participants") 147 } 148 149 qcData := &ParticipantData{} 150 151 participantLookup := make(map[flow.Identifier]flow.DKGParticipant) 152 153 // the index here is important - we assume allNodes is in the same order as the DKG 154 for i := 0; i < len(allNodes); i++ { 155 // assign a node to a DGKdata entry, using the canonical ordering 156 node := allNodes[i] 157 participantLookup[node.NodeID] = flow.DKGParticipant{ 158 KeyShare: dkgData.PubKeyShares[i], 159 Index: uint(i), 160 } 161 } 162 163 // the QC will be signed by everyone in internalNodes 164 for _, node := range internalNodes { 165 166 if node.NodeID == flow.ZeroID { 167 return nil, fmt.Errorf("node id cannot be zero") 168 } 169 170 if node.Weight == 0 { 171 return nil, fmt.Errorf("node (id=%s) cannot have 0 weight", node.NodeID) 172 } 173 174 dkgParticipant, ok := participantLookup[node.NodeID] 175 if !ok { 176 return nil, fmt.Errorf("nonexistannt node id (%x) in participant lookup", node.NodeID) 177 } 178 dkgIndex := dkgParticipant.Index 179 180 qcData.Participants = append(qcData.Participants, Participant{ 181 NodeInfo: node, 182 RandomBeaconPrivKey: dkgData.PrivKeyShares[dkgIndex], 183 }) 184 } 185 186 qcData.Lookup = participantLookup 187 qcData.GroupKey = dkgData.PubGroupKey 188 189 return qcData, nil 190 }