github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/badger/params.go (about) 1 package badger 2 3 import ( 4 "fmt" 5 6 "github.com/dgraph-io/badger/v2" 7 8 "github.com/onflow/flow-go/model/flow" 9 "github.com/onflow/flow-go/state/protocol" 10 "github.com/onflow/flow-go/state/protocol/inmem" 11 "github.com/onflow/flow-go/storage" 12 "github.com/onflow/flow-go/storage/badger/operation" 13 ) 14 15 type Params struct { 16 protocol.GlobalParams 17 protocol.InstanceParams 18 } 19 20 var _ protocol.Params = (*Params)(nil) 21 22 // InstanceParams implements the interface protocol.InstanceParams. All functions 23 // are served on demand directly from the database, _without_ any caching. 24 type InstanceParams struct { 25 db *badger.DB 26 // finalizedRoot marks the cutoff of the history this node knows about. It is the block at the tip 27 // of the root snapshot used to bootstrap this node - all newer blocks are synced from the network. 28 finalizedRoot *flow.Header 29 // sealedRoot is the latest sealed block with respect to `finalizedRoot`. 30 sealedRoot *flow.Header 31 // rootSeal is the seal for block `sealedRoot` - the newest incorporated seal with respect to `finalizedRoot`. 32 rootSeal *flow.Seal 33 } 34 35 var _ protocol.InstanceParams = (*InstanceParams)(nil) 36 37 // ReadInstanceParams reads the instance parameters from the database and returns them as in-memory representation. 38 // No errors are expected during normal operation. 39 func ReadInstanceParams(db *badger.DB, headers storage.Headers, seals storage.Seals) (*InstanceParams, error) { 40 params := &InstanceParams{ 41 db: db, 42 } 43 44 // in next section we will read data from the database and cache them, 45 // as they are immutable for the runtime of the node. 46 err := db.View(func(txn *badger.Txn) error { 47 var ( 48 finalizedRootHeight uint64 49 sealedRootHeight uint64 50 ) 51 52 // root height 53 err := db.View(operation.RetrieveRootHeight(&finalizedRootHeight)) 54 if err != nil { 55 return fmt.Errorf("could not read root block to populate cache: %w", err) 56 } 57 // sealed root height 58 err = db.View(operation.RetrieveSealedRootHeight(&sealedRootHeight)) 59 if err != nil { 60 return fmt.Errorf("could not read sealed root block to populate cache: %w", err) 61 } 62 63 // look up 'finalized root block' 64 var finalizedRootID flow.Identifier 65 err = db.View(operation.LookupBlockHeight(finalizedRootHeight, &finalizedRootID)) 66 if err != nil { 67 return fmt.Errorf("could not look up finalized root height: %w", err) 68 } 69 params.finalizedRoot, err = headers.ByBlockID(finalizedRootID) 70 if err != nil { 71 return fmt.Errorf("could not retrieve finalized root header: %w", err) 72 } 73 74 // look up the sealed block as of the 'finalized root block' 75 var sealedRootID flow.Identifier 76 err = db.View(operation.LookupBlockHeight(sealedRootHeight, &sealedRootID)) 77 if err != nil { 78 return fmt.Errorf("could not look up sealed root height: %w", err) 79 } 80 params.sealedRoot, err = headers.ByBlockID(sealedRootID) 81 if err != nil { 82 return fmt.Errorf("could not retrieve sealed root header: %w", err) 83 } 84 85 // retrieve the root seal 86 params.rootSeal, err = seals.HighestInFork(finalizedRootID) 87 if err != nil { 88 return fmt.Errorf("could not retrieve root seal: %w", err) 89 } 90 91 return nil 92 }) 93 if err != nil { 94 return nil, fmt.Errorf("could not read InstanceParams data to populate cache: %w", err) 95 } 96 97 return params, nil 98 } 99 100 // EpochFallbackTriggered returns whether epoch fallback mode [EFM] has been triggered. 101 // EFM is a permanent, spork-scoped state which is triggered when the next 102 // epoch fails to be committed in the allocated time. Once EFM is triggered, 103 // it will remain in effect until the next spork. 104 // TODO for 'leaving Epoch Fallback via special service event' 105 // No errors are expected during normal operation. 106 func (p *InstanceParams) EpochFallbackTriggered() (bool, error) { 107 var triggered bool 108 err := p.db.View(operation.CheckEpochEmergencyFallbackTriggered(&triggered)) 109 if err != nil { 110 return false, fmt.Errorf("could not check epoch fallback triggered: %w", err) 111 } 112 return triggered, nil 113 } 114 115 // FinalizedRoot returns the finalized root header of the current protocol state. This will be 116 // the head of the protocol state snapshot used to bootstrap this state and 117 // may differ from node to node for the same protocol state. 118 func (p *InstanceParams) FinalizedRoot() *flow.Header { 119 return p.finalizedRoot 120 } 121 122 // SealedRoot returns the sealed root block. If it's different from FinalizedRoot() block, 123 // it means the node is bootstrapped from mid-spork. 124 func (p *InstanceParams) SealedRoot() *flow.Header { 125 return p.sealedRoot 126 } 127 128 // Seal returns the root block seal of the current protocol state. This is the seal for the 129 // `SealedRoot` block that was used to bootstrap this state. It may differ from node to node. 130 func (p *InstanceParams) Seal() *flow.Seal { 131 return p.rootSeal 132 } 133 134 // ReadGlobalParams reads the global parameters from the database and returns them as in-memory representation. 135 // No errors are expected during normal operation. 136 func ReadGlobalParams(db *badger.DB) (*inmem.Params, error) { 137 var sporkID flow.Identifier 138 err := db.View(operation.RetrieveSporkID(&sporkID)) 139 if err != nil { 140 return nil, fmt.Errorf("could not get spork id: %w", err) 141 } 142 143 var sporkRootBlockHeight uint64 144 err = db.View(operation.RetrieveSporkRootBlockHeight(&sporkRootBlockHeight)) 145 if err != nil { 146 return nil, fmt.Errorf("could not get spork root block height: %w", err) 147 } 148 149 var threshold uint64 150 err = db.View(operation.RetrieveEpochCommitSafetyThreshold(&threshold)) 151 if err != nil { 152 return nil, fmt.Errorf("could not get epoch commit safety threshold") 153 } 154 155 var version uint 156 err = db.View(operation.RetrieveProtocolVersion(&version)) 157 if err != nil { 158 return nil, fmt.Errorf("could not get protocol version: %w", err) 159 } 160 161 root, err := ReadFinalizedRoot(db) // retrieve root header 162 if err != nil { 163 return nil, fmt.Errorf("could not get root: %w", err) 164 } 165 166 return inmem.NewParams( 167 inmem.EncodableParams{ 168 ChainID: root.ChainID, 169 SporkID: sporkID, 170 SporkRootBlockHeight: sporkRootBlockHeight, 171 ProtocolVersion: version, 172 EpochCommitSafetyThreshold: threshold, 173 }, 174 ), nil 175 } 176 177 // ReadFinalizedRoot retrieves the root block's header from the database. 178 // This information is immutable for the runtime of the software and may be cached. 179 func ReadFinalizedRoot(db *badger.DB) (*flow.Header, error) { 180 var finalizedRootHeight uint64 181 var rootID flow.Identifier 182 var rootHeader flow.Header 183 err := db.View(func(tx *badger.Txn) error { 184 err := operation.RetrieveRootHeight(&finalizedRootHeight)(tx) 185 if err != nil { 186 return fmt.Errorf("could not retrieve finalized root height: %w", err) 187 } 188 err = operation.LookupBlockHeight(finalizedRootHeight, &rootID)(tx) // look up root block ID 189 if err != nil { 190 return fmt.Errorf("could not retrieve root header's ID by height: %w", err) 191 } 192 err = operation.RetrieveHeader(rootID, &rootHeader)(tx) // retrieve root header 193 if err != nil { 194 return fmt.Errorf("could not retrieve root header: %w", err) 195 } 196 return nil 197 }) 198 if err != nil { 199 return nil, fmt.Errorf("failed to read root information from database: %w", err) 200 } 201 return &rootHeader, nil 202 }