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  }