github.com/onflow/flow-go@v0.33.17/cmd/bootstrap/run/cluster_qc.go (about) 1 package run 2 3 import ( 4 "fmt" 5 6 "github.com/rs/zerolog" 7 8 "github.com/onflow/flow-go/consensus/hotstuff" 9 "github.com/onflow/flow-go/consensus/hotstuff/committees" 10 "github.com/onflow/flow-go/consensus/hotstuff/model" 11 "github.com/onflow/flow-go/consensus/hotstuff/validator" 12 "github.com/onflow/flow-go/consensus/hotstuff/verification" 13 "github.com/onflow/flow-go/consensus/hotstuff/votecollector" 14 "github.com/onflow/flow-go/model/bootstrap" 15 "github.com/onflow/flow-go/model/cluster" 16 "github.com/onflow/flow-go/model/flow" 17 "github.com/onflow/flow-go/module/local" 18 ) 19 20 // GenerateClusterRootQC creates votes and generates a QC based on participant data 21 func GenerateClusterRootQC(signers []bootstrap.NodeInfo, allCommitteeMembers flow.IdentityList, clusterBlock *cluster.Block) (*flow.QuorumCertificate, error) { 22 clusterRootBlock := model.GenesisBlockFromFlow(clusterBlock.Header) 23 24 // STEP 1: create votes for cluster root block 25 votes, err := createRootBlockVotes(signers, clusterRootBlock) 26 if err != nil { 27 return nil, err 28 } 29 30 // STEP 2: create VoteProcessor 31 ordered := allCommitteeMembers.Sort(flow.Canonical) 32 committee, err := committees.NewStaticCommittee(ordered, flow.Identifier{}, nil, nil) 33 if err != nil { 34 return nil, err 35 } 36 var createdQC *flow.QuorumCertificate 37 processor, err := votecollector.NewBootstrapStakingVoteProcessor(zerolog.Logger{}, committee, clusterRootBlock, func(qc *flow.QuorumCertificate) { 38 createdQC = qc 39 }) 40 if err != nil { 41 return nil, fmt.Errorf("could not create cluster's StakingVoteProcessor: %w", err) 42 } 43 44 // STEP 3: feed the votes into the vote processor to create QC 45 for _, vote := range votes { 46 err := processor.Process(vote) 47 if err != nil { 48 return nil, fmt.Errorf("could not process vote: %w", err) 49 } 50 } 51 if createdQC == nil { 52 return nil, fmt.Errorf("not enough votes to create qc for bootstrapping") 53 } 54 55 // STEP 4: validate constructed QC 56 val, err := createClusterValidator(committee) 57 if err != nil { 58 return nil, fmt.Errorf("could not create cluster validator: %w", err) 59 } 60 err = val.ValidateQC(createdQC) 61 62 return createdQC, err 63 } 64 65 // createClusterValidator creates validator for cluster consensus 66 func createClusterValidator(committee hotstuff.DynamicCommittee) (hotstuff.Validator, error) { 67 verifier := verification.NewStakingVerifier() 68 69 hotstuffValidator := validator.New(committee, verifier) 70 return hotstuffValidator, nil 71 } 72 73 // createRootBlockVotes generates a vote for the rootBlock from each participant 74 func createRootBlockVotes(participants []bootstrap.NodeInfo, rootBlock *model.Block) ([]*model.Vote, error) { 75 votes := make([]*model.Vote, 0, len(participants)) 76 for _, participant := range participants { 77 // create the participant's local identity 78 keys, err := participant.PrivateKeys() 79 if err != nil { 80 return nil, fmt.Errorf("could not retrieve private keys for participant: %w", err) 81 } 82 me, err := local.New(participant.Identity(), keys.StakingKey) 83 if err != nil { 84 return nil, err 85 } 86 87 // generate root block vote 88 vote, err := verification.NewStakingSigner(me).CreateVote(rootBlock) 89 if err != nil { 90 return nil, fmt.Errorf("could not create cluster vote for participant %v: %w", me.NodeID(), err) 91 } 92 votes = append(votes, vote) 93 } 94 return votes, nil 95 }