github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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.IdentitySkeletonList, clusterBlock *cluster.Block) (*flow.QuorumCertificate, error) { 22 if !allCommitteeMembers.Sorted(flow.Canonical[flow.IdentitySkeleton]) { 23 return nil, fmt.Errorf("can't create root cluster QC: committee members are not sorted in canonical order") 24 } 25 clusterRootBlock := model.GenesisBlockFromFlow(clusterBlock.Header) 26 27 // STEP 1: create votes for cluster root block 28 votes, err := createRootBlockVotes(signers, clusterRootBlock) 29 if err != nil { 30 return nil, err 31 } 32 33 // STEP 1.5: patch committee to include dynamic identities. This is a temporary measure until bootstrapping is refactored. 34 // We need a Committee for creating the cluster's root QC and the Committee requires dynamic identities to be instantiated. 35 // The clustering for root block contain only static identities, since there no state transitions have happened yet. 36 dynamicCommitteeMembers := make(flow.IdentityList, 0, len(allCommitteeMembers)) 37 for _, participant := range allCommitteeMembers { 38 dynamicCommitteeMembers = append(dynamicCommitteeMembers, &flow.Identity{ 39 IdentitySkeleton: *participant, 40 DynamicIdentity: flow.DynamicIdentity{ 41 EpochParticipationStatus: flow.EpochParticipationStatusActive, 42 }, 43 }) 44 } 45 46 // STEP 2: create VoteProcessor 47 committee, err := committees.NewStaticCommittee(dynamicCommitteeMembers, flow.Identifier{}, nil, nil) 48 if err != nil { 49 return nil, err 50 } 51 var createdQC *flow.QuorumCertificate 52 processor, err := votecollector.NewBootstrapStakingVoteProcessor(zerolog.Logger{}, committee, clusterRootBlock, func(qc *flow.QuorumCertificate) { 53 createdQC = qc 54 }) 55 if err != nil { 56 return nil, fmt.Errorf("could not create cluster's StakingVoteProcessor: %w", err) 57 } 58 59 // STEP 3: feed the votes into the vote processor to create QC 60 for _, vote := range votes { 61 err := processor.Process(vote) 62 if err != nil { 63 return nil, fmt.Errorf("could not process vote: %w", err) 64 } 65 } 66 if createdQC == nil { 67 return nil, fmt.Errorf("not enough votes to create qc for bootstrapping") 68 } 69 70 // STEP 4: validate constructed QC 71 val, err := createClusterValidator(committee) 72 if err != nil { 73 return nil, fmt.Errorf("could not create cluster validator: %w", err) 74 } 75 err = val.ValidateQC(createdQC) 76 77 return createdQC, err 78 } 79 80 // createClusterValidator creates validator for cluster consensus 81 func createClusterValidator(committee hotstuff.DynamicCommittee) (hotstuff.Validator, error) { 82 verifier := verification.NewStakingVerifier() 83 84 hotstuffValidator := validator.New(committee, verifier) 85 return hotstuffValidator, nil 86 } 87 88 // createRootBlockVotes generates a vote for the rootBlock from each participant 89 func createRootBlockVotes(participants []bootstrap.NodeInfo, rootBlock *model.Block) ([]*model.Vote, error) { 90 votes := make([]*model.Vote, 0, len(participants)) 91 for _, participant := range participants { 92 // create the participant's local identity 93 keys, err := participant.PrivateKeys() 94 if err != nil { 95 return nil, fmt.Errorf("could not retrieve private keys for participant: %w", err) 96 } 97 me, err := local.New(participant.Identity().IdentitySkeleton, keys.StakingKey) 98 if err != nil { 99 return nil, err 100 } 101 102 // generate root block vote 103 vote, err := verification.NewStakingSigner(me).CreateVote(rootBlock) 104 if err != nil { 105 return nil, fmt.Errorf("could not create cluster vote for participant %v: %w", me.NodeID(), err) 106 } 107 votes = append(votes, vote) 108 } 109 return votes, nil 110 }