github.com/koko1123/flow-go-1@v0.29.6/state/protocol/badger/validity.go (about) 1 package badger 2 3 import ( 4 "fmt" 5 6 "github.com/koko1123/flow-go-1/consensus/hotstuff/committees" 7 "github.com/koko1123/flow-go-1/consensus/hotstuff/mocks" 8 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/signature" 10 "github.com/koko1123/flow-go-1/consensus/hotstuff/validator" 11 "github.com/koko1123/flow-go-1/consensus/hotstuff/verification" 12 "github.com/koko1123/flow-go-1/model/flow" 13 "github.com/koko1123/flow-go-1/model/flow/factory" 14 "github.com/koko1123/flow-go-1/model/flow/filter" 15 "github.com/koko1123/flow-go-1/model/flow/order" 16 "github.com/koko1123/flow-go-1/state" 17 "github.com/koko1123/flow-go-1/state/protocol" 18 ) 19 20 // isValidExtendingEpochSetup checks whether an epoch setup service being 21 // added to the state is valid. In addition to intrinsic validitym, we also 22 // check that it is valid w.r.t. the previous epoch setup event, and the 23 // current epoch status. 24 func isValidExtendingEpochSetup(extendingSetup *flow.EpochSetup, activeSetup *flow.EpochSetup, status *flow.EpochStatus) error { 25 26 // We should only have a single epoch setup event per epoch. 27 if status.NextEpoch.SetupID != flow.ZeroID { 28 // true iff EpochSetup event for NEXT epoch was already included before 29 return protocol.NewInvalidServiceEventError("duplicate epoch setup service event: %x", status.NextEpoch.SetupID) 30 } 31 32 // The setup event should have the counter increased by one. 33 if extendingSetup.Counter != activeSetup.Counter+1 { 34 return protocol.NewInvalidServiceEventError("next epoch setup has invalid counter (%d => %d)", activeSetup.Counter, extendingSetup.Counter) 35 } 36 37 // The first view needs to be exactly one greater than the current epoch final view 38 if extendingSetup.FirstView != activeSetup.FinalView+1 { 39 return protocol.NewInvalidServiceEventError( 40 "next epoch first view must be exactly 1 more than current epoch final view (%d != %d+1)", 41 extendingSetup.FirstView, 42 activeSetup.FinalView, 43 ) 44 } 45 46 // Finally, the epoch setup event must contain all necessary information. 47 err := isValidEpochSetup(extendingSetup) 48 if err != nil { 49 return protocol.NewInvalidServiceEventError("invalid epoch setup: %w", err) 50 } 51 52 return nil 53 } 54 55 // isValidEpochSetup checks whether an epoch setup service event is intrinsically valid 56 func isValidEpochSetup(setup *flow.EpochSetup) error { 57 return verifyEpochSetup(setup, true) 58 } 59 60 func verifyEpochSetup(setup *flow.EpochSetup, verifyNetworkAddress bool) error { 61 // STEP 1: general sanity checks 62 // the seed needs to be at least minimum length 63 if len(setup.RandomSource) != flow.EpochSetupRandomSourceLength { 64 return fmt.Errorf("seed has incorrect length (%d != %d)", len(setup.RandomSource), flow.EpochSetupRandomSourceLength) 65 } 66 67 // STEP 2: sanity checks of all nodes listed as participants 68 // there should be no duplicate node IDs 69 identLookup := make(map[flow.Identifier]struct{}) 70 for _, participant := range setup.Participants { 71 _, ok := identLookup[participant.NodeID] 72 if ok { 73 return fmt.Errorf("duplicate node identifier (%x)", participant.NodeID) 74 } 75 identLookup[participant.NodeID] = struct{}{} 76 } 77 78 if verifyNetworkAddress { 79 // there should be no duplicate node addresses 80 addrLookup := make(map[string]struct{}) 81 for _, participant := range setup.Participants { 82 _, ok := addrLookup[participant.Address] 83 if ok { 84 return fmt.Errorf("duplicate node address (%x)", participant.Address) 85 } 86 addrLookup[participant.Address] = struct{}{} 87 } 88 } 89 90 // there should be no nodes with zero weight 91 // TODO: we might want to remove the following as we generally want to allow nodes with 92 // zero weight in the protocol state. 93 for _, participant := range setup.Participants { 94 if participant.Weight == 0 { 95 return fmt.Errorf("node with zero weight (%x)", participant.NodeID) 96 } 97 } 98 99 // the participants must be ordered by canonical order 100 if !setup.Participants.Sorted(order.Canonical) { 101 return fmt.Errorf("participants are not canonically ordered") 102 } 103 104 // STEP 3: sanity checks for individual roles 105 // IMPORTANT: here we remove all nodes with zero weight, as they are allowed to partake 106 // in communication but not in respective node functions 107 activeParticipants := setup.Participants.Filter(filter.HasWeight(true)) 108 109 // we need at least one node of each role 110 roles := make(map[flow.Role]uint) 111 for _, participant := range activeParticipants { 112 roles[participant.Role]++ 113 } 114 if roles[flow.RoleConsensus] < 1 { 115 return fmt.Errorf("need at least one consensus node") 116 } 117 if roles[flow.RoleCollection] < 1 { 118 return fmt.Errorf("need at least one collection node") 119 } 120 if roles[flow.RoleExecution] < 1 { 121 return fmt.Errorf("need at least one execution node") 122 } 123 if roles[flow.RoleVerification] < 1 { 124 return fmt.Errorf("need at least one verification node") 125 } 126 127 // first view must be before final view 128 if setup.FirstView >= setup.FinalView { 129 return fmt.Errorf("first view (%d) must be before final view (%d)", setup.FirstView, setup.FinalView) 130 } 131 132 // we need at least one collection cluster 133 if len(setup.Assignments) == 0 { 134 return fmt.Errorf("need at least one collection cluster") 135 } 136 137 // the collection cluster assignments need to be valid 138 _, err := factory.NewClusterList(setup.Assignments, activeParticipants.Filter(filter.HasRole(flow.RoleCollection))) 139 if err != nil { 140 return fmt.Errorf("invalid cluster assignments: %w", err) 141 } 142 143 return nil 144 } 145 146 // isValidExtendingEpochCommit checks whether an epoch commit service being 147 // added to the state is valid. In addition to intrinsic validity, we also 148 // check that it is valid w.r.t. the previous epoch setup event, and the 149 // current epoch status. 150 func isValidExtendingEpochCommit(extendingCommit *flow.EpochCommit, extendingSetup *flow.EpochSetup, activeSetup *flow.EpochSetup, status *flow.EpochStatus) error { 151 152 // We should only have a single epoch commit event per epoch. 153 if status.NextEpoch.CommitID != flow.ZeroID { 154 // true iff EpochCommit event for NEXT epoch was already included before 155 return protocol.NewInvalidServiceEventError("duplicate epoch commit service event: %x", status.NextEpoch.CommitID) 156 } 157 158 // The epoch setup event needs to happen before the commit. 159 if status.NextEpoch.SetupID == flow.ZeroID { 160 return protocol.NewInvalidServiceEventError("missing epoch setup for epoch commit") 161 } 162 163 // The commit event should have the counter increased by one. 164 if extendingCommit.Counter != activeSetup.Counter+1 { 165 return protocol.NewInvalidServiceEventError("next epoch commit has invalid counter (%d => %d)", activeSetup.Counter, extendingCommit.Counter) 166 } 167 168 err := isValidEpochCommit(extendingCommit, extendingSetup) 169 if err != nil { 170 return state.NewInvalidExtensionErrorf("invalid epoch commit: %s", err) 171 } 172 173 return nil 174 } 175 176 // isValidEpochCommit checks whether an epoch commit service event is intrinsically valid. 177 func isValidEpochCommit(commit *flow.EpochCommit, setup *flow.EpochSetup) error { 178 179 if len(setup.Assignments) != len(commit.ClusterQCs) { 180 return fmt.Errorf("number of clusters (%d) does not number of QCs (%d)", len(setup.Assignments), len(commit.ClusterQCs)) 181 } 182 183 if commit.Counter != setup.Counter { 184 return fmt.Errorf("inconsistent epoch counter between commit (%d) and setup (%d) events in same epoch", commit.Counter, setup.Counter) 185 } 186 187 // make sure we have a valid DKG public key 188 if commit.DKGGroupKey == nil { 189 return fmt.Errorf("missing DKG public group key") 190 } 191 192 participants := setup.Participants.Filter(filter.IsValidDKGParticipant) 193 if len(participants) != len(commit.DKGParticipantKeys) { 194 return fmt.Errorf("participant list (len=%d) does not match dkg key list (len=%d)", len(participants), len(commit.DKGParticipantKeys)) 195 } 196 197 return nil 198 } 199 200 // IsValidRootSnapshot checks internal consistency of root state snapshot 201 // if verifyResultID allows/disallows Result ID verification 202 func IsValidRootSnapshot(snap protocol.Snapshot, verifyResultID bool) error { 203 204 segment, err := snap.SealingSegment() 205 if err != nil { 206 return fmt.Errorf("could not get sealing segment: %w", err) 207 } 208 result, seal, err := snap.SealedResult() 209 if err != nil { 210 return fmt.Errorf("could not latest sealed result: %w", err) 211 } 212 213 err = segment.Validate() 214 if err != nil { 215 return fmt.Errorf("invalid root sealing segment: %w", err) 216 } 217 218 highest := segment.Highest() // reference block of the snapshot 219 lowest := segment.Lowest() // last sealed block 220 highestID := highest.ID() 221 lowestID := lowest.ID() 222 223 if result.BlockID != lowestID { 224 return fmt.Errorf("root execution result for wrong block (%x != %x)", result.BlockID, lowest.ID()) 225 } 226 227 if seal.BlockID != lowestID { 228 return fmt.Errorf("root block seal for wrong block (%x != %x)", seal.BlockID, lowest.ID()) 229 } 230 231 if verifyResultID { 232 if seal.ResultID != result.ID() { 233 return fmt.Errorf("root block seal for wrong execution result (%x != %x)", seal.ResultID, result.ID()) 234 } 235 } 236 237 // identities must be canonically ordered 238 identities, err := snap.Identities(filter.Any) 239 if err != nil { 240 return fmt.Errorf("could not get identities for root snapshot: %w", err) 241 } 242 if !identities.Sorted(order.Canonical) { 243 return fmt.Errorf("identities are not canonically ordered") 244 } 245 246 // root qc must be for reference block of snapshot 247 qc, err := snap.QuorumCertificate() 248 if err != nil { 249 return fmt.Errorf("could not get qc for root snapshot: %w", err) 250 } 251 if qc.BlockID != highestID { 252 return fmt.Errorf("qc is for wrong block (got: %x, expected: %x)", qc.BlockID, highestID) 253 } 254 255 firstView, err := snap.Epochs().Current().FirstView() 256 if err != nil { 257 return fmt.Errorf("could not get first view: %w", err) 258 } 259 finalView, err := snap.Epochs().Current().FinalView() 260 if err != nil { 261 return fmt.Errorf("could not get final view: %w", err) 262 } 263 264 // the segment must be fully within the current epoch 265 if firstView > lowest.Header.View { 266 return fmt.Errorf("lowest block of sealing segment has lower view than first view of epoch") 267 } 268 if highest.Header.View >= finalView { 269 return fmt.Errorf("final view of epoch less than first block view") 270 } 271 272 return nil 273 } 274 275 // IsValidRootSnapshotQCs checks internal consistency of QCs that are included in the root state snapshot 276 // It verifies QCs for main consensus and for each collection cluster. 277 func IsValidRootSnapshotQCs(snap protocol.Snapshot) error { 278 // validate main consensus QC 279 err := validateRootQC(snap) 280 if err != nil { 281 return fmt.Errorf("invalid root QC: %w", err) 282 } 283 284 // validate each collection cluster separately 285 curEpoch := snap.Epochs().Current() 286 clusters, err := curEpoch.Clustering() 287 if err != nil { 288 return fmt.Errorf("could not get clustering for root snapshot: %w", err) 289 } 290 for clusterIndex := range clusters { 291 cluster, err := curEpoch.Cluster(uint(clusterIndex)) 292 if err != nil { 293 return fmt.Errorf("could not get cluster %d for root snapshot: %w", clusterIndex, err) 294 } 295 err = validateClusterQC(cluster) 296 if err != nil { 297 return fmt.Errorf("invalid cluster qc %d: %W", clusterIndex, err) 298 } 299 } 300 return nil 301 } 302 303 // validateRootQC performs validation of root QC 304 // Returns nil on success 305 func validateRootQC(snap protocol.Snapshot) error { 306 rootBlock, err := snap.Head() 307 if err != nil { 308 return fmt.Errorf("could not get root block: %w", err) 309 } 310 311 identities, err := snap.Identities(filter.IsVotingConsensusCommitteeMember) 312 if err != nil { 313 return fmt.Errorf("could not get root snapshot identities: %w", err) 314 } 315 316 rootQC, err := snap.QuorumCertificate() 317 if err != nil { 318 return fmt.Errorf("could not get root QC: %w", err) 319 } 320 321 dkg, err := snap.Epochs().Current().DKG() 322 if err != nil { 323 return fmt.Errorf("could not get DKG for root snapshot: %w", err) 324 } 325 326 hotstuffRootBlock := model.GenesisBlockFromFlow(rootBlock) 327 committee, err := committees.NewStaticCommitteeWithDKG(identities, flow.Identifier{}, dkg) 328 if err != nil { 329 return fmt.Errorf("could not create static committee: %w", err) 330 } 331 verifier := verification.NewCombinedVerifier(committee, signature.NewConsensusSigDataPacker(committee)) 332 forks := &mocks.ForksReader{} 333 hotstuffValidator := validator.New(committee, forks, verifier) 334 err = hotstuffValidator.ValidateQC(rootQC, hotstuffRootBlock) 335 if err != nil { 336 return fmt.Errorf("could not validate root qc: %w", err) 337 } 338 return nil 339 } 340 341 // validateClusterQC performs QC validation of single collection cluster 342 // Returns nil on success 343 func validateClusterQC(cluster protocol.Cluster) error { 344 clusterRootBlock := model.GenesisBlockFromFlow(cluster.RootBlock().Header) 345 346 committee, err := committees.NewStaticCommittee(cluster.Members(), flow.Identifier{}, nil, nil) 347 if err != nil { 348 return fmt.Errorf("could not create static committee: %w", err) 349 } 350 verifier := verification.NewStakingVerifier() 351 forks := &mocks.ForksReader{} 352 hotstuffValidator := validator.New(committee, forks, verifier) 353 err = hotstuffValidator.ValidateQC(cluster.RootQC(), clusterRootBlock) 354 if err != nil { 355 return fmt.Errorf("could not validate root qc: %w", err) 356 } 357 return nil 358 }