Skip to content

Commit df7fd01

Browse files
committed
allow providing votes to generate Cluster Root QCs
1 parent 502f81c commit df7fd01

File tree

2 files changed

+48
-4
lines changed

2 files changed

+48
-4
lines changed

cmd/bootstrap/run/cluster_qc.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ func GenerateClusterRootQC(signers []bootstrap.NodeInfo, allCommitteeMembers flo
2929
if err != nil {
3030
return nil, err
3131
}
32+
return GenerateClusterRootQCFromVotes(signers, allCommitteeMembers, clusterBlock, votes)
33+
}
34+
35+
// GenerateClusterRootQCFromVotes generates a QC from the provided votes based on participant data
36+
func GenerateClusterRootQCFromVotes(signers []bootstrap.NodeInfo, allCommitteeMembers flow.IdentitySkeletonList, clusterBlock *cluster.Block, votes []*model.Vote) (*flow.QuorumCertificate, error) {
37+
if !allCommitteeMembers.Sorted(flow.Canonical[flow.IdentitySkeleton]) {
38+
return nil, fmt.Errorf("can't create root cluster QC: committee members are not sorted in canonical order")
39+
}
40+
clusterRootBlock := model.GenesisBlockFromFlow(clusterBlock.ToHeader())
3241

3342
// STEP 1.5: patch committee to include dynamic identities. This is a temporary measure until bootstrapping is refactored.
3443
// We need a Committee for creating the cluster's root QC and the Committee requires dynamic identities to be instantiated.

cmd/bootstrap/run/epochs.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import (
99
"github.com/rs/zerolog"
1010
"golang.org/x/exp/slices"
1111

12-
"github.com/onflow/flow-go/state/protocol/prg"
13-
1412
"github.com/onflow/flow-go/cmd/util/cmd/common"
13+
hotstuff "github.com/onflow/flow-go/consensus/hotstuff/model"
1514
"github.com/onflow/flow-go/fvm/systemcontracts"
1615
"github.com/onflow/flow-go/model/bootstrap"
1716
model "github.com/onflow/flow-go/model/bootstrap"
1817
"github.com/onflow/flow-go/model/cluster"
1918
"github.com/onflow/flow-go/model/flow"
2019
"github.com/onflow/flow-go/model/flow/filter"
2120
"github.com/onflow/flow-go/state/protocol/inmem"
21+
"github.com/onflow/flow-go/state/protocol/prg"
2222
)
2323

2424
// GenerateRecoverEpochTxArgs generates the required transaction arguments for the `recoverEpoch` transaction.
@@ -303,8 +303,7 @@ func GenerateRecoverTxArgsWithDKG(
303303
// - nodeInfos: list of NodeInfos (must contain all internal nodes)
304304
// - clusterBlocks: list of root blocks (one for each cluster)
305305
// Returns:
306-
// - flow.AssignmentList: the generated assignment list.
307-
// - flow.ClusterList: the generate collection cluster list.
306+
// - The list of quorum certificates for all clusters.
308307
func ConstructRootQCsForClusters(log zerolog.Logger, clusterList flow.ClusterList, nodeInfos []bootstrap.NodeInfo, clusterBlocks []*cluster.Block) []*flow.QuorumCertificate {
309308
if len(clusterBlocks) != len(clusterList) {
310309
log.Fatal().Int("len(clusterBlocks)", len(clusterBlocks)).Int("len(clusterList)", len(clusterList)).
@@ -326,6 +325,42 @@ func ConstructRootQCsForClusters(log zerolog.Logger, clusterList flow.ClusterLis
326325
return qcs
327326
}
328327

328+
// ConstructClusterRootQCsFromVotes constructs a root QC for each cluster in the list, based on the provided votes.
329+
// Args:
330+
// - log: the logger instance.
331+
// - clusterList: list of clusters
332+
// - nodeInfos: list of NodeInfos (must contain all internal nodes)
333+
// - clusterBlocks: list of root blocks (one for each cluster)
334+
// - votes: lists of votes for each cluster (one list for each cluster)
335+
// Returns:
336+
// - the list of quorum certificates for all clusters
337+
func ConstructClusterRootQCsFromVotes(log zerolog.Logger, clusterList flow.ClusterList, nodeInfos []bootstrap.NodeInfo, clusterBlocks []*cluster.Block, votes [][]*hotstuff.Vote) []*flow.QuorumCertificate {
338+
if len(clusterBlocks) != len(clusterList) {
339+
log.Fatal().Int("len(clusterBlocks)", len(clusterBlocks)).Int("len(clusterList)", len(clusterList)).
340+
Msg("number of clusters needs to equal number of cluster blocks")
341+
}
342+
if len(votes) != len(clusterList) {
343+
log.Fatal().Int("len(votes)", len(votes)).Int("len(clusterList)", len(clusterList)).
344+
Msg("number of groups of votes needs to equal number of clusters")
345+
}
346+
347+
qcs := make([]*flow.QuorumCertificate, len(clusterBlocks))
348+
for i, clusterMembers := range clusterList {
349+
clusterBlock := clusterBlocks[i]
350+
clusterVotes := votes[i]
351+
signers := filterClusterSigners(clusterMembers, nodeInfos)
352+
log.Info().Msgf("producing QC for cluster (index: %d, size: %d) from %d votes", i, len(clusterMembers), len(votes))
353+
354+
qc, err := GenerateClusterRootQCFromVotes(signers, clusterMembers, clusterBlock, clusterVotes)
355+
if err != nil {
356+
log.Fatal().Err(err).Int("cluster index", i).Msg("generating collector cluster root QC failed")
357+
}
358+
qcs[i] = qc
359+
}
360+
361+
return qcs
362+
}
363+
329364
// Filters a list of nodes to include only nodes that will sign the QC for the
330365
// given cluster. The resulting list of nodes is only nodes that are in the
331366
// given cluster AND are not partner nodes (ie. we have the private keys).

0 commit comments

Comments
 (0)