-
Notifications
You must be signed in to change notification settings - Fork 175
/
qc.go
174 lines (139 loc) · 5.42 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
169
170
171
172
173
174
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
}
// GenerateQCParticipantData generates QC participant data used to create the
// random beacon and staking signatures on the QC.
//
// allNodes must be in the same order that was used when running the DKG.
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 index here is important - we assume allNodes is in the same order as the DKG
for i := 0; 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),
}
}
// the QC will be signed by everyone in internalNodes
for _, node := range internalNodes {
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)
}
dkgParticipant, ok := participantLookup[node.NodeID]
if !ok {
return nil, fmt.Errorf("nonexistannt node id (%x) in participant lookup", node.NodeID)
}
dkgIndex := dkgParticipant.Index
qcData.Participants = append(qcData.Participants, Participant{
NodeInfo: node,
RandomBeaconPrivKey: dkgData.PrivKeyShares[dkgIndex],
})
}
qcData.Lookup = participantLookup
qcData.GroupKey = dkgData.PubGroupKey
return qcData, nil
}