github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/state/block_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 state
     5  
     6  import (
     7  	"errors"
     8  
     9  	"github.com/prometheus/client_golang/prometheus"
    10  
    11  	"github.com/ava-labs/avalanchego/cache"
    12  	"github.com/ava-labs/avalanchego/cache/metercacher"
    13  	"github.com/ava-labs/avalanchego/database"
    14  	"github.com/ava-labs/avalanchego/ids"
    15  	"github.com/ava-labs/avalanchego/snow/choices"
    16  	"github.com/ava-labs/avalanchego/utils/constants"
    17  	"github.com/ava-labs/avalanchego/utils/metric"
    18  	"github.com/ava-labs/avalanchego/utils/units"
    19  	"github.com/ava-labs/avalanchego/utils/wrappers"
    20  	"github.com/ava-labs/avalanchego/vms/proposervm/block"
    21  )
    22  
    23  const blockCacheSize = 64 * units.MiB
    24  
    25  var (
    26  	errBlockWrongVersion = errors.New("wrong version")
    27  
    28  	_ BlockState = (*blockState)(nil)
    29  )
    30  
    31  type BlockState interface {
    32  	GetBlock(blkID ids.ID) (block.Block, error)
    33  	PutBlock(blk block.Block) error
    34  	DeleteBlock(blkID ids.ID) error
    35  }
    36  
    37  type blockState struct {
    38  	// Caches BlockID -> Block. If the Block is nil, that means the block is not
    39  	// in storage.
    40  	blkCache cache.Cacher[ids.ID, *blockWrapper]
    41  
    42  	db database.Database
    43  }
    44  
    45  type blockWrapper struct {
    46  	Block  []byte         `serialize:"true"`
    47  	Status choices.Status `serialize:"true"`
    48  
    49  	block block.Block
    50  }
    51  
    52  func cachedBlockSize(_ ids.ID, bw *blockWrapper) int {
    53  	if bw == nil {
    54  		return ids.IDLen + constants.PointerOverhead
    55  	}
    56  	return ids.IDLen + len(bw.Block) + wrappers.IntLen + 2*constants.PointerOverhead
    57  }
    58  
    59  func NewBlockState(db database.Database) BlockState {
    60  	return &blockState{
    61  		blkCache: cache.NewSizedLRU[ids.ID, *blockWrapper](
    62  			blockCacheSize,
    63  			cachedBlockSize,
    64  		),
    65  		db: db,
    66  	}
    67  }
    68  
    69  func NewMeteredBlockState(db database.Database, namespace string, metrics prometheus.Registerer) (BlockState, error) {
    70  	blkCache, err := metercacher.New[ids.ID, *blockWrapper](
    71  		metric.AppendNamespace(namespace, "block_cache"),
    72  		metrics,
    73  		cache.NewSizedLRU[ids.ID, *blockWrapper](
    74  			blockCacheSize,
    75  			cachedBlockSize,
    76  		),
    77  	)
    78  
    79  	return &blockState{
    80  		blkCache: blkCache,
    81  		db:       db,
    82  	}, err
    83  }
    84  
    85  func (s *blockState) GetBlock(blkID ids.ID) (block.Block, error) {
    86  	if blk, found := s.blkCache.Get(blkID); found {
    87  		if blk == nil {
    88  			return nil, database.ErrNotFound
    89  		}
    90  		return blk.block, nil
    91  	}
    92  
    93  	blkWrapperBytes, err := s.db.Get(blkID[:])
    94  	if err == database.ErrNotFound {
    95  		s.blkCache.Put(blkID, nil)
    96  		return nil, database.ErrNotFound
    97  	}
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	blkWrapper := blockWrapper{}
   103  	parsedVersion, err := Codec.Unmarshal(blkWrapperBytes, &blkWrapper)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	if parsedVersion != CodecVersion {
   108  		return nil, errBlockWrongVersion
   109  	}
   110  
   111  	// The key was in the database
   112  	blk, err := block.ParseWithoutVerification(blkWrapper.Block)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	blkWrapper.block = blk
   117  
   118  	s.blkCache.Put(blkID, &blkWrapper)
   119  	return blk, nil
   120  }
   121  
   122  func (s *blockState) PutBlock(blk block.Block) error {
   123  	blkWrapper := blockWrapper{
   124  		Block:  blk.Bytes(),
   125  		Status: choices.Accepted,
   126  		block:  blk,
   127  	}
   128  
   129  	bytes, err := Codec.Marshal(CodecVersion, &blkWrapper)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	blkID := blk.ID()
   135  	s.blkCache.Put(blkID, &blkWrapper)
   136  	return s.db.Put(blkID[:], bytes)
   137  }
   138  
   139  func (s *blockState) DeleteBlock(blkID ids.ID) error {
   140  	s.blkCache.Evict(blkID)
   141  	return s.db.Delete(blkID[:])
   142  }