github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/model/flow/epoch.go (about) 1 package flow 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 9 "github.com/ethereum/go-ethereum/rlp" 10 "github.com/fxamacker/cbor/v2" 11 "github.com/onflow/crypto" 12 "github.com/vmihailenco/msgpack/v4" 13 14 "github.com/onflow/flow-go/model/encodable" 15 ) 16 17 // EpochPhase represents a phase of the Epoch Preparation Protocol. The phase 18 // of an epoch is resolved based on a block reference and is fork-dependent. 19 // An epoch begins in the staking phase, then transitions to the setup phase in 20 // the block containing the EpochSetup service event, then to the committed 21 // phase in the block containing the EpochCommit service event. 22 // |<-- EpochPhaseStaking -->|<-- EpochPhaseSetup -->|<-- EpochPhaseCommitted -->|<-- EpochPhaseStaking -->... 23 // |<------------------------------- Epoch N ------------------------------------>|<-- Epoch N + 1 --... 24 type EpochPhase int 25 26 const ( 27 EpochPhaseUndefined EpochPhase = iota 28 EpochPhaseStaking 29 EpochPhaseSetup 30 EpochPhaseCommitted 31 ) 32 33 func (p EpochPhase) String() string { 34 return [...]string{ 35 "EpochPhaseUndefined", 36 "EpochPhaseStaking", 37 "EpochPhaseSetup", 38 "EpochPhaseCommitted", 39 }[p] 40 } 41 42 func GetEpochPhase(phase string) EpochPhase { 43 phases := []EpochPhase{ 44 EpochPhaseUndefined, 45 EpochPhaseStaking, 46 EpochPhaseSetup, 47 EpochPhaseCommitted, 48 } 49 for _, p := range phases { 50 if p.String() == phase { 51 return p 52 } 53 } 54 55 return EpochPhaseUndefined 56 } 57 58 // EpochSetupRandomSourceLength is the required length of the random source 59 // included in an EpochSetup service event. 60 const EpochSetupRandomSourceLength = 16 61 62 // EpochSetup is a service event emitted when the network is ready to set up 63 // for the upcoming epoch. It contains the participants in the epoch, the 64 // length, the cluster assignment, and the seed for leader selection. 65 type EpochSetup struct { 66 Counter uint64 // the number of the epoch 67 FirstView uint64 // the first view of the epoch 68 DKGPhase1FinalView uint64 // the final view of DKG phase 1 69 DKGPhase2FinalView uint64 // the final view of DKG phase 2 70 DKGPhase3FinalView uint64 // the final view of DKG phase 3 71 FinalView uint64 // the final view of the epoch 72 Participants IdentitySkeletonList // all participants of the epoch in canonical order 73 Assignments AssignmentList // cluster assignment for the epoch 74 RandomSource []byte // source of randomness for epoch-specific setup tasks 75 TargetDuration uint64 // desired real-world duration for the epoch [seconds] 76 TargetEndTime uint64 // desired real-world end time for the epoch in UNIX time [seconds] 77 } 78 79 func (setup *EpochSetup) ServiceEvent() ServiceEvent { 80 return ServiceEvent{ 81 Type: ServiceEventSetup, 82 Event: setup, 83 } 84 } 85 86 // ID returns the hash of the event contents. 87 func (setup *EpochSetup) ID() Identifier { 88 return MakeID(setup) 89 } 90 91 func (setup *EpochSetup) EqualTo(other *EpochSetup) bool { 92 if setup.Counter != other.Counter { 93 return false 94 } 95 if setup.FirstView != other.FirstView { 96 return false 97 } 98 if setup.DKGPhase1FinalView != other.DKGPhase1FinalView { 99 return false 100 } 101 if setup.DKGPhase2FinalView != other.DKGPhase2FinalView { 102 return false 103 } 104 if setup.DKGPhase3FinalView != other.DKGPhase3FinalView { 105 return false 106 } 107 if setup.FinalView != other.FinalView { 108 return false 109 } 110 if setup.TargetDuration != other.TargetDuration { 111 return false 112 } 113 if setup.TargetEndTime != other.TargetEndTime { 114 return false 115 } 116 if !IdentitySkeletonListEqualTo(setup.Participants, other.Participants) { 117 return false 118 } 119 if !setup.Assignments.EqualTo(other.Assignments) { 120 return false 121 } 122 return bytes.Equal(setup.RandomSource, other.RandomSource) 123 } 124 125 // EpochCommit is a service event emitted when epoch setup has been completed. 126 // When an EpochCommit event is emitted, the network is ready to transition to 127 // the epoch. 128 type EpochCommit struct { 129 Counter uint64 // the number of the epoch 130 ClusterQCs []ClusterQCVoteData // quorum certificates for each cluster 131 DKGGroupKey crypto.PublicKey // group key from DKG 132 DKGParticipantKeys []crypto.PublicKey // public keys for DKG participants 133 } 134 135 // ClusterQCVoteData represents the votes for a cluster quorum certificate, as 136 // gathered by the ClusterQC smart contract. It contains the aggregated 137 // signature over the root block for the cluster as well as the set of voters. 138 type ClusterQCVoteData struct { 139 SigData crypto.Signature // the aggregated signature over all the votes 140 VoterIDs []Identifier // the set of voters that contributed to the qc 141 } 142 143 func (c *ClusterQCVoteData) EqualTo(other *ClusterQCVoteData) bool { 144 if len(c.VoterIDs) != len(other.VoterIDs) { 145 return false 146 } 147 if !bytes.Equal(c.SigData, other.SigData) { 148 return false 149 } 150 for i, v := range c.VoterIDs { 151 if v != other.VoterIDs[i] { 152 return false 153 } 154 } 155 return true 156 } 157 158 // ClusterQCVoteDataFromQC converts a quorum certificate to the representation 159 // used by the smart contract, essentially discarding the block ID and view 160 // (which are protocol-defined given the EpochSetup event). 161 func ClusterQCVoteDataFromQC(qc *QuorumCertificateWithSignerIDs) ClusterQCVoteData { 162 return ClusterQCVoteData{ 163 SigData: qc.SigData, 164 VoterIDs: qc.SignerIDs, 165 } 166 } 167 168 func ClusterQCVoteDatasFromQCs(qcs []*QuorumCertificateWithSignerIDs) []ClusterQCVoteData { 169 qcVotes := make([]ClusterQCVoteData, 0, len(qcs)) 170 for _, qc := range qcs { 171 qcVotes = append(qcVotes, ClusterQCVoteDataFromQC(qc)) 172 } 173 return qcVotes 174 } 175 176 func (commit *EpochCommit) ServiceEvent() ServiceEvent { 177 return ServiceEvent{ 178 Type: ServiceEventCommit, 179 Event: commit, 180 } 181 } 182 183 type encodableCommit struct { 184 Counter uint64 185 ClusterQCs []ClusterQCVoteData 186 DKGGroupKey encodable.RandomBeaconPubKey 187 DKGParticipantKeys []encodable.RandomBeaconPubKey 188 } 189 190 func encodableFromCommit(commit *EpochCommit) encodableCommit { 191 encKeys := make([]encodable.RandomBeaconPubKey, 0, len(commit.DKGParticipantKeys)) 192 for _, key := range commit.DKGParticipantKeys { 193 encKeys = append(encKeys, encodable.RandomBeaconPubKey{PublicKey: key}) 194 } 195 return encodableCommit{ 196 Counter: commit.Counter, 197 ClusterQCs: commit.ClusterQCs, 198 DKGGroupKey: encodable.RandomBeaconPubKey{PublicKey: commit.DKGGroupKey}, 199 DKGParticipantKeys: encKeys, 200 } 201 } 202 203 func commitFromEncodable(enc encodableCommit) EpochCommit { 204 dkgKeys := make([]crypto.PublicKey, 0, len(enc.DKGParticipantKeys)) 205 for _, key := range enc.DKGParticipantKeys { 206 dkgKeys = append(dkgKeys, key.PublicKey) 207 } 208 return EpochCommit{ 209 Counter: enc.Counter, 210 ClusterQCs: enc.ClusterQCs, 211 DKGGroupKey: enc.DKGGroupKey.PublicKey, 212 DKGParticipantKeys: dkgKeys, 213 } 214 } 215 216 func (commit EpochCommit) MarshalJSON() ([]byte, error) { 217 return json.Marshal(encodableFromCommit(&commit)) 218 } 219 220 func (commit *EpochCommit) UnmarshalJSON(b []byte) error { 221 var enc encodableCommit 222 err := json.Unmarshal(b, &enc) 223 if err != nil { 224 return err 225 } 226 227 *commit = commitFromEncodable(enc) 228 return nil 229 } 230 231 func (commit *EpochCommit) MarshalCBOR() ([]byte, error) { 232 return cbor.Marshal(encodableFromCommit(commit)) 233 } 234 235 func (commit *EpochCommit) UnmarshalCBOR(b []byte) error { 236 var enc encodableCommit 237 err := cbor.Unmarshal(b, &enc) 238 if err != nil { 239 return err 240 } 241 242 *commit = commitFromEncodable(enc) 243 return nil 244 } 245 246 func (commit *EpochCommit) MarshalMsgpack() ([]byte, error) { 247 return msgpack.Marshal(encodableFromCommit(commit)) 248 } 249 250 func (commit *EpochCommit) UnmarshalMsgpack(b []byte) error { 251 var enc encodableCommit 252 err := msgpack.Unmarshal(b, &enc) 253 if err != nil { 254 return err 255 } 256 *commit = commitFromEncodable(enc) 257 return nil 258 } 259 260 // EncodeRLP encodes the commit as RLP. The RLP encoding needs to be handled 261 // differently from JSON/msgpack, because it does not handle custom encoders 262 // within map types. 263 // NOTE: DecodeRLP is not needed, as this is only used for hashing. 264 func (commit *EpochCommit) EncodeRLP(w io.Writer) error { 265 rlpEncodable := struct { 266 Counter uint64 267 ClusterQCs []ClusterQCVoteData 268 DKGGroupKey []byte 269 DKGParticipantKeys [][]byte 270 }{ 271 Counter: commit.Counter, 272 ClusterQCs: commit.ClusterQCs, 273 DKGGroupKey: commit.DKGGroupKey.Encode(), 274 DKGParticipantKeys: make([][]byte, 0, len(commit.DKGParticipantKeys)), 275 } 276 for _, key := range commit.DKGParticipantKeys { 277 rlpEncodable.DKGParticipantKeys = append(rlpEncodable.DKGParticipantKeys, key.Encode()) 278 } 279 280 return rlp.Encode(w, rlpEncodable) 281 } 282 283 // ID returns the hash of the event contents. 284 func (commit *EpochCommit) ID() Identifier { 285 return MakeID(commit) 286 } 287 288 func (commit *EpochCommit) EqualTo(other *EpochCommit) bool { 289 if commit.Counter != other.Counter { 290 return false 291 } 292 if len(commit.ClusterQCs) != len(other.ClusterQCs) { 293 return false 294 } 295 for i, qc := range commit.ClusterQCs { 296 if !qc.EqualTo(&other.ClusterQCs[i]) { 297 return false 298 } 299 } 300 if (commit.DKGGroupKey == nil && other.DKGGroupKey != nil) || 301 (commit.DKGGroupKey != nil && other.DKGGroupKey == nil) { 302 return false 303 } 304 if commit.DKGGroupKey != nil && other.DKGGroupKey != nil && !commit.DKGGroupKey.Equals(other.DKGGroupKey) { 305 return false 306 } 307 if len(commit.DKGParticipantKeys) != len(other.DKGParticipantKeys) { 308 return false 309 } 310 311 for i, key := range commit.DKGParticipantKeys { 312 if !key.Equals(other.DKGParticipantKeys[i]) { 313 return false 314 } 315 } 316 317 return true 318 } 319 320 // ToDKGParticipantLookup constructs a DKG participant lookup from an identity 321 // list and a key list. The identity list must be EXACTLY the same (order and 322 // contents) as that used when initializing the corresponding DKG instance. 323 func ToDKGParticipantLookup(participants IdentitySkeletonList, keys []crypto.PublicKey) (map[Identifier]DKGParticipant, error) { 324 if len(participants) != len(keys) { 325 return nil, fmt.Errorf("participant list (len=%d) does not match key list (len=%d)", len(participants), len(keys)) 326 } 327 328 lookup := make(map[Identifier]DKGParticipant, len(participants)) 329 for i := 0; i < len(participants); i++ { 330 part := participants[i] 331 key := keys[i] 332 lookup[part.NodeID] = DKGParticipant{ 333 Index: uint(i), 334 KeyShare: key, 335 } 336 } 337 return lookup, nil 338 } 339 340 type DKGParticipant struct { 341 Index uint 342 KeyShare crypto.PublicKey 343 } 344 345 type encodableDKGParticipant struct { 346 Index uint 347 KeyShare encodable.RandomBeaconPubKey 348 } 349 350 func encodableFromDKGParticipant(part DKGParticipant) encodableDKGParticipant { 351 return encodableDKGParticipant{ 352 Index: part.Index, 353 KeyShare: encodable.RandomBeaconPubKey{PublicKey: part.KeyShare}, 354 } 355 } 356 357 func dkgParticipantFromEncodable(enc encodableDKGParticipant) DKGParticipant { 358 return DKGParticipant{ 359 Index: enc.Index, 360 KeyShare: enc.KeyShare.PublicKey, 361 } 362 } 363 364 func (part DKGParticipant) MarshalJSON() ([]byte, error) { 365 enc := encodableFromDKGParticipant(part) 366 return json.Marshal(enc) 367 } 368 369 func (part *DKGParticipant) UnmarshalJSON(b []byte) error { 370 var enc encodableDKGParticipant 371 err := json.Unmarshal(b, &enc) 372 if err != nil { 373 return err 374 } 375 376 *part = dkgParticipantFromEncodable(enc) 377 return nil 378 } 379 380 func (part DKGParticipant) MarshalCBOR() ([]byte, error) { 381 enc := encodableFromDKGParticipant(part) 382 return cbor.Marshal(enc) 383 } 384 385 func (part *DKGParticipant) UnmarshalCBOR(b []byte) error { 386 var enc encodableDKGParticipant 387 err := cbor.Unmarshal(b, &enc) 388 if err != nil { 389 return err 390 } 391 392 *part = dkgParticipantFromEncodable(enc) 393 return nil 394 } 395 396 func (part DKGParticipant) MarshalMsgpack() ([]byte, error) { 397 return msgpack.Marshal(encodableFromDKGParticipant(part)) 398 } 399 400 func (part *DKGParticipant) UnmarshalMsgpack(b []byte) error { 401 var enc encodableDKGParticipant 402 err := msgpack.Unmarshal(b, &enc) 403 if err != nil { 404 return err 405 } 406 *part = dkgParticipantFromEncodable(enc) 407 return nil 408 } 409 410 func (part DKGParticipant) EncodeRLP(w io.Writer) error { 411 return rlp.Encode(w, encodableFromDKGParticipant(part)) 412 } 413 414 // EventIDs is a container for IDs of epoch service events. 415 type EventIDs struct { 416 // SetupID is the ID of the EpochSetup event for the respective Epoch 417 SetupID Identifier 418 // CommitID is the ID of the EpochCommit event for the respective Epoch 419 CommitID Identifier 420 } 421 422 // ID returns hash of the event IDs. 423 func (e *EventIDs) ID() Identifier { 424 return MakeID(e) 425 }