github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/state/stategen/getter.go (about) 1 package stategen 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 types "github.com/prysmaticlabs/eth2-types" 8 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 9 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 10 "github.com/prysmaticlabs/prysm/shared/bytesutil" 11 "github.com/prysmaticlabs/prysm/shared/params" 12 "go.opencensus.io/trace" 13 ) 14 15 // HasState returns true if the state exists in cache or in DB. 16 func (s *State) HasState(ctx context.Context, blockRoot [32]byte) (bool, error) { 17 has, err := s.HasStateInCache(ctx, blockRoot) 18 if err != nil { 19 return false, err 20 } 21 if has { 22 return true, nil 23 } 24 return s.beaconDB.HasState(ctx, blockRoot), nil 25 } 26 27 // HasStateInCache returns true if the state exists in cache. 28 func (s *State) HasStateInCache(ctx context.Context, blockRoot [32]byte) (bool, error) { 29 if s.hotStateCache.has(blockRoot) { 30 return true, nil 31 } 32 _, has, err := s.epochBoundaryStateCache.getByRoot(blockRoot) 33 if err != nil { 34 return false, err 35 } 36 return has, nil 37 } 38 39 // StateByRoot retrieves the state using input block root. 40 func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) { 41 ctx, span := trace.StartSpan(ctx, "stateGen.StateByRoot") 42 defer span.End() 43 44 // Genesis case. If block root is zero hash, short circuit to use genesis cachedState stored in DB. 45 if blockRoot == params.BeaconConfig().ZeroHash { 46 return s.beaconDB.State(ctx, blockRoot) 47 } 48 return s.loadStateByRoot(ctx, blockRoot) 49 } 50 51 // StateByRootInitialSync retrieves the state from the DB for the initial syncing phase. 52 // It assumes initial syncing using a block list rather than a block tree hence the returned 53 // state is not copied. 54 // It invalidates cache for parent root because pre state will get mutated. 55 // Do not use this method for anything other than initial syncing purpose or block tree is applied. 56 func (s *State) StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) { 57 // Genesis case. If block root is zero hash, short circuit to use genesis state stored in DB. 58 if blockRoot == params.BeaconConfig().ZeroHash { 59 return s.beaconDB.State(ctx, blockRoot) 60 } 61 62 // To invalidate cache for parent root because pre state will get mutated. 63 defer s.hotStateCache.delete(blockRoot) 64 65 if s.hotStateCache.has(blockRoot) { 66 return s.hotStateCache.getWithoutCopy(blockRoot), nil 67 } 68 69 cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot) 70 if err != nil { 71 return nil, err 72 } 73 if ok { 74 return cachedInfo.state, nil 75 } 76 77 startState, err := s.lastAncestorState(ctx, blockRoot) 78 if err != nil { 79 return nil, errors.Wrap(err, "could not get ancestor state") 80 } 81 if startState == nil || startState.IsNil() { 82 return nil, errUnknownState 83 } 84 summary, err := s.stateSummary(ctx, blockRoot) 85 if err != nil { 86 return nil, errors.Wrap(err, "could not get state summary") 87 } 88 if startState.Slot() == summary.Slot { 89 return startState, nil 90 } 91 92 blks, err := s.LoadBlocks(ctx, startState.Slot()+1, summary.Slot, bytesutil.ToBytes32(summary.Root)) 93 if err != nil { 94 return nil, errors.Wrap(err, "could not load blocks") 95 } 96 startState, err = s.ReplayBlocks(ctx, startState, blks, summary.Slot) 97 if err != nil { 98 return nil, errors.Wrap(err, "could not replay blocks") 99 } 100 101 return startState, nil 102 } 103 104 // StateBySlot retrieves the state using input slot. 105 func (s *State) StateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) { 106 ctx, span := trace.StartSpan(ctx, "stateGen.StateBySlot") 107 defer span.End() 108 109 return s.loadStateBySlot(ctx, slot) 110 } 111 112 // This returns the state summary object of a given block root, it first checks the cache 113 // then checks the DB. An error is returned if state summary object is nil. 114 func (s *State) stateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) { 115 var summary *pb.StateSummary 116 var err error 117 118 summary, err = s.beaconDB.StateSummary(ctx, blockRoot) 119 if err != nil { 120 return nil, err 121 } 122 123 if summary == nil { 124 return s.RecoverStateSummary(ctx, blockRoot) 125 } 126 return summary, nil 127 } 128 129 // RecoverStateSummary recovers state summary object of a given block root by using the saved block in DB. 130 func (s *State) RecoverStateSummary(ctx context.Context, blockRoot [32]byte) (*pb.StateSummary, error) { 131 if s.beaconDB.HasBlock(ctx, blockRoot) { 132 b, err := s.beaconDB.Block(ctx, blockRoot) 133 if err != nil { 134 return nil, err 135 } 136 summary := &pb.StateSummary{Slot: b.Block().Slot(), Root: blockRoot[:]} 137 if err := s.beaconDB.SaveStateSummary(ctx, summary); err != nil { 138 return nil, err 139 } 140 return summary, nil 141 } 142 return nil, errors.New("could not find block in DB") 143 } 144 145 // This loads a beacon state from either the cache or DB then replay blocks up the requested block root. 146 func (s *State) loadStateByRoot(ctx context.Context, blockRoot [32]byte) (iface.BeaconState, error) { 147 ctx, span := trace.StartSpan(ctx, "stateGen.loadStateByRoot") 148 defer span.End() 149 150 // First, it checks if the state exists in hot state cache. 151 cachedState := s.hotStateCache.get(blockRoot) 152 if cachedState != nil && !cachedState.IsNil() { 153 return cachedState, nil 154 } 155 156 // Second, it checks if the state exits in epoch boundary state cache. 157 cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(blockRoot) 158 if err != nil { 159 return nil, err 160 } 161 if ok { 162 return cachedInfo.state, nil 163 } 164 165 // Short cut if the cachedState is already in the DB. 166 if s.beaconDB.HasState(ctx, blockRoot) { 167 return s.beaconDB.State(ctx, blockRoot) 168 } 169 170 summary, err := s.stateSummary(ctx, blockRoot) 171 if err != nil { 172 return nil, errors.Wrap(err, "could not get state summary") 173 } 174 targetSlot := summary.Slot 175 176 // Since the requested state is not in caches, start replaying using the last available ancestor state which is 177 // retrieved using input block's parent root. 178 startState, err := s.lastAncestorState(ctx, blockRoot) 179 if err != nil { 180 return nil, errors.Wrap(err, "could not get ancestor state") 181 } 182 if startState == nil || startState.IsNil() { 183 return nil, errUnknownBoundaryState 184 } 185 186 // Return state early if we are retrieving it from our finalized state cache. 187 if startState.Slot() == targetSlot { 188 return startState, nil 189 } 190 191 blks, err := s.LoadBlocks(ctx, startState.Slot()+1, targetSlot, bytesutil.ToBytes32(summary.Root)) 192 if err != nil { 193 return nil, errors.Wrap(err, "could not load blocks for hot state using root") 194 } 195 196 replayBlockCount.Observe(float64(len(blks))) 197 198 return s.ReplayBlocks(ctx, startState, blks, targetSlot) 199 } 200 201 // This loads a state by slot. 202 func (s *State) loadStateBySlot(ctx context.Context, slot types.Slot) (iface.BeaconState, error) { 203 ctx, span := trace.StartSpan(ctx, "stateGen.loadStateBySlot") 204 defer span.End() 205 206 // Return genesis state if slot is 0. 207 if slot == 0 { 208 return s.beaconDB.GenesisState(ctx) 209 } 210 211 // Gather the last saved block root and the slot number. 212 lastValidRoot, lastValidSlot, err := s.lastSavedBlock(ctx, slot) 213 if err != nil { 214 return nil, errors.Wrap(err, "could not get last valid block for hot state using slot") 215 } 216 217 replayStartState, err := s.loadStateByRoot(ctx, lastValidRoot) 218 if err != nil { 219 return nil, err 220 } 221 222 if lastValidSlot < slot { 223 replayStartState, err = processSlotsStateGen(ctx, replayStartState, slot) 224 if err != nil { 225 return nil, err 226 } 227 } 228 return replayStartState, nil 229 } 230 231 // This returns the highest available ancestor state of the input block root. 232 // It recursively look up block's parent until a corresponding state of the block root 233 // is found in the caches or DB. 234 // 235 // There's three ways to derive block parent state: 236 // 1.) block parent state is the last finalized state 237 // 2.) block parent state is the epoch boundary state and exists in epoch boundary cache. 238 // 3.) block parent state is in DB. 239 func (s *State) lastAncestorState(ctx context.Context, root [32]byte) (iface.BeaconState, error) { 240 ctx, span := trace.StartSpan(ctx, "stateGen.lastAncestorState") 241 defer span.End() 242 243 if s.isFinalizedRoot(root) && s.finalizedState() != nil { 244 return s.finalizedState(), nil 245 } 246 247 b, err := s.beaconDB.Block(ctx, root) 248 if err != nil { 249 return nil, err 250 } 251 if b == nil || b.IsNil() { 252 return nil, errUnknownBlock 253 } 254 255 for { 256 if ctx.Err() != nil { 257 return nil, ctx.Err() 258 } 259 // Is the state a genesis state. 260 parentRoot := bytesutil.ToBytes32(b.Block().ParentRoot()) 261 if parentRoot == params.BeaconConfig().ZeroHash { 262 return s.beaconDB.GenesisState(ctx) 263 } 264 265 // Does the state exist in the hot state cache. 266 if s.hotStateCache.has(parentRoot) { 267 return s.hotStateCache.get(parentRoot), nil 268 } 269 270 // Does the state exist in finalized info cache. 271 if s.isFinalizedRoot(parentRoot) { 272 return s.finalizedState(), nil 273 } 274 275 // Does the state exist in epoch boundary cache. 276 cachedInfo, ok, err := s.epochBoundaryStateCache.getByRoot(parentRoot) 277 if err != nil { 278 return nil, err 279 } 280 if ok { 281 return cachedInfo.state, nil 282 } 283 284 // Does the state exists in DB. 285 if s.beaconDB.HasState(ctx, parentRoot) { 286 return s.beaconDB.State(ctx, parentRoot) 287 } 288 b, err = s.beaconDB.Block(ctx, parentRoot) 289 if err != nil { 290 return nil, err 291 } 292 if b == nil || b.IsNil() { 293 return nil, errUnknownBlock 294 } 295 } 296 }