-
Notifications
You must be signed in to change notification settings - Fork 178
/
qc.go
168 lines (134 loc) · 5.14 KB
/
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package run
import (
"fmt"
"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/crypto"
"github.com/onflow/flow-go/model/bootstrap"
"github.com/onflow/flow-go/model/encodable"
"github.com/onflow/flow-go/model/encoding"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/module/local"
"github.com/onflow/flow-go/module/signature"
)
type Participant struct {
bootstrap.NodeInfo
RandomBeaconPrivKey crypto.PrivateKey
}
type ParticipantData struct {
Participants []Participant
Lookup map[flow.Identifier]flow.DKGParticipant
GroupKey crypto.PublicKey
}
func (pd *ParticipantData) Identities() flow.IdentityList {
nodes := make([]bootstrap.NodeInfo, 0, len(pd.Participants))
for _, participant := range pd.Participants {
nodes = append(nodes, participant.NodeInfo)
}
return bootstrap.ToIdentityList(nodes)
}
func GenerateRootQC(block *flow.Block, participantData *ParticipantData) (*flow.QuorumCertificate, error) {
validators, signers, err := createValidators(participantData)
if err != nil {
return nil, err
}
hotBlock := model.GenesisBlockFromFlow(block.Header)
votes := make([]*model.Vote, 0, len(signers))
for _, signer := range signers {
vote, err := signer.CreateVote(hotBlock)
if err != nil {
return nil, err
}
votes = append(votes, vote)
}
// manually aggregate sigs
qc, err := signers[0].CreateQC(votes)
if err != nil {
return nil, err
}
// validate QC
err = validators[0].ValidateQC(qc, hotBlock)
return qc, err
}
func createValidators(participantData *ParticipantData) ([]hotstuff.Validator, []hotstuff.SignerVerifier, error) {
n := len(participantData.Participants)
identities := participantData.Identities()
groupSize := uint(len(participantData.Participants))
if groupSize < uint(n) {
return nil, nil, fmt.Errorf("need at least as many signers as DKG participants, got %v and %v", groupSize, n)
}
signers := make([]hotstuff.SignerVerifier, n)
validators := make([]hotstuff.Validator, n)
forks := &mocks.ForksReader{}
for i, participant := range participantData.Participants {
// get the participant private keys
keys, err := participant.PrivateKeys()
if err != nil {
return nil, nil, fmt.Errorf("could not get private keys for participant: %w", err)
}
local, err := local.New(participant.Identity(), keys.StakingKey)
if err != nil {
return nil, nil, err
}
// create consensus committee's state
committee, err := committees.NewStaticCommittee(identities, local.NodeID(), participantData.Lookup, participantData.GroupKey)
if err != nil {
return nil, nil, err
}
// create signer
stakingSigner := signature.NewAggregationProvider(encoding.ConsensusVoteTag, local)
beaconSigner := signature.NewThresholdProvider(encoding.RandomBeaconTag, participant.RandomBeaconPrivKey)
merger := signature.NewCombiner(encodable.ConsensusVoteSigLen, encodable.RandomBeaconSigLen)
signer := verification.NewCombinedSigner(committee, stakingSigner, beaconSigner, merger, participant.NodeID)
signers[i] = signer
// create validator
v := validator.New(committee, forks, signer)
validators[i] = v
}
return validators, signers, nil
}
func GenerateQCParticipantData(allNodes, internalNodes []bootstrap.NodeInfo, dkgData bootstrap.DKGData) (*ParticipantData, error) {
// stakingNodes can include external validators, so it can be longer than internalNodes
if len(allNodes) < len(internalNodes) {
return nil, fmt.Errorf("need at least as many staking public keys as private keys (pub=%d, priv=%d)", len(allNodes), len(internalNodes))
}
// length of DKG participants needs to match stakingNodes, since we run DKG for external and internal validators
if len(allNodes) != len(dkgData.PrivKeyShares) {
return nil, fmt.Errorf("need exactly the same number of staking public keys as DKG private participants")
}
qcData := &ParticipantData{}
participantLookup := make(map[flow.Identifier]flow.DKGParticipant)
// the QC will be signed by everyone in internalNodes
for i, node := range internalNodes {
// assign a node to a DGKdata entry, using the canonical ordering
participantLookup[node.NodeID] = flow.DKGParticipant{
KeyShare: dkgData.PubKeyShares[i],
Index: uint(i),
}
if node.NodeID == flow.ZeroID {
return nil, fmt.Errorf("node id cannot be zero")
}
if node.Stake == 0 {
return nil, fmt.Errorf("node (id=%s) cannot have 0 stake", node.NodeID)
}
qcData.Participants = append(qcData.Participants, Participant{
NodeInfo: node,
RandomBeaconPrivKey: dkgData.PrivKeyShares[i],
})
}
for i := len(internalNodes); i < len(allNodes); i++ {
// assign a node to a DGKdata entry, using the canonical ordering
node := allNodes[i]
participantLookup[node.NodeID] = flow.DKGParticipant{
KeyShare: dkgData.PubKeyShares[i],
Index: uint(i),
}
}
qcData.Lookup = participantLookup
qcData.GroupKey = dkgData.PubGroupKey
return qcData, nil
}