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  }