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