github.com/ava-labs/avalanchego@v1.11.11/vms/components/chain/state_test.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  	"testing"
    11  
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/require"
    14  
    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/consensus/snowman/snowmantest"
    19  	"github.com/ava-labs/avalanchego/snow/snowtest"
    20  	"github.com/ava-labs/avalanchego/utils/hashing"
    21  )
    22  
    23  const (
    24  	Unknown snowtest.Status = -1
    25  
    26  	defaultBlockCacheSize = 256
    27  )
    28  
    29  var (
    30  	errCantBuildBlock       = errors.New("can't build new block")
    31  	errVerify               = errors.New("verify failed")
    32  	errAccept               = errors.New("accept failed")
    33  	errReject               = errors.New("reject failed")
    34  	errUnexpectedBlockBytes = errors.New("unexpected block bytes")
    35  )
    36  
    37  // NewTestBlock returns a new test block with height, bytes, and ID derived from [i]
    38  // and using [parentID] as the parent block ID
    39  func NewTestBlock(i uint64, parentID ids.ID) *snowmantest.Block {
    40  	b := []byte{byte(i)}
    41  	id := hashing.ComputeHash256Array(b)
    42  	return &snowmantest.Block{
    43  		Decidable: snowtest.Decidable{
    44  			IDV:    id,
    45  			Status: Unknown,
    46  		},
    47  		HeightV: i,
    48  		ParentV: parentID,
    49  		BytesV:  b,
    50  	}
    51  }
    52  
    53  // NewTestBlocks generates [numBlocks] consecutive blocks
    54  func NewTestBlocks(numBlocks uint64) []*snowmantest.Block {
    55  	blks := make([]*snowmantest.Block, 0, numBlocks)
    56  	parentID := ids.Empty
    57  	for i := uint64(0); i < numBlocks; i++ {
    58  		blks = append(blks, NewTestBlock(i, parentID))
    59  		parent := blks[len(blks)-1]
    60  		parentID = parent.ID()
    61  	}
    62  
    63  	return blks
    64  }
    65  
    66  func createInternalBlockFuncs(blks []*snowmantest.Block) (
    67  	func(ctx context.Context, blkID ids.ID) (snowman.Block, error),
    68  	func(ctx context.Context, b []byte) (snowman.Block, error),
    69  ) {
    70  	blkMap := make(map[ids.ID]*snowmantest.Block)
    71  	blkBytesMap := make(map[string]*snowmantest.Block)
    72  	for _, blk := range blks {
    73  		blkMap[blk.ID()] = blk
    74  		blkBytes := blk.Bytes()
    75  		blkBytesMap[string(blkBytes)] = blk
    76  	}
    77  
    78  	getBlock := func(_ context.Context, id ids.ID) (snowman.Block, error) {
    79  		blk, ok := blkMap[id]
    80  		if !ok || blk.Status == Unknown {
    81  			return nil, database.ErrNotFound
    82  		}
    83  
    84  		return blk, nil
    85  	}
    86  
    87  	parseBlk := func(_ context.Context, b []byte) (snowman.Block, error) {
    88  		blk, ok := blkBytesMap[string(b)]
    89  		if !ok {
    90  			return nil, fmt.Errorf("%w: %x", errUnexpectedBlockBytes, b)
    91  		}
    92  		if blk.Status == Unknown {
    93  			blk.Status = snowtest.Undecided
    94  		}
    95  		blkMap[blk.ID()] = blk
    96  
    97  		return blk, nil
    98  	}
    99  
   100  	return getBlock, parseBlk
   101  }
   102  
   103  func cantBuildBlock(context.Context) (snowman.Block, error) {
   104  	return nil, errCantBuildBlock
   105  }
   106  
   107  // checkProcessingBlock checks that [blk] is of the correct type and is
   108  // correctly uniquified when calling GetBlock and ParseBlock.
   109  func checkProcessingBlock(t *testing.T, s *State, blk snowman.Block) {
   110  	require := require.New(t)
   111  
   112  	require.IsType(&BlockWrapper{}, blk)
   113  
   114  	parsedBlk, err := s.ParseBlock(context.Background(), blk.Bytes())
   115  	require.NoError(err)
   116  	require.Equal(blk.ID(), parsedBlk.ID())
   117  	require.Equal(blk.Bytes(), parsedBlk.Bytes())
   118  	require.Equal(blk, parsedBlk)
   119  
   120  	getBlk, err := s.GetBlock(context.Background(), blk.ID())
   121  	require.NoError(err)
   122  	require.Equal(parsedBlk, getBlk)
   123  }
   124  
   125  // checkDecidedBlock asserts that [blk] is returned with the correct status by ParseBlock
   126  // and GetBlock.
   127  // expectedStatus should be either Accepted or Rejected.
   128  func checkDecidedBlock(t *testing.T, s *State, blk snowman.Block, cached bool) {
   129  	require := require.New(t)
   130  
   131  	require.IsType(&BlockWrapper{}, blk)
   132  
   133  	if cached {
   134  		_, ok := s.decidedBlocks.Get(blk.ID())
   135  		require.True(ok)
   136  	}
   137  
   138  	parsedBlk, err := s.ParseBlock(context.Background(), blk.Bytes())
   139  	require.NoError(err)
   140  	require.Equal(blk.ID(), parsedBlk.ID())
   141  	require.Equal(blk.Bytes(), parsedBlk.Bytes())
   142  
   143  	_, ok := s.decidedBlocks.Get(blk.ID())
   144  	require.True(ok)
   145  
   146  	// If the block should be in the cache, assert that the returned block is identical to [blk]
   147  	if cached {
   148  		require.Equal(blk, parsedBlk)
   149  	}
   150  
   151  	getBlk, err := s.GetBlock(context.Background(), blk.ID())
   152  	require.NoError(err)
   153  	require.Equal(blk.ID(), getBlk.ID())
   154  	require.Equal(blk.Bytes(), getBlk.Bytes())
   155  
   156  	// Since ParseBlock should have triggered a cache hit, assert that the block is identical
   157  	// to the parsed block.
   158  	require.Equal(parsedBlk, getBlk)
   159  }
   160  
   161  func TestState(t *testing.T) {
   162  	require := require.New(t)
   163  
   164  	testBlks := NewTestBlocks(3)
   165  	genesisBlock := testBlks[0]
   166  	genesisBlock.Status = snowtest.Accepted
   167  	blk1 := testBlks[1]
   168  	blk2 := testBlks[2]
   169  	// Need to create a block with a different bytes and hash here
   170  	// to generate a conflict with blk2
   171  	blk3 := snowmantest.BuildChild(blk1)
   172  	testBlks = append(testBlks, blk3)
   173  
   174  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   175  	chainState := NewState(&Config{
   176  		DecidedCacheSize:    defaultBlockCacheSize,
   177  		MissingCacheSize:    defaultBlockCacheSize,
   178  		UnverifiedCacheSize: defaultBlockCacheSize,
   179  		BytesToIDCacheSize:  defaultBlockCacheSize,
   180  		LastAcceptedBlock:   genesisBlock,
   181  		GetBlock:            getBlock,
   182  		UnmarshalBlock:      parseBlock,
   183  		BuildBlock:          cantBuildBlock,
   184  	})
   185  
   186  	lastAccepted, err := chainState.LastAccepted(context.Background())
   187  	require.NoError(err)
   188  	require.Equal(genesisBlock.ID(), lastAccepted)
   189  
   190  	wrappedGenesisBlk, err := chainState.GetBlock(context.Background(), genesisBlock.ID())
   191  	require.NoError(err)
   192  
   193  	// Check that a cache miss on a block is handled correctly
   194  	_, err = chainState.GetBlock(context.Background(), blk1.ID())
   195  	require.ErrorIs(err, database.ErrNotFound)
   196  
   197  	// Parse and verify blk1 and blk2
   198  	parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes())
   199  	require.NoError(err)
   200  	require.NoError(parsedBlk1.Verify(context.Background()))
   201  
   202  	parsedBlk2, err := chainState.ParseBlock(context.Background(), blk2.Bytes())
   203  	require.NoError(err)
   204  	require.NoError(parsedBlk2.Verify(context.Background()))
   205  
   206  	// Check that the verified blocks have been placed in the processing map
   207  	require.Len(chainState.verifiedBlocks, 2)
   208  
   209  	parsedBlk3, err := chainState.ParseBlock(context.Background(), blk3.Bytes())
   210  	require.NoError(err)
   211  	getBlk3, err := chainState.GetBlock(context.Background(), blk3.ID())
   212  	require.NoError(err)
   213  	require.Equal(parsedBlk3.ID(), getBlk3.ID(), "State GetBlock returned the wrong block")
   214  
   215  	// Check that parsing blk3 does not add it to processing blocks since it has
   216  	// not been verified.
   217  	require.Len(chainState.verifiedBlocks, 2)
   218  
   219  	require.NoError(parsedBlk3.Verify(context.Background()))
   220  	// Check that blk3 has been added to processing blocks.
   221  	require.Len(chainState.verifiedBlocks, 3)
   222  
   223  	// Decide the blocks and ensure they are removed from the processing blocks map
   224  	require.NoError(parsedBlk1.Accept(context.Background()))
   225  	require.NoError(parsedBlk2.Accept(context.Background()))
   226  	require.NoError(parsedBlk3.Reject(context.Background()))
   227  
   228  	require.Empty(chainState.verifiedBlocks)
   229  
   230  	// Check that the last accepted block was updated correctly
   231  	lastAcceptedID, err := chainState.LastAccepted(context.Background())
   232  	require.NoError(err)
   233  	require.Equal(blk2.ID(), lastAcceptedID)
   234  	require.Equal(blk2.ID(), chainState.LastAcceptedBlock().ID())
   235  
   236  	// Flush the caches to ensure decided blocks are handled correctly on cache misses.
   237  	chainState.Flush()
   238  	checkDecidedBlock(t, chainState, wrappedGenesisBlk, false)
   239  	checkDecidedBlock(t, chainState, parsedBlk1, false)
   240  	checkDecidedBlock(t, chainState, parsedBlk2, false)
   241  	checkDecidedBlock(t, chainState, parsedBlk3, false)
   242  }
   243  
   244  func TestBuildBlock(t *testing.T) {
   245  	require := require.New(t)
   246  
   247  	testBlks := NewTestBlocks(2)
   248  	genesisBlock := testBlks[0]
   249  	genesisBlock.Status = snowtest.Accepted
   250  	blk1 := testBlks[1]
   251  
   252  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   253  	buildBlock := func(context.Context) (snowman.Block, error) {
   254  		// Once the block is built, mark it as processing
   255  		blk1.Status = snowtest.Undecided
   256  		return blk1, nil
   257  	}
   258  
   259  	chainState := NewState(&Config{
   260  		DecidedCacheSize:    defaultBlockCacheSize,
   261  		MissingCacheSize:    defaultBlockCacheSize,
   262  		UnverifiedCacheSize: defaultBlockCacheSize,
   263  		BytesToIDCacheSize:  defaultBlockCacheSize,
   264  		LastAcceptedBlock:   genesisBlock,
   265  		GetBlock:            getBlock,
   266  		UnmarshalBlock:      parseBlock,
   267  		BuildBlock:          buildBlock,
   268  	})
   269  
   270  	builtBlk, err := chainState.BuildBlock(context.Background())
   271  	require.NoError(err)
   272  	require.Empty(chainState.verifiedBlocks)
   273  
   274  	require.NoError(builtBlk.Verify(context.Background()))
   275  	require.Len(chainState.verifiedBlocks, 1)
   276  
   277  	checkProcessingBlock(t, chainState, builtBlk)
   278  
   279  	require.NoError(builtBlk.Accept(context.Background()))
   280  
   281  	checkDecidedBlock(t, chainState, builtBlk, true)
   282  }
   283  
   284  func TestStateDecideBlock(t *testing.T) {
   285  	require := require.New(t)
   286  
   287  	testBlks := NewTestBlocks(4)
   288  	genesisBlock := testBlks[0]
   289  	genesisBlock.Status = snowtest.Accepted
   290  	badAcceptBlk := testBlks[1]
   291  	badAcceptBlk.AcceptV = errAccept
   292  	badVerifyBlk := testBlks[2]
   293  	badVerifyBlk.VerifyV = errVerify
   294  	badRejectBlk := testBlks[3]
   295  	badRejectBlk.RejectV = errReject
   296  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   297  	chainState := NewState(&Config{
   298  		DecidedCacheSize:    defaultBlockCacheSize,
   299  		MissingCacheSize:    defaultBlockCacheSize,
   300  		UnverifiedCacheSize: defaultBlockCacheSize,
   301  		BytesToIDCacheSize:  defaultBlockCacheSize,
   302  		LastAcceptedBlock:   genesisBlock,
   303  		GetBlock:            getBlock,
   304  		UnmarshalBlock:      parseBlock,
   305  		BuildBlock:          cantBuildBlock,
   306  	})
   307  
   308  	// Parse badVerifyBlk (which should fail verification)
   309  	badBlk, err := chainState.ParseBlock(context.Background(), badVerifyBlk.Bytes())
   310  	require.NoError(err)
   311  	err = badBlk.Verify(context.Background())
   312  	require.ErrorIs(err, errVerify)
   313  	// Ensure a block that fails verification is not marked as processing
   314  	require.Empty(chainState.verifiedBlocks)
   315  
   316  	// Ensure that an error during block acceptance is propagated correctly
   317  	badBlk, err = chainState.ParseBlock(context.Background(), badAcceptBlk.Bytes())
   318  	require.NoError(err)
   319  	require.NoError(badBlk.Verify(context.Background()))
   320  	require.Len(chainState.verifiedBlocks, 1)
   321  
   322  	err = badBlk.Accept(context.Background())
   323  	require.ErrorIs(err, errAccept)
   324  
   325  	// Ensure that an error during block reject is propagated correctly
   326  	badBlk, err = chainState.ParseBlock(context.Background(), badRejectBlk.Bytes())
   327  	require.NoError(err)
   328  	require.NoError(badBlk.Verify(context.Background()))
   329  	// Note: an error during block Accept/Reject is fatal, so it is undefined whether
   330  	// the block that failed on Accept should be removed from processing or not. We allow
   331  	// either case here to make this test more flexible.
   332  	numProcessing := len(chainState.verifiedBlocks)
   333  	require.Contains([]int{1, 2}, numProcessing)
   334  
   335  	err = badBlk.Reject(context.Background())
   336  	require.ErrorIs(err, errReject)
   337  }
   338  
   339  func TestStateParent(t *testing.T) {
   340  	require := require.New(t)
   341  
   342  	testBlks := NewTestBlocks(3)
   343  	genesisBlock := testBlks[0]
   344  	genesisBlock.Status = snowtest.Accepted
   345  	blk1 := testBlks[1]
   346  	blk2 := testBlks[2]
   347  
   348  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   349  	chainState := NewState(&Config{
   350  		DecidedCacheSize:    defaultBlockCacheSize,
   351  		MissingCacheSize:    defaultBlockCacheSize,
   352  		UnverifiedCacheSize: defaultBlockCacheSize,
   353  		BytesToIDCacheSize:  defaultBlockCacheSize,
   354  		LastAcceptedBlock:   genesisBlock,
   355  		GetBlock:            getBlock,
   356  		UnmarshalBlock:      parseBlock,
   357  		BuildBlock:          cantBuildBlock,
   358  	})
   359  
   360  	parsedBlk2, err := chainState.ParseBlock(context.Background(), blk2.Bytes())
   361  	require.NoError(err)
   362  
   363  	missingBlk1ID := parsedBlk2.Parent()
   364  
   365  	_, err = chainState.GetBlock(context.Background(), missingBlk1ID)
   366  	require.ErrorIs(err, database.ErrNotFound)
   367  
   368  	parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes())
   369  	require.NoError(err)
   370  
   371  	genesisBlkParentID := parsedBlk1.Parent()
   372  	genesisBlkParent, err := chainState.GetBlock(context.Background(), genesisBlkParentID)
   373  	require.NoError(err)
   374  	checkDecidedBlock(t, chainState, genesisBlkParent, true)
   375  
   376  	parentBlk1ID := parsedBlk2.Parent()
   377  	parentBlk1, err := chainState.GetBlock(context.Background(), parentBlk1ID)
   378  	require.NoError(err)
   379  	checkProcessingBlock(t, chainState, parentBlk1)
   380  }
   381  
   382  func TestGetBlockInternal(t *testing.T) {
   383  	require := require.New(t)
   384  	testBlks := NewTestBlocks(1)
   385  	genesisBlock := testBlks[0]
   386  	genesisBlock.Status = snowtest.Accepted
   387  
   388  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   389  	chainState := NewState(&Config{
   390  		DecidedCacheSize:    defaultBlockCacheSize,
   391  		MissingCacheSize:    defaultBlockCacheSize,
   392  		UnverifiedCacheSize: defaultBlockCacheSize,
   393  		BytesToIDCacheSize:  defaultBlockCacheSize,
   394  		LastAcceptedBlock:   genesisBlock,
   395  		GetBlock:            getBlock,
   396  		UnmarshalBlock:      parseBlock,
   397  		BuildBlock:          cantBuildBlock,
   398  	})
   399  
   400  	genesisBlockInternal := chainState.LastAcceptedBlockInternal()
   401  	require.IsType(&snowmantest.Block{}, genesisBlockInternal)
   402  	require.Equal(genesisBlock.ID(), genesisBlockInternal.ID())
   403  
   404  	blk, err := chainState.GetBlockInternal(context.Background(), genesisBlock.ID())
   405  	require.NoError(err)
   406  
   407  	require.IsType(&snowmantest.Block{}, blk)
   408  	require.Equal(genesisBlock.ID(), blk.ID())
   409  }
   410  
   411  func TestGetBlockError(t *testing.T) {
   412  	require := require.New(t)
   413  
   414  	testBlks := NewTestBlocks(2)
   415  	genesisBlock := testBlks[0]
   416  	genesisBlock.Status = snowtest.Accepted
   417  	blk1 := testBlks[1]
   418  
   419  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   420  	wrappedGetBlock := func(ctx context.Context, id ids.ID) (snowman.Block, error) {
   421  		blk, err := getBlock(ctx, id)
   422  		if err != nil {
   423  			return nil, fmt.Errorf("wrapping error to prevent caching miss: %w", err)
   424  		}
   425  		return blk, nil
   426  	}
   427  	chainState := NewState(&Config{
   428  		DecidedCacheSize:    defaultBlockCacheSize,
   429  		MissingCacheSize:    defaultBlockCacheSize,
   430  		UnverifiedCacheSize: defaultBlockCacheSize,
   431  		BytesToIDCacheSize:  defaultBlockCacheSize,
   432  		LastAcceptedBlock:   genesisBlock,
   433  		GetBlock:            wrappedGetBlock,
   434  		UnmarshalBlock:      parseBlock,
   435  		BuildBlock:          cantBuildBlock,
   436  	})
   437  
   438  	_, err := chainState.GetBlock(context.Background(), blk1.ID())
   439  	require.ErrorIs(err, database.ErrNotFound)
   440  
   441  	// Update the status to Undecided, so that it will be returned by the
   442  	// internal get block function.
   443  	blk1.Status = snowtest.Undecided
   444  	blk, err := chainState.GetBlock(context.Background(), blk1.ID())
   445  	require.NoError(err)
   446  	require.Equal(blk1.ID(), blk.ID())
   447  	checkProcessingBlock(t, chainState, blk)
   448  }
   449  
   450  func TestParseBlockError(t *testing.T) {
   451  	testBlks := NewTestBlocks(1)
   452  	genesisBlock := testBlks[0]
   453  	genesisBlock.Status = snowtest.Accepted
   454  
   455  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   456  	chainState := NewState(&Config{
   457  		DecidedCacheSize:    defaultBlockCacheSize,
   458  		MissingCacheSize:    defaultBlockCacheSize,
   459  		UnverifiedCacheSize: defaultBlockCacheSize,
   460  		BytesToIDCacheSize:  defaultBlockCacheSize,
   461  		LastAcceptedBlock:   genesisBlock,
   462  		GetBlock:            getBlock,
   463  		UnmarshalBlock:      parseBlock,
   464  		BuildBlock:          cantBuildBlock,
   465  	})
   466  
   467  	_, err := chainState.ParseBlock(context.Background(), []byte{255})
   468  	require.ErrorIs(t, err, errUnexpectedBlockBytes)
   469  }
   470  
   471  func TestBuildBlockError(t *testing.T) {
   472  	testBlks := NewTestBlocks(1)
   473  	genesisBlock := testBlks[0]
   474  	genesisBlock.Status = snowtest.Accepted
   475  
   476  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   477  	chainState := NewState(&Config{
   478  		DecidedCacheSize:    defaultBlockCacheSize,
   479  		MissingCacheSize:    defaultBlockCacheSize,
   480  		UnverifiedCacheSize: defaultBlockCacheSize,
   481  		BytesToIDCacheSize:  defaultBlockCacheSize,
   482  		LastAcceptedBlock:   genesisBlock,
   483  		GetBlock:            getBlock,
   484  		UnmarshalBlock:      parseBlock,
   485  		BuildBlock:          cantBuildBlock,
   486  	})
   487  
   488  	_, err := chainState.BuildBlock(context.Background())
   489  	require.ErrorIs(t, err, errCantBuildBlock)
   490  }
   491  
   492  func TestMeteredCache(t *testing.T) {
   493  	require := require.New(t)
   494  
   495  	registry := prometheus.NewRegistry()
   496  
   497  	testBlks := NewTestBlocks(1)
   498  	genesisBlock := testBlks[0]
   499  	genesisBlock.Status = snowtest.Accepted
   500  
   501  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   502  	config := &Config{
   503  		DecidedCacheSize:    defaultBlockCacheSize,
   504  		MissingCacheSize:    defaultBlockCacheSize,
   505  		UnverifiedCacheSize: defaultBlockCacheSize,
   506  		BytesToIDCacheSize:  defaultBlockCacheSize,
   507  		LastAcceptedBlock:   genesisBlock,
   508  		GetBlock:            getBlock,
   509  		UnmarshalBlock:      parseBlock,
   510  		BuildBlock:          cantBuildBlock,
   511  	}
   512  	_, err := NewMeteredState(registry, config)
   513  	require.NoError(err)
   514  	_, err = NewMeteredState(registry, config)
   515  	require.Error(err) //nolint:forbidigo // error is not exported https://github.com/prometheus/client_golang/blob/main/prometheus/registry.go#L315
   516  }
   517  
   518  // Test the bytesToIDCache
   519  func TestStateBytesToIDCache(t *testing.T) {
   520  	require := require.New(t)
   521  
   522  	testBlks := NewTestBlocks(3)
   523  	genesisBlock := testBlks[0]
   524  	genesisBlock.Status = snowtest.Accepted
   525  	blk1 := testBlks[1]
   526  	blk2 := testBlks[2]
   527  
   528  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   529  	buildBlock := func(context.Context) (snowman.Block, error) {
   530  		require.FailNow("shouldn't have been called")
   531  		return nil, nil
   532  	}
   533  
   534  	chainState := NewState(&Config{
   535  		DecidedCacheSize:    0,
   536  		MissingCacheSize:    0,
   537  		UnverifiedCacheSize: 0,
   538  		BytesToIDCacheSize:  1 + ids.IDLen, // Size of one block
   539  		LastAcceptedBlock:   genesisBlock,
   540  		GetBlock:            getBlock,
   541  		UnmarshalBlock:      parseBlock,
   542  		BuildBlock:          buildBlock,
   543  	})
   544  
   545  	// Shouldn't have blk1 ID to start with
   546  	_, err := chainState.GetBlock(context.Background(), blk1.ID())
   547  	require.ErrorIs(err, database.ErrNotFound)
   548  	_, ok := chainState.bytesToIDCache.Get(string(blk1.Bytes()))
   549  	require.False(ok)
   550  
   551  	// Parse blk1 from bytes
   552  	_, err = chainState.ParseBlock(context.Background(), blk1.Bytes())
   553  	require.NoError(err)
   554  
   555  	// blk1 should be in cache now
   556  	_, ok = chainState.bytesToIDCache.Get(string(blk1.Bytes()))
   557  	require.True(ok)
   558  
   559  	// Parse another block
   560  	_, err = chainState.ParseBlock(context.Background(), blk2.Bytes())
   561  	require.NoError(err)
   562  
   563  	// Should have bumped blk1 from cache
   564  	_, ok = chainState.bytesToIDCache.Get(string(blk2.Bytes()))
   565  	require.True(ok)
   566  	_, ok = chainState.bytesToIDCache.Get(string(blk1.Bytes()))
   567  	require.False(ok)
   568  }
   569  
   570  // TestSetLastAcceptedBlock ensures chainState's last accepted block
   571  // can be updated by calling [SetLastAcceptedBlock].
   572  func TestSetLastAcceptedBlock(t *testing.T) {
   573  	require := require.New(t)
   574  
   575  	testBlks := NewTestBlocks(1)
   576  	genesisBlock := testBlks[0]
   577  	genesisBlock.Status = snowtest.Accepted
   578  
   579  	postSetBlk1ParentID := hashing.ComputeHash256Array([]byte{byte(199)})
   580  	postSetBlk1 := NewTestBlock(200, postSetBlk1ParentID)
   581  	postSetBlk2 := NewTestBlock(201, postSetBlk1.ID())
   582  
   583  	// note we do not need to parse postSetBlk1 so it is omitted here
   584  	testBlks = append(testBlks, postSetBlk2)
   585  
   586  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   587  	chainState := NewState(&Config{
   588  		DecidedCacheSize:    defaultBlockCacheSize,
   589  		MissingCacheSize:    defaultBlockCacheSize,
   590  		UnverifiedCacheSize: defaultBlockCacheSize,
   591  		BytesToIDCacheSize:  defaultBlockCacheSize,
   592  		LastAcceptedBlock:   genesisBlock,
   593  		GetBlock:            getBlock,
   594  		UnmarshalBlock:      parseBlock,
   595  		BuildBlock:          cantBuildBlock,
   596  	})
   597  	lastAcceptedID, err := chainState.LastAccepted(context.Background())
   598  	require.NoError(err)
   599  	require.Equal(genesisBlock.ID(), lastAcceptedID)
   600  
   601  	// call SetLastAcceptedBlock for postSetBlk1
   602  	require.NoError(chainState.SetLastAcceptedBlock(postSetBlk1))
   603  	lastAcceptedID, err = chainState.LastAccepted(context.Background())
   604  	require.NoError(err)
   605  	require.Equal(postSetBlk1.ID(), lastAcceptedID)
   606  	require.Equal(postSetBlk1.ID(), chainState.LastAcceptedBlock().ID())
   607  
   608  	// ensure further blocks can be accepted
   609  	parsedpostSetBlk2, err := chainState.ParseBlock(context.Background(), postSetBlk2.Bytes())
   610  	require.NoError(err)
   611  	require.NoError(parsedpostSetBlk2.Verify(context.Background()))
   612  	require.NoError(parsedpostSetBlk2.Accept(context.Background()))
   613  	lastAcceptedID, err = chainState.LastAccepted(context.Background())
   614  	require.NoError(err)
   615  	require.Equal(postSetBlk2.ID(), lastAcceptedID)
   616  	require.Equal(postSetBlk2.ID(), chainState.LastAcceptedBlock().ID())
   617  
   618  	checkDecidedBlock(t, chainState, parsedpostSetBlk2, false)
   619  }
   620  
   621  func TestSetLastAcceptedBlockWithProcessingBlocksErrors(t *testing.T) {
   622  	require := require.New(t)
   623  
   624  	testBlks := NewTestBlocks(5)
   625  	genesisBlock := testBlks[0]
   626  	genesisBlock.Status = snowtest.Accepted
   627  	blk1 := testBlks[1]
   628  	resetBlk := testBlks[4]
   629  
   630  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   631  	buildBlock := func(context.Context) (snowman.Block, error) {
   632  		// Once the block is built, mark it as undecided
   633  		genesisBlock.Status = snowtest.Undecided
   634  		return blk1, nil
   635  	}
   636  
   637  	chainState := NewState(&Config{
   638  		DecidedCacheSize:    defaultBlockCacheSize,
   639  		MissingCacheSize:    defaultBlockCacheSize,
   640  		UnverifiedCacheSize: defaultBlockCacheSize,
   641  		BytesToIDCacheSize:  defaultBlockCacheSize,
   642  		LastAcceptedBlock:   genesisBlock,
   643  		GetBlock:            getBlock,
   644  		UnmarshalBlock:      parseBlock,
   645  		BuildBlock:          buildBlock,
   646  	})
   647  
   648  	builtBlk, err := chainState.BuildBlock(context.Background())
   649  	require.NoError(err)
   650  	require.Empty(chainState.verifiedBlocks)
   651  
   652  	require.NoError(builtBlk.Verify(context.Background()))
   653  	require.Len(chainState.verifiedBlocks, 1)
   654  
   655  	checkProcessingBlock(t, chainState, builtBlk)
   656  
   657  	err = chainState.SetLastAcceptedBlock(resetBlk)
   658  	require.ErrorIs(err, errSetAcceptedWithProcessing)
   659  }
   660  
   661  func TestStateParseTransitivelyAcceptedBlock(t *testing.T) {
   662  	require := require.New(t)
   663  
   664  	testBlks := NewTestBlocks(3)
   665  	genesisBlock := testBlks[0]
   666  	genesisBlock.Status = snowtest.Accepted
   667  	blk1 := testBlks[1]
   668  	blk2 := testBlks[2]
   669  	blk2.Status = snowtest.Accepted
   670  
   671  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   672  	chainState := NewState(&Config{
   673  		DecidedCacheSize:    defaultBlockCacheSize,
   674  		MissingCacheSize:    defaultBlockCacheSize,
   675  		UnverifiedCacheSize: defaultBlockCacheSize,
   676  		BytesToIDCacheSize:  defaultBlockCacheSize,
   677  		LastAcceptedBlock:   blk2,
   678  		GetBlock:            getBlock,
   679  		UnmarshalBlock:      parseBlock,
   680  		BuildBlock:          cantBuildBlock,
   681  	})
   682  
   683  	parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes())
   684  	require.NoError(err)
   685  	require.Equal(blk1.Height(), parsedBlk1.Height())
   686  }
   687  
   688  func TestIsProcessing(t *testing.T) {
   689  	require := require.New(t)
   690  
   691  	testBlks := NewTestBlocks(2)
   692  	genesisBlock := testBlks[0]
   693  	genesisBlock.Status = snowtest.Accepted
   694  	blk1 := testBlks[1]
   695  
   696  	getBlock, parseBlock := createInternalBlockFuncs(testBlks)
   697  	chainState := NewState(&Config{
   698  		DecidedCacheSize:    defaultBlockCacheSize,
   699  		MissingCacheSize:    defaultBlockCacheSize,
   700  		UnverifiedCacheSize: defaultBlockCacheSize,
   701  		BytesToIDCacheSize:  defaultBlockCacheSize,
   702  		LastAcceptedBlock:   genesisBlock,
   703  		GetBlock:            getBlock,
   704  		UnmarshalBlock:      parseBlock,
   705  		BuildBlock:          cantBuildBlock,
   706  	})
   707  
   708  	// Parse blk1
   709  	parsedBlk1, err := chainState.ParseBlock(context.Background(), blk1.Bytes())
   710  	require.NoError(err)
   711  
   712  	// Check that it is not processing in consensus
   713  	require.False(chainState.IsProcessing(parsedBlk1.ID()))
   714  
   715  	// Verify blk1
   716  	require.NoError(parsedBlk1.Verify(context.Background()))
   717  
   718  	// Check that it is processing in consensus
   719  	require.True(chainState.IsProcessing(parsedBlk1.ID()))
   720  
   721  	// Accept blk1
   722  	require.NoError(parsedBlk1.Accept(context.Background()))
   723  
   724  	// Check that it is no longer processing in consensus
   725  	require.False(chainState.IsProcessing(parsedBlk1.ID()))
   726  }