github.com/koko1123/flow-go-1@v0.29.6/state/protocol/badger/snapshot.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package badger 4 5 import ( 6 "errors" 7 "fmt" 8 9 "github.com/koko1123/flow-go-1/model/flow" 10 "github.com/koko1123/flow-go-1/model/flow/filter" 11 "github.com/koko1123/flow-go-1/model/flow/mapfunc" 12 "github.com/koko1123/flow-go-1/model/flow/order" 13 "github.com/koko1123/flow-go-1/state" 14 "github.com/koko1123/flow-go-1/state/fork" 15 "github.com/koko1123/flow-go-1/state/protocol" 16 "github.com/koko1123/flow-go-1/state/protocol/inmem" 17 "github.com/koko1123/flow-go-1/state/protocol/invalid" 18 "github.com/koko1123/flow-go-1/state/protocol/seed" 19 "github.com/koko1123/flow-go-1/storage" 20 "github.com/koko1123/flow-go-1/storage/badger/operation" 21 "github.com/koko1123/flow-go-1/storage/badger/procedure" 22 ) 23 24 // Snapshot implements the protocol.Snapshot interface. 25 // It represents a read-only immutable snapshot of the protocol state at the 26 // block it is constructed with. It allows efficient access to data associated directly 27 // with blocks at a given state (finalized, sealed), such as the related header, commit, 28 // seed or descending blocks. A block snapshot can lazily convert to an epoch snapshot in 29 // order to make data associated directly with epochs accessible through its API. 30 type Snapshot struct { 31 state *State 32 blockID flow.Identifier // reference block for this snapshot 33 } 34 35 func NewSnapshot(state *State, blockID flow.Identifier) *Snapshot { 36 return &Snapshot{ 37 state: state, 38 blockID: blockID, 39 } 40 } 41 42 func (s *Snapshot) Head() (*flow.Header, error) { 43 head, err := s.state.headers.ByBlockID(s.blockID) 44 return head, err 45 } 46 47 // QuorumCertificate (QC) returns a valid quorum certificate pointing to the 48 // header at this snapshot. With the exception of the root block, a valid child 49 // block must be which contains the desired QC. The sentinel error 50 // state.NoValidChildBlockError is returned if the the QC is unknown. 51 // 52 // For root block snapshots, returns the root quorum certificate. For all other 53 // blocks, generates a quorum certificate from a valid child, if one exists. 54 func (s *Snapshot) QuorumCertificate() (*flow.QuorumCertificate, error) { 55 56 // CASE 1: for the root block, return the root QC 57 root, err := s.state.Params().Root() 58 if err != nil { 59 return nil, fmt.Errorf("could not get root: %w", err) 60 } 61 62 if s.blockID == root.ID() { 63 var rootQC flow.QuorumCertificate 64 err := s.state.db.View(operation.RetrieveRootQuorumCertificate(&rootQC)) 65 if err != nil { 66 return nil, fmt.Errorf("could not retrieve root qc: %w", err) 67 } 68 return &rootQC, nil 69 } 70 71 // CASE 2: for any other block, generate the root QC from a valid child 72 child, err := s.validChild() 73 if err != nil { 74 return nil, fmt.Errorf("could not get valid child of block %x: %w", s.blockID, err) 75 } 76 77 // sanity check: ensure the child has the snapshot block as parent 78 if child.ParentID != s.blockID { 79 return nil, fmt.Errorf("child parent id (%x) does not match snapshot id (%x)", child.ParentID, s.blockID) 80 } 81 82 // retrieve the full header as we need the view for the quorum certificate 83 head, err := s.Head() 84 if err != nil { 85 return nil, fmt.Errorf("could not get head: %w", err) 86 } 87 88 qc := &flow.QuorumCertificate{ 89 View: head.View, 90 BlockID: s.blockID, 91 SignerIndices: child.ParentVoterIndices, 92 SigData: child.ParentVoterSigData, 93 } 94 95 return qc, nil 96 } 97 98 // validChild returns a child of the snapshot head that has been validated 99 // by HotStuff. Returns state.NoValidChildBlockError if no valid child exists. 100 // 101 // Any valid child may be returned. Subsequent calls are not guaranteed to 102 // return the same child. 103 func (s *Snapshot) validChild() (*flow.Header, error) { 104 105 var childIDs flow.IdentifierList 106 err := s.state.db.View(procedure.LookupBlockChildren(s.blockID, &childIDs)) 107 if err != nil { 108 return nil, fmt.Errorf("could not look up children: %w", err) 109 } 110 111 // find the first child that has been validated 112 validChildID := flow.ZeroID 113 for _, childID := range childIDs { 114 var valid bool 115 err = s.state.db.View(operation.RetrieveBlockValidity(childID, &valid)) 116 // skip blocks whose validity hasn't been checked yet 117 if errors.Is(err, storage.ErrNotFound) { 118 continue 119 } 120 if err != nil { 121 return nil, fmt.Errorf("failed to determine validity of child block %v: %w", childID, err) 122 } 123 if valid { 124 validChildID = childID 125 break 126 } 127 } 128 129 if validChildID == flow.ZeroID { 130 return nil, state.NewNoValidChildBlockErrorf("block has no valid children (total children: %d)", len(childIDs)) 131 } 132 133 // get the header of the first child 134 child, err := s.state.headers.ByBlockID(validChildID) 135 return child, err 136 } 137 138 func (s *Snapshot) Phase() (flow.EpochPhase, error) { 139 status, err := s.state.epoch.statuses.ByBlockID(s.blockID) 140 if err != nil { 141 return flow.EpochPhaseUndefined, fmt.Errorf("could not retrieve epoch status: %w", err) 142 } 143 phase, err := status.Phase() 144 return phase, err 145 } 146 147 func (s *Snapshot) Identities(selector flow.IdentityFilter) (flow.IdentityList, error) { 148 149 // TODO: CAUTION SHORTCUT 150 // we retrieve identities based on the initial identity table from the EpochSetup 151 // event here -- this will need revision to support mid-epoch identity changes 152 // once slashing is implemented 153 154 status, err := s.state.epoch.statuses.ByBlockID(s.blockID) 155 if err != nil { 156 return nil, err 157 } 158 159 setup, err := s.state.epoch.setups.ByID(status.CurrentEpoch.SetupID) 160 if err != nil { 161 return nil, err 162 } 163 164 // sort the identities so the 'Exists' binary search works 165 identities := setup.Participants.Sort(order.Canonical) 166 167 // get identities that are in either last/next epoch but NOT in the current epoch 168 var otherEpochIdentities flow.IdentityList 169 phase, err := status.Phase() 170 if err != nil { 171 return nil, fmt.Errorf("could not get phase: %w", err) 172 } 173 switch phase { 174 // during staking phase (the beginning of the epoch) we include identities 175 // from the previous epoch that are now un-staking 176 case flow.EpochPhaseStaking: 177 178 if !status.HasPrevious() { 179 break 180 } 181 182 previousSetup, err := s.state.epoch.setups.ByID(status.PreviousEpoch.SetupID) 183 if err != nil { 184 return nil, fmt.Errorf("could not get previous epoch setup event: %w", err) 185 } 186 187 for _, identity := range previousSetup.Participants { 188 exists := identities.Exists(identity) 189 // add identity from previous epoch that is not in current epoch 190 if !exists { 191 otherEpochIdentities = append(otherEpochIdentities, identity) 192 } 193 } 194 195 // during setup and committed phases (the end of the epoch) we include 196 // identities that will join in the next epoch 197 case flow.EpochPhaseSetup, flow.EpochPhaseCommitted: 198 199 nextSetup, err := s.state.epoch.setups.ByID(status.NextEpoch.SetupID) 200 if err != nil { 201 return nil, fmt.Errorf("could not get next epoch setup: %w", err) 202 } 203 204 for _, identity := range nextSetup.Participants { 205 exists := identities.Exists(identity) 206 207 // add identity from next epoch that is not in current epoch 208 if !exists { 209 otherEpochIdentities = append(otherEpochIdentities, identity) 210 } 211 } 212 213 default: 214 return nil, fmt.Errorf("invalid epoch phase: %s", phase) 215 } 216 217 // add the identities from next/last epoch, with weight set to 0 218 identities = append( 219 identities, 220 otherEpochIdentities.Map(mapfunc.WithWeight(0))..., 221 ) 222 223 // apply the filter to the participants 224 identities = identities.Filter(selector) 225 226 // apply a deterministic sort to the participants 227 identities = identities.Sort(order.Canonical) 228 229 return identities, nil 230 } 231 232 func (s *Snapshot) Identity(nodeID flow.Identifier) (*flow.Identity, error) { 233 // filter identities at snapshot for node ID 234 identities, err := s.Identities(filter.HasNodeID(nodeID)) 235 if err != nil { 236 return nil, fmt.Errorf("could not get identities: %w", err) 237 } 238 239 // check if node ID is part of identities 240 if len(identities) == 0 { 241 return nil, protocol.IdentityNotFoundError{NodeID: nodeID} 242 } 243 return identities[0], nil 244 } 245 246 // Commit retrieves the latest execution state commitment at the current block snapshot. This 247 // commitment represents the execution state as currently finalized. 248 func (s *Snapshot) Commit() (flow.StateCommitment, error) { 249 // get the ID of the sealed block 250 seal, err := s.state.seals.HighestInFork(s.blockID) 251 if err != nil { 252 return flow.DummyStateCommitment, fmt.Errorf("could not retrieve sealed state commit: %w", err) 253 } 254 return seal.FinalState, nil 255 } 256 257 func (s *Snapshot) SealedResult() (*flow.ExecutionResult, *flow.Seal, error) { 258 seal, err := s.state.seals.HighestInFork(s.blockID) 259 if err != nil { 260 return nil, nil, fmt.Errorf("could not look up latest seal: %w", err) 261 } 262 result, err := s.state.results.ByID(seal.ResultID) 263 if err != nil { 264 return nil, nil, fmt.Errorf("could not get latest result: %w", err) 265 } 266 return result, seal, nil 267 } 268 269 // SealingSegment will walk through the chain backward until we reach the block referenced 270 // by the latest seal and build a SealingSegment. As we visit each block we check each execution 271 // receipt in the block's payload to make sure we have a corresponding execution result, any execution 272 // results missing from blocks are stored in the SealingSegment.ExecutionResults field. 273 func (s *Snapshot) SealingSegment() (*flow.SealingSegment, error) { 274 var rootHeight uint64 275 err := s.state.db.View(operation.RetrieveRootHeight(&rootHeight)) 276 if err != nil { 277 return nil, fmt.Errorf("could not get root height: %w", err) 278 } 279 head, err := s.Head() 280 if err != nil { 281 return nil, fmt.Errorf("could not get snapshot reference block: %w", err) 282 } 283 if head.Height < rootHeight { 284 return nil, protocol.ErrSealingSegmentBelowRootBlock 285 } 286 287 seal, err := s.state.seals.HighestInFork(s.blockID) 288 if err != nil { 289 return nil, fmt.Errorf("could not get seal for sealing segment: %w", err) 290 } 291 292 // walk through the chain backward until we reach the block referenced by 293 // the latest seal - the returned segment includes this block 294 builder := flow.NewSealingSegmentBuilder(s.state.results.ByID, s.state.seals.HighestInFork) 295 scraper := func(header *flow.Header) error { 296 blockID := header.ID() 297 block, err := s.state.blocks.ByID(blockID) 298 if err != nil { 299 return fmt.Errorf("could not get block: %w", err) 300 } 301 302 err = builder.AddBlock(block) 303 if err != nil { 304 return fmt.Errorf("could not add block to sealing segment: %w", err) 305 } 306 307 return nil 308 } 309 310 err = fork.TraverseForward(s.state.headers, s.blockID, scraper, fork.IncludingBlock(seal.BlockID)) 311 if err != nil { 312 return nil, fmt.Errorf("could not traverse sealing segment: %w", err) 313 } 314 315 segment, err := builder.SealingSegment() 316 if err != nil { 317 return nil, fmt.Errorf("could not build sealing segment: %w", err) 318 } 319 320 return segment, nil 321 } 322 323 func (s *Snapshot) Descendants() ([]flow.Identifier, error) { 324 descendants, err := s.descendants(s.blockID) 325 if err != nil { 326 return nil, fmt.Errorf("failed to traverse the descendants tree of block %v: %w", s.blockID, err) 327 } 328 return descendants, nil 329 } 330 331 func (s *Snapshot) ValidDescendants() ([]flow.Identifier, error) { 332 valid, err := s.lookupValidity(s.blockID) 333 if err != nil { 334 return nil, fmt.Errorf("could not determine validity of block %v: %w", s.blockID, err) 335 } 336 if !valid { 337 return []flow.Identifier{}, nil 338 } 339 340 descendants, err := s.validDescendants(s.blockID) 341 if err != nil { 342 return nil, fmt.Errorf("failed to traverse the descendants tree of block %v: %w", s.blockID, err) 343 } 344 return descendants, nil 345 } 346 347 func (s *Snapshot) lookupChildren(blockID flow.Identifier) ([]flow.Identifier, error) { 348 var children flow.IdentifierList 349 err := s.state.db.View(procedure.LookupBlockChildren(blockID, &children)) 350 if err != nil { 351 return nil, fmt.Errorf("could not get children of block %v: %w", blockID, err) 352 } 353 return children, nil 354 } 355 356 func (s *Snapshot) lookupValidity(blockID flow.Identifier) (bool, error) { 357 valid := false 358 err := s.state.db.View(operation.RetrieveBlockValidity(blockID, &valid)) 359 if err != nil { 360 // We only store the validity flag for blocks that have been marked valid. 361 // For blocks that haven't been marked valid (yet), the flag is simply absent. 362 if !errors.Is(err, storage.ErrNotFound) { 363 return false, fmt.Errorf("could not retrieve validity of block %v: %w", blockID, err) 364 } 365 } 366 return valid, nil 367 } 368 369 func (s *Snapshot) validDescendants(blockID flow.Identifier) ([]flow.Identifier, error) { 370 var descendantIDs []flow.Identifier 371 372 children, err := s.lookupChildren(blockID) 373 if err != nil { 374 return nil, err 375 } 376 377 for _, descendantID := range children { 378 valid, err := s.lookupValidity(descendantID) 379 if err != nil { 380 return nil, err 381 } 382 383 if valid { 384 descendantIDs = append(descendantIDs, descendantID) 385 additionalIDs, err := s.validDescendants(descendantID) 386 if err != nil { 387 return nil, err 388 } 389 descendantIDs = append(descendantIDs, additionalIDs...) 390 } 391 } 392 return descendantIDs, nil 393 } 394 395 func (s *Snapshot) descendants(blockID flow.Identifier) ([]flow.Identifier, error) { 396 descendantIDs, err := s.lookupChildren(blockID) 397 if err != nil { 398 return nil, err 399 } 400 401 for _, descendantID := range descendantIDs { 402 additionalIDs, err := s.descendants(descendantID) 403 if err != nil { 404 return nil, err 405 } 406 descendantIDs = append(descendantIDs, additionalIDs...) 407 } 408 return descendantIDs, nil 409 } 410 411 // RandomSource returns the seed for the current block snapshot. 412 // Expected error returns: 413 // * state.NoValidChildBlockError if no valid child is known 414 func (s *Snapshot) RandomSource() ([]byte, error) { 415 416 // CASE 1: for the root block, generate the seed from the root qc 417 root, err := s.state.Params().Root() 418 if err != nil { 419 return nil, fmt.Errorf("could not get root: %w", err) 420 } 421 422 if s.blockID == root.ID() { 423 var rootQC flow.QuorumCertificate 424 err := s.state.db.View(operation.RetrieveRootQuorumCertificate(&rootQC)) 425 if err != nil { 426 return nil, fmt.Errorf("could not retrieve root qc: %w", err) 427 } 428 429 seed, err := seed.FromParentQCSignature(rootQC.SigData) 430 if err != nil { 431 return nil, fmt.Errorf("could not create seed from root qc: %w", err) 432 } 433 return seed, nil 434 } 435 436 // CASE 2: for any other block, use any valid child 437 child, err := s.validChild() 438 if err != nil { 439 return nil, fmt.Errorf("failed to get valid child of block %x: %w", s.blockID, err) 440 } 441 442 seed, err := seed.FromParentQCSignature(child.ParentVoterSigData) 443 if err != nil { 444 return nil, fmt.Errorf("could not create seed from header's signature: %w", err) 445 } 446 447 return seed, nil 448 } 449 450 func (s *Snapshot) Epochs() protocol.EpochQuery { 451 return &EpochQuery{ 452 snap: s, 453 } 454 } 455 456 func (s *Snapshot) Params() protocol.GlobalParams { 457 return s.state.Params() 458 } 459 460 // EpochQuery encapsulates querying epochs w.r.t. a snapshot. 461 type EpochQuery struct { 462 snap *Snapshot 463 } 464 465 // Current returns the current epoch. 466 func (q *EpochQuery) Current() protocol.Epoch { 467 468 // all errors returned from storage reads here are unexpected, because all 469 // snapshots reside within a current epoch, which must be queriable 470 status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) 471 if err != nil { 472 return invalid.NewEpochf("could not get epoch status for block %x: %w", q.snap.blockID, err) 473 } 474 setup, err := q.snap.state.epoch.setups.ByID(status.CurrentEpoch.SetupID) 475 if err != nil { 476 return invalid.NewEpochf("could not get current EpochSetup (id=%x) for block %x: %w", status.CurrentEpoch.SetupID, q.snap.blockID, err) 477 } 478 commit, err := q.snap.state.epoch.commits.ByID(status.CurrentEpoch.CommitID) 479 if err != nil { 480 return invalid.NewEpochf("could not get current EpochCommit (id=%x) for block %x: %w", status.CurrentEpoch.CommitID, q.snap.blockID, err) 481 } 482 483 epoch, err := inmem.NewCommittedEpoch(setup, commit) 484 if err != nil { 485 // all conversion errors are critical and indicate we have stored invalid epoch info - strip error type info 486 return invalid.NewEpochf("could not convert current epoch at block %x: %s", q.snap.blockID, err.Error()) 487 } 488 return epoch 489 } 490 491 // Next returns the next epoch, if it is available. 492 func (q *EpochQuery) Next() protocol.Epoch { 493 494 status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) 495 if err != nil { 496 return invalid.NewEpochf("could not get epoch status for block %x: %w", q.snap.blockID, err) 497 } 498 phase, err := status.Phase() 499 if err != nil { 500 // critical error: malformed EpochStatus in storage 501 return invalid.NewEpochf("read malformed EpochStatus from storage: %w", err) 502 } 503 // if we are in the staking phase, the next epoch is not setup yet 504 if phase == flow.EpochPhaseStaking { 505 return invalid.NewEpoch(protocol.ErrNextEpochNotSetup) 506 } 507 508 // if we are in setup phase, return a SetupEpoch 509 nextSetup, err := q.snap.state.epoch.setups.ByID(status.NextEpoch.SetupID) 510 if err != nil { 511 // all errors are critical, because we must be able to retrieve EpochSetup when in setup phase 512 return invalid.NewEpochf("could not get next EpochSetup (id=%x) for block %x: %w", status.NextEpoch.SetupID, q.snap.blockID, err) 513 } 514 if phase == flow.EpochPhaseSetup { 515 epoch, err := inmem.NewSetupEpoch(nextSetup) 516 if err != nil { 517 // all conversion errors are critical and indicate we have stored invalid epoch info - strip error type info 518 return invalid.NewEpochf("could not convert next (setup) epoch: %s", err.Error()) 519 } 520 return epoch 521 } 522 523 // if we are in committed phase, return a CommittedEpoch 524 nextCommit, err := q.snap.state.epoch.commits.ByID(status.NextEpoch.CommitID) 525 if err != nil { 526 // all errors are critical, because we must be able to retrieve EpochCommit when in committed phase 527 return invalid.NewEpochf("could not get next EpochCommit (id=%x) for block %x: %w", status.NextEpoch.CommitID, q.snap.blockID, err) 528 } 529 epoch, err := inmem.NewCommittedEpoch(nextSetup, nextCommit) 530 if err != nil { 531 // all conversion errors are critical and indicate we have stored invalid epoch info - strip error type info 532 return invalid.NewEpochf("could not convert next (committed) epoch: %s", err.Error()) 533 } 534 return epoch 535 } 536 537 // Previous returns the previous epoch. During the first epoch after the root 538 // block, this returns a sentinel error (since there is no previous epoch). 539 // For all other epochs, returns the previous epoch. 540 func (q *EpochQuery) Previous() protocol.Epoch { 541 542 status, err := q.snap.state.epoch.statuses.ByBlockID(q.snap.blockID) 543 if err != nil { 544 return invalid.NewEpochf("could not get epoch status for block %x: %w", q.snap.blockID, err) 545 } 546 547 // CASE 1: there is no previous epoch - this indicates we are in the first 548 // epoch after a spork root or genesis block 549 if !status.HasPrevious() { 550 return invalid.NewEpoch(protocol.ErrNoPreviousEpoch) 551 } 552 553 // CASE 2: we are in any other epoch - retrieve the setup and commit events 554 // for the previous epoch 555 setup, err := q.snap.state.epoch.setups.ByID(status.PreviousEpoch.SetupID) 556 if err != nil { 557 // all errors are critical, because we must be able to retrieve EpochSetup for previous epoch 558 return invalid.NewEpochf("could not get previous EpochSetup (id=%x) for block %x: %w", status.PreviousEpoch.SetupID, q.snap.blockID, err) 559 } 560 commit, err := q.snap.state.epoch.commits.ByID(status.PreviousEpoch.CommitID) 561 if err != nil { 562 // all errors are critical, because we must be able to retrieve EpochCommit for previous epoch 563 return invalid.NewEpochf("could not get current EpochCommit (id=%x) for block %x: %w", status.PreviousEpoch.CommitID, q.snap.blockID, err) 564 } 565 566 epoch, err := inmem.NewCommittedEpoch(setup, commit) 567 if err != nil { 568 // all conversion errors are critical and indicate we have stored invalid epoch info - strip error type info 569 return invalid.NewEpochf("could not convert previous epoch: %s", err.Error()) 570 } 571 return epoch 572 }