github.com/ava-labs/avalanchego@v1.11.11/vms/components/chain/state.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package chain
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  
    13  	"github.com/ava-labs/avalanchego/cache"
    14  	"github.com/ava-labs/avalanchego/cache/metercacher"
    15  	"github.com/ava-labs/avalanchego/database"
    16  	"github.com/ava-labs/avalanchego/ids"
    17  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    18  	"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
    19  	"github.com/ava-labs/avalanchego/utils/constants"
    20  )
    21  
    22  func cachedBlockSize(_ ids.ID, bw *BlockWrapper) int {
    23  	return ids.IDLen + len(bw.Bytes()) + 2*constants.PointerOverhead
    24  }
    25  
    26  func cachedBlockBytesSize(blockBytes string, _ ids.ID) int {
    27  	return len(blockBytes) + ids.IDLen
    28  }
    29  
    30  // State implements an efficient caching layer used to wrap a VM
    31  // implementation.
    32  type State struct {
    33  	// getBlock retrieves a block from the VM's storage. If getBlock returns
    34  	// a nil error, then the returned block must not have the status Unknown
    35  	getBlock func(context.Context, ids.ID) (snowman.Block, error)
    36  	// unmarshals [b] into a block
    37  	unmarshalBlock        func(context.Context, []byte) (snowman.Block, error)
    38  	batchedUnmarshalBlock func(context.Context, [][]byte) ([]snowman.Block, error)
    39  	// buildBlock attempts to build a block on top of the currently preferred block
    40  	// buildBlock should always return a block with status Processing since it should never
    41  	// create an unknown block, and building on top of the preferred block should never yield
    42  	// a block that has already been decided.
    43  	buildBlock func(context.Context) (snowman.Block, error)
    44  
    45  	// If nil, [BuildBlockWithContext] returns [BuildBlock].
    46  	buildBlockWithContext func(context.Context, *block.Context) (snowman.Block, error)
    47  
    48  	// verifiedBlocks is a map of blocks that have been verified and are
    49  	// therefore currently in consensus.
    50  	verifiedBlocks map[ids.ID]*BlockWrapper
    51  	// decidedBlocks is an LRU cache of decided blocks.
    52  	decidedBlocks cache.Cacher[ids.ID, *BlockWrapper]
    53  	// unverifiedBlocks is an LRU cache of blocks with status processing
    54  	// that have not yet passed verification.
    55  	unverifiedBlocks cache.Cacher[ids.ID, *BlockWrapper]
    56  	// missingBlocks is an LRU cache of missing blocks
    57  	missingBlocks cache.Cacher[ids.ID, struct{}]
    58  	// string([byte repr. of block]) --> the block's ID
    59  	bytesToIDCache    cache.Cacher[string, ids.ID]
    60  	lastAcceptedBlock *BlockWrapper
    61  }
    62  
    63  // Config defines all of the parameters necessary to initialize State
    64  type Config struct {
    65  	// Cache configuration:
    66  	DecidedCacheSize, MissingCacheSize, UnverifiedCacheSize, BytesToIDCacheSize int
    67  
    68  	LastAcceptedBlock     snowman.Block
    69  	GetBlock              func(context.Context, ids.ID) (snowman.Block, error)
    70  	UnmarshalBlock        func(context.Context, []byte) (snowman.Block, error)
    71  	BatchedUnmarshalBlock func(context.Context, [][]byte) ([]snowman.Block, error)
    72  	BuildBlock            func(context.Context) (snowman.Block, error)
    73  	BuildBlockWithContext func(context.Context, *block.Context) (snowman.Block, error)
    74  }
    75  
    76  func (s *State) initialize(config *Config) {
    77  	s.verifiedBlocks = make(map[ids.ID]*BlockWrapper)
    78  	s.getBlock = config.GetBlock
    79  	s.buildBlock = config.BuildBlock
    80  	s.buildBlockWithContext = config.BuildBlockWithContext
    81  	s.unmarshalBlock = config.UnmarshalBlock
    82  	s.batchedUnmarshalBlock = config.BatchedUnmarshalBlock
    83  	s.lastAcceptedBlock = &BlockWrapper{
    84  		Block: config.LastAcceptedBlock,
    85  		state: s,
    86  	}
    87  	s.decidedBlocks.Put(config.LastAcceptedBlock.ID(), s.lastAcceptedBlock)
    88  }
    89  
    90  func NewState(config *Config) *State {
    91  	c := &State{
    92  		verifiedBlocks: make(map[ids.ID]*BlockWrapper),
    93  		decidedBlocks: cache.NewSizedLRU[ids.ID, *BlockWrapper](
    94  			config.DecidedCacheSize,
    95  			cachedBlockSize,
    96  		),
    97  		missingBlocks: &cache.LRU[ids.ID, struct{}]{Size: config.MissingCacheSize},
    98  		unverifiedBlocks: cache.NewSizedLRU[ids.ID, *BlockWrapper](
    99  			config.UnverifiedCacheSize,
   100  			cachedBlockSize,
   101  		),
   102  		bytesToIDCache: cache.NewSizedLRU[string, ids.ID](
   103  			config.BytesToIDCacheSize,
   104  			cachedBlockBytesSize,
   105  		),
   106  	}
   107  	c.initialize(config)
   108  	return c
   109  }
   110  
   111  func NewMeteredState(
   112  	registerer prometheus.Registerer,
   113  	config *Config,
   114  ) (*State, error) {
   115  	decidedCache, err := metercacher.New[ids.ID, *BlockWrapper](
   116  		"decided_cache",
   117  		registerer,
   118  		cache.NewSizedLRU[ids.ID, *BlockWrapper](
   119  			config.DecidedCacheSize,
   120  			cachedBlockSize,
   121  		),
   122  	)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	missingCache, err := metercacher.New[ids.ID, struct{}](
   127  		"missing_cache",
   128  		registerer,
   129  		&cache.LRU[ids.ID, struct{}]{Size: config.MissingCacheSize},
   130  	)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	unverifiedCache, err := metercacher.New[ids.ID, *BlockWrapper](
   135  		"unverified_cache",
   136  		registerer,
   137  		cache.NewSizedLRU[ids.ID, *BlockWrapper](
   138  			config.UnverifiedCacheSize,
   139  			cachedBlockSize,
   140  		),
   141  	)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	bytesToIDCache, err := metercacher.New[string, ids.ID](
   146  		"bytes_to_id_cache",
   147  		registerer,
   148  		cache.NewSizedLRU[string, ids.ID](
   149  			config.BytesToIDCacheSize,
   150  			cachedBlockBytesSize,
   151  		),
   152  	)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	c := &State{
   157  		verifiedBlocks:   make(map[ids.ID]*BlockWrapper),
   158  		decidedBlocks:    decidedCache,
   159  		missingBlocks:    missingCache,
   160  		unverifiedBlocks: unverifiedCache,
   161  		bytesToIDCache:   bytesToIDCache,
   162  	}
   163  	c.initialize(config)
   164  	return c, nil
   165  }
   166  
   167  var errSetAcceptedWithProcessing = errors.New("cannot set last accepted block with blocks processing")
   168  
   169  // SetLastAcceptedBlock sets the last accepted block to [lastAcceptedBlock].
   170  // This should be called with an internal block - not a wrapped block returned
   171  // from state.
   172  //
   173  // This also flushes [lastAcceptedBlock] from missingBlocks and unverifiedBlocks
   174  // to ensure that their contents stay valid.
   175  func (s *State) SetLastAcceptedBlock(lastAcceptedBlock snowman.Block) error {
   176  	if len(s.verifiedBlocks) != 0 {
   177  		return fmt.Errorf("%w: %d", errSetAcceptedWithProcessing, len(s.verifiedBlocks))
   178  	}
   179  
   180  	// [lastAcceptedBlock] is no longer missing or unverified, so we evict it from the corresponding
   181  	// caches.
   182  	//
   183  	// Note: there's no need to evict from the decided blocks cache or bytesToIDCache since their
   184  	// contents will still be valid.
   185  	lastAcceptedBlockID := lastAcceptedBlock.ID()
   186  	s.missingBlocks.Evict(lastAcceptedBlockID)
   187  	s.unverifiedBlocks.Evict(lastAcceptedBlockID)
   188  	s.lastAcceptedBlock = &BlockWrapper{
   189  		Block: lastAcceptedBlock,
   190  		state: s,
   191  	}
   192  	s.decidedBlocks.Put(lastAcceptedBlockID, s.lastAcceptedBlock)
   193  
   194  	return nil
   195  }
   196  
   197  // Flush each block cache
   198  func (s *State) Flush() {
   199  	s.decidedBlocks.Flush()
   200  	s.missingBlocks.Flush()
   201  	s.unverifiedBlocks.Flush()
   202  	s.bytesToIDCache.Flush()
   203  }
   204  
   205  // GetBlock returns the BlockWrapper as snowman.Block corresponding to [blkID]
   206  func (s *State) GetBlock(ctx context.Context, blkID ids.ID) (snowman.Block, error) {
   207  	if blk, ok := s.getCachedBlock(blkID); ok {
   208  		return blk, nil
   209  	}
   210  
   211  	if _, ok := s.missingBlocks.Get(blkID); ok {
   212  		return nil, database.ErrNotFound
   213  	}
   214  
   215  	blk, err := s.getBlock(ctx, blkID)
   216  	// If getBlock returns [database.ErrNotFound], State considers
   217  	// this a cacheable miss.
   218  	if err == database.ErrNotFound {
   219  		s.missingBlocks.Put(blkID, struct{}{})
   220  		return nil, err
   221  	} else if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	// Since this block is not in consensus, addBlockOutsideConsensus
   226  	// is called to add [blk] to the correct cache.
   227  	return s.addBlockOutsideConsensus(blk), nil
   228  }
   229  
   230  // getCachedBlock checks the caches for [blkID] by priority. Returning
   231  // true if [blkID] is found in one of the caches.
   232  func (s *State) getCachedBlock(blkID ids.ID) (snowman.Block, bool) {
   233  	if blk, ok := s.verifiedBlocks[blkID]; ok {
   234  		return blk, true
   235  	}
   236  
   237  	if blk, ok := s.decidedBlocks.Get(blkID); ok {
   238  		return blk, true
   239  	}
   240  
   241  	if blk, ok := s.unverifiedBlocks.Get(blkID); ok {
   242  		return blk, true
   243  	}
   244  
   245  	return nil, false
   246  }
   247  
   248  // GetBlockInternal returns the internal representation of [blkID]
   249  func (s *State) GetBlockInternal(ctx context.Context, blkID ids.ID) (snowman.Block, error) {
   250  	wrappedBlk, err := s.GetBlock(ctx, blkID)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  
   255  	return wrappedBlk.(*BlockWrapper).Block, nil
   256  }
   257  
   258  // ParseBlock attempts to parse [b] into an internal Block and adds it to the
   259  // appropriate caching layer if successful.
   260  func (s *State) ParseBlock(ctx context.Context, b []byte) (snowman.Block, error) {
   261  	// See if we've cached this block's ID by its byte repr.
   262  	cachedBlkID, blkIDCached := s.bytesToIDCache.Get(string(b))
   263  	if blkIDCached {
   264  		// See if we have this block cached
   265  		if cachedBlk, ok := s.getCachedBlock(cachedBlkID); ok {
   266  			return cachedBlk, nil
   267  		}
   268  	}
   269  
   270  	// We don't have this block cached by its byte repr.
   271  	// Parse the block from bytes
   272  	blk, err := s.unmarshalBlock(ctx, b)
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  	blkID := blk.ID()
   277  	s.bytesToIDCache.Put(string(b), blkID)
   278  
   279  	// Only check the caches if we didn't do so above
   280  	if !blkIDCached {
   281  		// Check for an existing block, so we can return a unique block
   282  		// if processing or simply allow this block to be immediately
   283  		// garbage collected if it is already cached.
   284  		if cachedBlk, ok := s.getCachedBlock(blkID); ok {
   285  			return cachedBlk, nil
   286  		}
   287  	}
   288  
   289  	s.missingBlocks.Evict(blkID)
   290  
   291  	// Since this block is not in consensus, addBlockOutsideConsensus
   292  	// is called to add [blk] to the correct cache.
   293  	return s.addBlockOutsideConsensus(blk), nil
   294  }
   295  
   296  // BatchedParseBlock implements part of the block.BatchedChainVM interface. In
   297  // addition to performing all the caching as the ParseBlock function, it
   298  // performs at most one call to the underlying VM if [batchedUnmarshalBlock] was
   299  // provided.
   300  func (s *State) BatchedParseBlock(ctx context.Context, blksBytes [][]byte) ([]snowman.Block, error) {
   301  	blks := make([]snowman.Block, len(blksBytes))
   302  	idWasCached := make([]bool, len(blksBytes))
   303  	unparsedBlksBytes := make([][]byte, 0, len(blksBytes))
   304  	for i, blkBytes := range blksBytes {
   305  		// See if we've cached this block's ID by its byte repr.
   306  		blkID, blkIDCached := s.bytesToIDCache.Get(string(blkBytes))
   307  		idWasCached[i] = blkIDCached
   308  		if !blkIDCached {
   309  			unparsedBlksBytes = append(unparsedBlksBytes, blkBytes)
   310  			continue
   311  		}
   312  
   313  		// See if we have this block cached
   314  		if cachedBlk, ok := s.getCachedBlock(blkID); ok {
   315  			blks[i] = cachedBlk
   316  		} else {
   317  			unparsedBlksBytes = append(unparsedBlksBytes, blkBytes)
   318  		}
   319  	}
   320  
   321  	if len(unparsedBlksBytes) == 0 {
   322  		return blks, nil
   323  	}
   324  
   325  	var (
   326  		parsedBlks []snowman.Block
   327  		err        error
   328  	)
   329  	if s.batchedUnmarshalBlock != nil {
   330  		parsedBlks, err = s.batchedUnmarshalBlock(ctx, unparsedBlksBytes)
   331  		if err != nil {
   332  			return nil, err
   333  		}
   334  	} else {
   335  		parsedBlks = make([]snowman.Block, len(unparsedBlksBytes))
   336  		for i, blkBytes := range unparsedBlksBytes {
   337  			parsedBlks[i], err = s.unmarshalBlock(ctx, blkBytes)
   338  			if err != nil {
   339  				return nil, err
   340  			}
   341  		}
   342  	}
   343  
   344  	i := 0
   345  	for _, blk := range parsedBlks {
   346  		for ; ; i++ {
   347  			if blks[i] == nil {
   348  				break
   349  			}
   350  		}
   351  
   352  		blkID := blk.ID()
   353  		if !idWasCached[i] {
   354  			blkBytes := blk.Bytes()
   355  			blkBytesStr := string(blkBytes)
   356  			s.bytesToIDCache.Put(blkBytesStr, blkID)
   357  
   358  			// Check for an existing block, so we can return a unique block
   359  			// if processing or simply allow this block to be immediately
   360  			// garbage collected if it is already cached.
   361  			if cachedBlk, ok := s.getCachedBlock(blkID); ok {
   362  				blks[i] = cachedBlk
   363  				continue
   364  			}
   365  		}
   366  
   367  		s.missingBlocks.Evict(blkID)
   368  		blks[i] = s.addBlockOutsideConsensus(blk)
   369  	}
   370  	return blks, nil
   371  }
   372  
   373  // BuildBlockWithContext attempts to build a new internal Block, wraps it, and
   374  // adds it to the appropriate caching layer if successful.
   375  // If [s.buildBlockWithContext] is nil, returns [BuildBlock].
   376  func (s *State) BuildBlockWithContext(ctx context.Context, blockCtx *block.Context) (snowman.Block, error) {
   377  	if s.buildBlockWithContext == nil {
   378  		return s.BuildBlock(ctx)
   379  	}
   380  
   381  	blk, err := s.buildBlockWithContext(ctx, blockCtx)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	return s.deduplicate(blk), nil
   387  }
   388  
   389  // BuildBlock attempts to build a new internal Block, wraps it, and adds it
   390  // to the appropriate caching layer if successful.
   391  func (s *State) BuildBlock(ctx context.Context) (snowman.Block, error) {
   392  	blk, err := s.buildBlock(ctx)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  
   397  	return s.deduplicate(blk), nil
   398  }
   399  
   400  func (s *State) deduplicate(blk snowman.Block) snowman.Block {
   401  	blkID := blk.ID()
   402  	// Defensive: buildBlock should not return a block that has already been verified.
   403  	// If it does, make sure to return the existing reference to the block.
   404  	if existingBlk, ok := s.getCachedBlock(blkID); ok {
   405  		return existingBlk
   406  	}
   407  	// Evict the produced block from missing blocks in case it was previously
   408  	// marked as missing.
   409  	s.missingBlocks.Evict(blkID)
   410  
   411  	// wrap the returned block and add it to the correct cache
   412  	return s.addBlockOutsideConsensus(blk)
   413  }
   414  
   415  // addBlockOutsideConsensus adds [blk] to the correct cache and returns
   416  // a wrapped version of [blk]
   417  // assumes [blk] is a known, non-wrapped block that is not currently
   418  // in consensus. [blk] could be either decided or a block that has not yet
   419  // been verified and added to consensus.
   420  func (s *State) addBlockOutsideConsensus(blk snowman.Block) snowman.Block {
   421  	wrappedBlk := &BlockWrapper{
   422  		Block: blk,
   423  		state: s,
   424  	}
   425  
   426  	blkID := blk.ID()
   427  	if blk.Height() <= s.lastAcceptedBlock.Height() {
   428  		s.decidedBlocks.Put(blkID, wrappedBlk)
   429  	} else {
   430  		s.unverifiedBlocks.Put(blkID, wrappedBlk)
   431  	}
   432  
   433  	return wrappedBlk
   434  }
   435  
   436  func (s *State) LastAccepted(context.Context) (ids.ID, error) {
   437  	return s.lastAcceptedBlock.ID(), nil
   438  }
   439  
   440  // LastAcceptedBlock returns the last accepted wrapped block
   441  func (s *State) LastAcceptedBlock() *BlockWrapper {
   442  	return s.lastAcceptedBlock
   443  }
   444  
   445  // LastAcceptedBlockInternal returns the internal snowman.Block that was last accepted
   446  func (s *State) LastAcceptedBlockInternal() snowman.Block {
   447  	return s.LastAcceptedBlock().Block
   448  }
   449  
   450  // IsProcessing returns whether [blkID] is processing in consensus
   451  func (s *State) IsProcessing(blkID ids.ID) bool {
   452  	_, ok := s.verifiedBlocks[blkID]
   453  	return ok
   454  }