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