github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/state/stategen/replay.go (about) 1 package stategen 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/pkg/errors" 8 types "github.com/prysmaticlabs/eth2-types" 9 transition "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 10 "github.com/prysmaticlabs/prysm/beacon-chain/db/filters" 11 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 12 "github.com/prysmaticlabs/prysm/proto/interfaces" 13 "github.com/prysmaticlabs/prysm/shared/bytesutil" 14 "go.opencensus.io/trace" 15 ) 16 17 // ReplayBlocks replays the input blocks on the input state until the target slot is reached. 18 func (s *State) ReplayBlocks( 19 ctx context.Context, 20 state iface.BeaconState, 21 signed []interfaces.SignedBeaconBlock, 22 targetSlot types.Slot, 23 ) (iface.BeaconState, error) { 24 ctx, span := trace.StartSpan(ctx, "stateGen.ReplayBlocks") 25 defer span.End() 26 27 var err error 28 log.Debugf("Replaying state from slot %d till slot %d", state.Slot(), targetSlot) 29 // The input block list is sorted in decreasing slots order. 30 if len(signed) > 0 { 31 for i := len(signed) - 1; i >= 0; i-- { 32 if ctx.Err() != nil { 33 return nil, ctx.Err() 34 } 35 if state.Slot() >= targetSlot { 36 break 37 } 38 // A node shouldn't process the block if the block slot is lower than the state slot. 39 if state.Slot() >= signed[i].Block().Slot() { 40 continue 41 } 42 state, err = executeStateTransitionStateGen(ctx, state, signed[i]) 43 if err != nil { 44 return nil, err 45 } 46 } 47 } 48 49 // If there is skip slots at the end. 50 if targetSlot > state.Slot() { 51 state, err = processSlotsStateGen(ctx, state, targetSlot) 52 if err != nil { 53 return nil, err 54 } 55 } 56 57 return state, nil 58 } 59 60 // LoadBlocks loads the blocks between start slot and end slot by recursively fetching from end block root. 61 // The Blocks are returned in slot-descending order. 62 func (s *State) LoadBlocks(ctx context.Context, startSlot, endSlot types.Slot, endBlockRoot [32]byte) ([]interfaces.SignedBeaconBlock, error) { 63 // Nothing to load for invalid range. 64 if endSlot < startSlot { 65 return nil, fmt.Errorf("start slot %d >= end slot %d", startSlot, endSlot) 66 } 67 filter := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot) 68 blocks, blockRoots, err := s.beaconDB.Blocks(ctx, filter) 69 if err != nil { 70 return nil, err 71 } 72 // The retrieved blocks and block roots have to be in the same length given same filter. 73 if len(blocks) != len(blockRoots) { 74 return nil, errors.New("length of blocks and roots don't match") 75 } 76 // Return early if there's no block given the input. 77 length := len(blocks) 78 if length == 0 { 79 return nil, nil 80 } 81 82 // The last retrieved block root has to match input end block root. 83 // Covers the edge case if there's multiple blocks on the same end slot, 84 // the end root may not be the last index in `blockRoots`. 85 for length >= 3 && blocks[length-1].Block().Slot() == blocks[length-2].Block().Slot() && blockRoots[length-1] != endBlockRoot { 86 if ctx.Err() != nil { 87 return nil, ctx.Err() 88 } 89 length-- 90 if blockRoots[length-2] == endBlockRoot { 91 length-- 92 break 93 } 94 } 95 96 if blockRoots[length-1] != endBlockRoot { 97 return nil, errors.New("end block roots don't match") 98 } 99 100 filteredBlocks := []interfaces.SignedBeaconBlock{blocks[length-1]} 101 // Starting from second to last index because the last block is already in the filtered block list. 102 for i := length - 2; i >= 0; i-- { 103 if ctx.Err() != nil { 104 return nil, ctx.Err() 105 } 106 b := filteredBlocks[len(filteredBlocks)-1] 107 if bytesutil.ToBytes32(b.Block().ParentRoot()) != blockRoots[i] { 108 continue 109 } 110 filteredBlocks = append(filteredBlocks, blocks[i]) 111 } 112 113 return filteredBlocks, nil 114 } 115 116 // executeStateTransitionStateGen applies state transition on input historical state and block for state gen usages. 117 // There's no signature verification involved given state gen only works with stored block and state in DB. 118 // If the objects are already in stored in DB, one can omit redundant signature checks and ssz hashing calculations. 119 // WARNING: This method should not be used on an unverified new block. 120 func executeStateTransitionStateGen( 121 ctx context.Context, 122 state iface.BeaconState, 123 signed interfaces.SignedBeaconBlock, 124 ) (iface.BeaconState, error) { 125 if ctx.Err() != nil { 126 return nil, ctx.Err() 127 } 128 if signed == nil || signed.IsNil() || signed.Block().IsNil() { 129 return nil, errUnknownBlock 130 } 131 132 ctx, span := trace.StartSpan(ctx, "stategen.ExecuteStateTransitionStateGen") 133 defer span.End() 134 var err error 135 136 // Execute per slots transition. 137 // Given this is for state gen, a node uses the version process slots without skip slots cache. 138 state, err = processSlotsStateGen(ctx, state, signed.Block().Slot()) 139 if err != nil { 140 return nil, errors.Wrap(err, "could not process slot") 141 } 142 143 // Execute per block transition. 144 // Given this is for state gen, a node only cares about the post state without proposer 145 // and randao signature verifications. 146 state, err = transition.ProcessBlockForStateRoot(ctx, state, signed) 147 if err != nil { 148 return nil, errors.Wrap(err, "could not process block") 149 } 150 151 return state, nil 152 } 153 154 // processSlotsStateGen to process old slots for state gen usages. 155 // There's no skip slot cache involved given state gen only works with already stored block and state in DB. 156 // WARNING: This method should not be used for future slot. 157 func processSlotsStateGen(ctx context.Context, state iface.BeaconState, slot types.Slot) (iface.BeaconState, error) { 158 ctx, span := trace.StartSpan(ctx, "stategen.ProcessSlotsStateGen") 159 defer span.End() 160 if state == nil || state.IsNil() { 161 return nil, errUnknownState 162 } 163 164 if state.Slot() > slot { 165 err := fmt.Errorf("expected state.slot %d < slot %d", state.Slot(), slot) 166 return nil, err 167 } 168 169 if state.Slot() == slot { 170 return state, nil 171 } 172 173 var err error 174 for state.Slot() < slot { 175 state, err = transition.ProcessSlot(ctx, state) 176 if err != nil { 177 return nil, errors.Wrap(err, "could not process slot") 178 } 179 if transition.CanProcessEpoch(state) { 180 state, err = transition.ProcessEpochPrecompute(ctx, state) 181 if err != nil { 182 return nil, errors.Wrap(err, "could not process epoch with optimizations") 183 } 184 } 185 if err := state.SetSlot(state.Slot() + 1); err != nil { 186 return nil, err 187 } 188 } 189 190 return state, nil 191 } 192 193 // This finds the last saved block in DB from searching backwards from input slot, 194 // it returns the block root and the slot of the block. 195 // This is used by both hot and cold state management. 196 func (s *State) lastSavedBlock(ctx context.Context, slot types.Slot) ([32]byte, types.Slot, error) { 197 ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedBlock") 198 defer span.End() 199 200 // Handle the genesis case where the input slot is 0. 201 if slot == 0 { 202 gRoot, err := s.genesisRoot(ctx) 203 if err != nil { 204 return [32]byte{}, 0, err 205 } 206 return gRoot, 0, nil 207 } 208 209 lastSaved, err := s.beaconDB.HighestSlotBlocksBelow(ctx, slot+1) 210 if err != nil { 211 return [32]byte{}, 0, err 212 } 213 214 // Given this is used to query canonical block. There should only be one saved canonical block of a given slot. 215 if len(lastSaved) != 1 { 216 return [32]byte{}, 0, fmt.Errorf("highest saved block does not equal to 1, it equals to %d", len(lastSaved)) 217 } 218 if lastSaved[0] == nil || lastSaved[0].IsNil() || lastSaved[0].Block().IsNil() { 219 return [32]byte{}, 0, nil 220 } 221 r, err := lastSaved[0].Block().HashTreeRoot() 222 if err != nil { 223 return [32]byte{}, 0, err 224 } 225 226 return r, lastSaved[0].Block().Slot(), nil 227 } 228 229 // This finds the last saved state in DB from searching backwards from input slot, 230 // it returns the block root of the block which was used to produce the state. 231 // This is used by both hot and cold state management. 232 func (s *State) lastSavedState(ctx context.Context, slot types.Slot) (iface.ReadOnlyBeaconState, error) { 233 ctx, span := trace.StartSpan(ctx, "stateGen.lastSavedState") 234 defer span.End() 235 236 // Handle the genesis case where the input slot is 0. 237 if slot == 0 { 238 return s.beaconDB.GenesisState(ctx) 239 } 240 241 lastSaved, err := s.beaconDB.HighestSlotStatesBelow(ctx, slot+1) 242 if err != nil { 243 return nil, err 244 } 245 246 // Given this is used to query canonical state. There should only be one saved canonical block of a given slot. 247 if len(lastSaved) != 1 { 248 return nil, fmt.Errorf("highest saved state does not equal to 1, it equals to %d", len(lastSaved)) 249 } 250 if lastSaved[0] == nil { 251 return nil, errUnknownState 252 } 253 254 return lastSaved[0], nil 255 } 256 257 // This returns the genesis root. 258 func (s *State) genesisRoot(ctx context.Context) ([32]byte, error) { 259 b, err := s.beaconDB.GenesisBlock(ctx) 260 if err != nil { 261 return [32]byte{}, err 262 } 263 return b.Block().HashTreeRoot() 264 } 265 266 // Given the start slot and the end slot, this returns the finalized beacon blocks in between. 267 // Since hot states don't have finalized blocks, this should ONLY be used for replaying cold state. 268 func (s *State) loadFinalizedBlocks(ctx context.Context, startSlot, endSlot types.Slot) ([]interfaces.SignedBeaconBlock, error) { 269 f := filters.NewFilter().SetStartSlot(startSlot).SetEndSlot(endSlot) 270 bs, bRoots, err := s.beaconDB.Blocks(ctx, f) 271 if err != nil { 272 return nil, err 273 } 274 if len(bs) != len(bRoots) { 275 return nil, errors.New("length of blocks and roots don't match") 276 } 277 fbs := make([]interfaces.SignedBeaconBlock, 0, len(bs)) 278 for i := len(bs) - 1; i >= 0; i-- { 279 if s.beaconDB.IsFinalizedBlock(ctx, bRoots[i]) { 280 fbs = append(fbs, bs[i]) 281 } 282 } 283 return fbs, nil 284 }