github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/execution/storehouse/in_memory_register_store.go (about) 1 package storehouse 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 "github.com/onflow/flow-go/engine/execution" 9 "github.com/onflow/flow-go/model/flow" 10 ) 11 12 var _ execution.InMemoryRegisterStore = (*InMemoryRegisterStore)(nil) 13 14 var ErrNotExecuted = fmt.Errorf("block is not executed") 15 16 type PrunedError struct { 17 PrunedHeight uint64 18 PrunedID flow.Identifier 19 Height uint64 20 } 21 22 func NewPrunedError(height uint64, prunedHeight uint64, prunedID flow.Identifier) error { 23 return PrunedError{Height: height, PrunedHeight: prunedHeight, PrunedID: prunedID} 24 } 25 26 func (e PrunedError) Error() string { 27 return fmt.Sprintf("block is pruned at height %d", e.Height) 28 } 29 30 func IsPrunedError(err error) (PrunedError, bool) { 31 var e PrunedError 32 ok := errors.As(err, &e) 33 if ok { 34 return e, true 35 } 36 return PrunedError{}, false 37 } 38 39 type InMemoryRegisterStore struct { 40 sync.RWMutex 41 registersByBlockID map[flow.Identifier]map[flow.RegisterID]flow.RegisterValue // for storing the registers 42 parentByBlockID map[flow.Identifier]flow.Identifier // for register updates to be fork-aware 43 blockIDsByHeight map[uint64]map[flow.Identifier]struct{} // for pruning 44 prunedHeight uint64 // registers at pruned height are pruned (not saved in registersByBlockID) 45 prunedID flow.Identifier // to ensure all blocks are extending from pruned block (last finalized and executed block) 46 } 47 48 func NewInMemoryRegisterStore(lastHeight uint64, lastID flow.Identifier) *InMemoryRegisterStore { 49 return &InMemoryRegisterStore{ 50 registersByBlockID: make(map[flow.Identifier]map[flow.RegisterID]flow.RegisterValue), 51 parentByBlockID: make(map[flow.Identifier]flow.Identifier), 52 blockIDsByHeight: make(map[uint64]map[flow.Identifier]struct{}), 53 prunedHeight: lastHeight, 54 prunedID: lastID, 55 } 56 } 57 58 // SaveRegisters saves the registers of a block to InMemoryRegisterStore 59 // It needs to ensure the block is above the pruned height and is connected to the pruned block 60 func (s *InMemoryRegisterStore) SaveRegisters( 61 height uint64, 62 blockID flow.Identifier, 63 parentID flow.Identifier, 64 registers flow.RegisterEntries, 65 ) error { 66 // preprocess data before acquiring the lock 67 regs := make(map[flow.RegisterID]flow.RegisterValue, len(registers)) 68 for _, reg := range registers { 69 regs[reg.Key] = reg.Value 70 } 71 72 s.Lock() 73 defer s.Unlock() 74 75 // ensure all saved registers are above the pruned height 76 if height <= s.prunedHeight { 77 return fmt.Errorf("saving pruned registers height %v <= pruned height %v", height, s.prunedHeight) 78 } 79 80 // ensure the block is not already saved 81 _, ok := s.registersByBlockID[blockID] 82 if ok { 83 // already exist 84 return fmt.Errorf("saving registers for block %s, but it already exists", blockID) 85 } 86 87 // make sure parent is a known block or the pruned block, which forms a fork 88 _, ok = s.registersByBlockID[parentID] 89 if !ok && parentID != s.prunedID { 90 return fmt.Errorf("saving registers for block %s, but its parent %s is not saved", blockID, parentID) 91 } 92 93 // update registers for the block 94 s.registersByBlockID[blockID] = regs 95 96 // update index on parent 97 s.parentByBlockID[blockID] = parentID 98 99 // update index on height 100 sameHeight, ok := s.blockIDsByHeight[height] 101 if !ok { 102 sameHeight = make(map[flow.Identifier]struct{}) 103 s.blockIDsByHeight[height] = sameHeight 104 } 105 106 sameHeight[blockID] = struct{}{} 107 return nil 108 } 109 110 // GetRegister will return the latest updated value of the given register 111 // since the pruned height. 112 // It returns PrunedError if the register is unknown or not updated since the pruned height 113 // Can't return ErrNotFound, since we can't distinguish between not found or not updated since the pruned height 114 func (s *InMemoryRegisterStore) GetRegister(height uint64, blockID flow.Identifier, register flow.RegisterID) (flow.RegisterValue, error) { 115 s.RLock() 116 defer s.RUnlock() 117 118 if height <= s.prunedHeight { 119 return flow.RegisterValue{}, NewPrunedError(height, s.prunedHeight, s.prunedID) 120 } 121 122 _, ok := s.registersByBlockID[blockID] 123 if !ok { 124 return flow.RegisterValue{}, fmt.Errorf("cannot get register at height %d, block %v is not saved: %w", height, blockID, ErrNotExecuted) 125 } 126 127 // traverse the fork to find the latest updated value of the given register 128 // if not found, it means the register is not updated from the pruned block to the given block 129 block := blockID 130 for { 131 // TODO: do not hold the read lock when reading register from the updated register map 132 reg, ok := s.readRegisterAtBlockID(block, register) 133 if ok { 134 return reg, nil 135 } 136 137 // the register didn't get updated at this block, so check its parent 138 139 parent, ok := s.parentByBlockID[block] 140 if !ok { 141 // if the parent doesn't exist because the block itself is the pruned block, 142 // then it means the register is not updated since the pruned height. 143 // since we can't distinguish whether the register is not updated or not exist at all, 144 // we just return PrunedError error along with the prunedHeight, so the 145 // caller could check with OnDiskRegisterStore to find if this register has a updated value 146 // at earlier height. 147 if block == s.prunedID { 148 return flow.RegisterValue{}, NewPrunedError(height, s.prunedHeight, s.prunedID) 149 } 150 151 // in this case, it means the state of in-memory register store is inconsistent, 152 // because all saved block must have their parent saved in `parentByBlockID`, and traversing 153 // its parent should eventually reach the pruned block, otherwise it's a bug. 154 155 return flow.RegisterValue{}, 156 fmt.Errorf("inconsistent parent block index in in-memory-register-store, ancient block %v is not found when getting register at block %v", 157 block, blockID) 158 } 159 160 block = parent 161 } 162 } 163 164 func (s *InMemoryRegisterStore) readRegisterAtBlockID(blockID flow.Identifier, register flow.RegisterID) (flow.RegisterValue, bool) { 165 registers, ok := s.registersByBlockID[blockID] 166 if !ok { 167 return flow.RegisterValue{}, false 168 } 169 170 value, ok := registers[register] 171 return value, ok 172 } 173 174 // GetUpdatedRegisters returns the updated registers of a block 175 func (s *InMemoryRegisterStore) GetUpdatedRegisters(height uint64, blockID flow.Identifier) (flow.RegisterEntries, error) { 176 registerUpdates, err := s.getUpdatedRegisters(height, blockID) 177 if err != nil { 178 return nil, err 179 } 180 181 // since the registerUpdates won't be updated and registers for a block can only be set once, 182 // we don't need to hold the lock when converting it from map into slice. 183 registers := make(flow.RegisterEntries, 0, len(registerUpdates)) 184 for regID, reg := range registerUpdates { 185 registers = append(registers, flow.RegisterEntry{ 186 Key: regID, 187 Value: reg, 188 }) 189 } 190 191 return registers, nil 192 } 193 194 func (s *InMemoryRegisterStore) getUpdatedRegisters(height uint64, blockID flow.Identifier) (map[flow.RegisterID]flow.RegisterValue, error) { 195 s.RLock() 196 defer s.RUnlock() 197 if height <= s.prunedHeight { 198 return nil, fmt.Errorf("cannot get register at height %d, it is pruned %v", height, s.prunedHeight) 199 } 200 201 registerUpdates, ok := s.registersByBlockID[blockID] 202 if !ok { 203 return nil, fmt.Errorf("cannot get register at height %d, block %s is not found: %w", height, blockID, ErrNotExecuted) 204 } 205 return registerUpdates, nil 206 } 207 208 // Prune prunes the register store to the given height 209 // The pruned height must be an executed block, the caller should ensure that by calling SaveRegisters before. 210 // 211 // Pruning is done by walking up the finalized fork from `s.prunedHeight` to `height`. At each height, prune all 212 // other forks that begin at that height. This ensures that data for all conflicting forks are freed 213 // 214 // TODO: It does not block the caller, the pruning work is done async 215 func (s *InMemoryRegisterStore) Prune(height uint64, blockID flow.Identifier) error { 216 finalizedFork, err := s.findFinalizedFork(height, blockID) 217 if err != nil { 218 return fmt.Errorf("cannot find finalized fork: %w", err) 219 } 220 221 s.Lock() 222 defer s.Unlock() 223 224 // prune each height starting at the lowest height in the fork. this will remove all blocks 225 // below the new pruned height along with any conflicting forks. 226 for i := len(finalizedFork) - 1; i >= 0; i-- { 227 blockID := finalizedFork[i] 228 229 err := s.pruneByHeight(s.prunedHeight+1, blockID) 230 if err != nil { 231 return fmt.Errorf("could not prune by height %v: %w", s.prunedHeight+1, err) 232 } 233 } 234 235 return nil 236 } 237 238 func (s *InMemoryRegisterStore) PrunedHeight() uint64 { 239 s.RLock() 240 defer s.RUnlock() 241 return s.prunedHeight 242 } 243 244 func (s *InMemoryRegisterStore) IsBlockExecuted(height uint64, blockID flow.Identifier) (bool, error) { 245 s.RLock() 246 defer s.RUnlock() 247 248 // finalized and executed blocks are pruned 249 // so if the height is below pruned height, in memory register store is not sure if 250 // it's executed or not 251 if height < s.prunedHeight { 252 return false, fmt.Errorf("below pruned height") 253 } 254 255 // if the height is the pruned height, then it's executed only if the blockID is the prunedID 256 // since the pruned block must be finalized and executed. 257 if height == s.prunedHeight { 258 return blockID == s.prunedID, nil 259 } 260 261 _, ok := s.registersByBlockID[blockID] 262 return ok, nil 263 } 264 265 // findFinalizedFork returns the finalized fork from higher height to lower height 266 // the last block's height is s.prunedHeight + 1 267 func (s *InMemoryRegisterStore) findFinalizedFork(height uint64, blockID flow.Identifier) ([]flow.Identifier, error) { 268 s.RLock() 269 defer s.RUnlock() 270 271 if height < s.prunedHeight { 272 return nil, fmt.Errorf("cannot find finalized fork at height %d, it is pruned (prunedHeight: %v)", height, s.prunedHeight) 273 } 274 275 if height == s.prunedHeight { 276 if blockID != s.prunedID { 277 return nil, fmt.Errorf("cannot find finalized fork at height %d, it is pruned (prunedHeight: %v, prunedID: %v)", height, s.prunedHeight, s.prunedID) 278 } 279 280 return nil, nil 281 } 282 283 prunedHeight := height 284 block := blockID 285 286 // walk backwards from the provided finalized block to the last pruned block 287 // the result must be a chain from height/blockID to s.prunedHeight/s.prunedID 288 fork := make([]flow.Identifier, 0, height-s.prunedHeight) 289 for { 290 fork = append(fork, block) 291 prunedHeight-- 292 293 parent, ok := s.parentByBlockID[block] 294 if !ok { 295 return nil, fmt.Errorf("inconsistent parent block index in in-memory-register-store, ancient block %s is not found when finding finalized fork at height %v", block, height) 296 } 297 if parent == s.prunedID { 298 break 299 } 300 block = parent 301 } 302 303 if prunedHeight != s.prunedHeight { 304 return nil, fmt.Errorf("inconsistent parent block index in in-memory-register-store, pruned height %d is not equal to %d", prunedHeight, s.prunedHeight) 305 } 306 307 return fork, nil 308 } 309 310 func (s *InMemoryRegisterStore) pruneByHeight(height uint64, finalized flow.Identifier) error { 311 s.removeBlock(height, finalized) 312 313 // remove conflicting forks 314 for blockID := range s.blockIDsByHeight[height] { 315 s.pruneFork(height, blockID) 316 } 317 318 if len(s.blockIDsByHeight[height]) > 0 { 319 return fmt.Errorf("all forks on the same height should have been pruend, but actually not: %v", len(s.blockIDsByHeight[height])) 320 } 321 322 delete(s.blockIDsByHeight, height) 323 s.prunedHeight = height 324 s.prunedID = finalized 325 return nil 326 } 327 328 func (s *InMemoryRegisterStore) removeBlock(height uint64, blockID flow.Identifier) { 329 delete(s.registersByBlockID, blockID) 330 delete(s.parentByBlockID, blockID) 331 delete(s.blockIDsByHeight[height], blockID) 332 } 333 334 // pruneFork prunes the provided block and all of its children 335 func (s *InMemoryRegisterStore) pruneFork(height uint64, blockID flow.Identifier) { 336 s.removeBlock(height, blockID) 337 // all its children must be at height + 1, whose parent is blockID 338 339 nextHeight := height + 1 340 blocksAtNextHeight, ok := s.blockIDsByHeight[nextHeight] 341 if !ok { 342 return 343 } 344 345 for block := range blocksAtNextHeight { 346 isChild := s.parentByBlockID[block] == blockID 347 if isChild { 348 s.pruneFork(nextHeight, block) 349 } 350 } 351 }