11package badger
22
33import (
4+ "errors"
45 "fmt"
56
67 "github.com/onflow/flow-go/model/cluster"
78 "github.com/onflow/flow-go/model/flow"
9+ "github.com/onflow/flow-go/module/irrecoverable"
810 clusterState "github.com/onflow/flow-go/state/cluster"
11+ "github.com/onflow/flow-go/storage"
912 "github.com/onflow/flow-go/storage/operation"
1013 "github.com/onflow/flow-go/storage/procedure"
1114)
1215
13- // Snapshot represents a snapshot of chain state anchored at a particular
14- // reference block.
16+ // Snapshot pertains to a specific fork of the collector cluster consensus. Specifically,
17+ // it references one block denoted as the `Head`. This Snapshot type is for collector
18+ // clusters, so we are referencing a cluster block, aka collection, here.
19+ //
20+ // This implementation must be used for KNOWN reference BLOCKs only.
1521type Snapshot struct {
16- err error
1722 state * State
1823 blockID flow.Identifier
1924}
2025
2126var _ clusterState.Snapshot = (* Snapshot )(nil )
2227
23- func (s * Snapshot ) Collection () (* flow.Collection , error ) {
24- if s .err != nil {
25- return nil , s .err
28+ // newSnapshot instantiates a new snapshot for the given collection ID.
29+ // CAUTION: This constructor must be called for KNOWN blocks.
30+ // For unknown blocks, please use `invalid.NewSnapshot` or `invalid.NewSnapshotf`.
31+ func newSnapshot (state * State , blockID flow.Identifier ) * Snapshot {
32+ return & Snapshot {
33+ state : state ,
34+ blockID : blockID ,
2635 }
36+ }
2737
38+ // Collection returns the collection designated as the reference for this
39+ // snapshot. Technically, this is a portion of the payload of a cluster block.
40+ //
41+ // By contract of the constructor, the blockID must correspond to a known collection in the database.
42+ // No error returns are expected during normal operation.
43+ func (s * Snapshot ) Collection () (* flow.Collection , error ) {
2844 // get the payload
2945 var payload cluster.Payload
3046 err := procedure .RetrieveClusterPayload (s .state .db .Reader (), s .blockID , & payload )
@@ -36,38 +52,50 @@ func (s *Snapshot) Collection() (*flow.Collection, error) {
3652 return & collection , nil
3753}
3854
55+ // Head returns the header of the collection that designated as the reference for this
56+ // snapshot.
57+ //
58+ // By contract of the constructor, the blockID must correspond to a known collection in the database.
59+ // No error returns are expected during normal operation.
3960func (s * Snapshot ) Head () (* flow.Header , error ) {
40- if s .err != nil {
41- return nil , s .err
42- }
43-
4461 var head flow.Header
45- err := s .head (& head )
62+ err := operation .RetrieveHeader (s .state .db .Reader (), s .blockID , & head )
63+ if err != nil {
64+ // `storage.ErrNotFound` is the only error that the storage layer may return other than exceptions.
65+ // In the context of this call, `s.blockID` should correspond to a known block, so receiving a
66+ // `storage.ErrNotFound` is an exception here.
67+ return nil , irrecoverable .NewExceptionf ("could not retrieve header for block (%s): %w" , s .blockID , err )
68+ }
4669 return & head , err
4770}
4871
72+ // Pending returns the IDs of all collections descending from the snapshot's head collection.
73+ // The result is ordered such that parents are included before their children. While only valid
74+ // descendants will be returned, note that the descendants may not be finalized yet.
75+ // By contract of the constructor, the blockID must correspond to a known collection in the database.
76+ // No error returns are expected during normal operation.
4977func (s * Snapshot ) Pending () ([]flow.Identifier , error ) {
50- if s .err != nil {
51- return nil , s .err
52- }
5378 return s .pending (s .blockID )
5479}
5580
56- // head finds the header referenced by the snapshot.
57- func (s * Snapshot ) head (head * flow.Header ) error {
58- // get the snapshot header
59- err := operation .RetrieveHeader (s .state .db .Reader (), s .blockID , head )
60- if err != nil {
61- return fmt .Errorf ("could not retrieve header for block (%s): %w" , s .blockID , err )
62- }
63- return nil
64- }
65-
81+ // pending returns a slice with all blocks descending from the given blockID (children, grandchildren, etc).
82+ // CAUTION: this function behaves only correctly for known blocks, which should always be the case as
83+ // required by the constructor.
84+ // No error returns are expected during normal operation.
6685func (s * Snapshot ) pending (blockID flow.Identifier ) ([]flow.Identifier , error ) {
6786 var pendingIDs flow.IdentifierList
68- err := procedure . LookupBlockChildren (s .state .db .Reader (), blockID , & pendingIDs )
87+ err := operation . RetrieveBlockChildren (s .state .db .Reader (), blockID , & pendingIDs )
6988 if err != nil {
70- return nil , fmt .Errorf ("could not get pending children: %w" , err )
89+ if ! errors .Is (err , storage .ErrNotFound ) {
90+ return nil , fmt .Errorf ("could not get pending block %v: %w" , blockID , err )
91+ }
92+
93+ // The low-level storage returns `storage.ErrNotFound` in two cases:
94+ // 1. the block/collection is unknown
95+ // 2. the block/collection is known but no children have been indexed yet
96+ // By contract of the constructor, the blockID must correspond to a known collection in the database.
97+ // A snapshot with s.err == nil is only created for known blocks. Hence, only case 2 is
98+ // possible here, and we just return an empty list.
7199 }
72100
73101 for _ , pendingID := range pendingIDs {
0 commit comments