github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/vm_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 proposervm
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"crypto"
    10  	"errors"
    11  	"fmt"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/stretchr/testify/require"
    17  	"go.uber.org/mock/gomock"
    18  
    19  	"github.com/MetalBlockchain/metalgo/database"
    20  	"github.com/MetalBlockchain/metalgo/database/memdb"
    21  	"github.com/MetalBlockchain/metalgo/database/prefixdb"
    22  	"github.com/MetalBlockchain/metalgo/ids"
    23  	"github.com/MetalBlockchain/metalgo/snow"
    24  	"github.com/MetalBlockchain/metalgo/snow/choices"
    25  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    26  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest"
    27  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    28  	"github.com/MetalBlockchain/metalgo/snow/engine/snowman/block"
    29  	"github.com/MetalBlockchain/metalgo/snow/snowtest"
    30  	"github.com/MetalBlockchain/metalgo/snow/validators"
    31  	"github.com/MetalBlockchain/metalgo/staking"
    32  	"github.com/MetalBlockchain/metalgo/utils"
    33  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    34  	"github.com/MetalBlockchain/metalgo/vms/proposervm/proposer"
    35  	"github.com/MetalBlockchain/metalgo/vms/proposervm/state"
    36  
    37  	statelessblock "github.com/MetalBlockchain/metalgo/vms/proposervm/block"
    38  )
    39  
    40  var (
    41  	_ block.ChainVM         = (*fullVM)(nil)
    42  	_ block.StateSyncableVM = (*fullVM)(nil)
    43  )
    44  
    45  type fullVM struct {
    46  	*block.TestVM
    47  	*block.TestStateSyncableVM
    48  }
    49  
    50  var (
    51  	pTestSigner crypto.Signer
    52  	pTestCert   *staking.Certificate
    53  
    54  	defaultPChainHeight uint64 = 2000
    55  
    56  	errUnknownBlock      = errors.New("unknown block")
    57  	errUnverifiedBlock   = errors.New("unverified block")
    58  	errMarshallingFailed = errors.New("marshalling failed")
    59  	errTooHigh           = errors.New("too high")
    60  	errUnexpectedCall    = errors.New("unexpected call")
    61  )
    62  
    63  func init() {
    64  	tlsCert, err := staking.NewTLSCert()
    65  	if err != nil {
    66  		panic(err)
    67  	}
    68  	pTestSigner = tlsCert.PrivateKey.(crypto.Signer)
    69  	pTestCert, err = staking.ParseCertificate(tlsCert.Leaf.Raw)
    70  	if err != nil {
    71  		panic(err)
    72  	}
    73  }
    74  
    75  func initTestProposerVM(
    76  	t *testing.T,
    77  	proBlkStartTime time.Time,
    78  	durangoTime time.Time,
    79  	minPChainHeight uint64,
    80  ) (
    81  	*fullVM,
    82  	*validators.TestState,
    83  	*VM,
    84  	database.Database,
    85  ) {
    86  	require := require.New(t)
    87  
    88  	initialState := []byte("genesis state")
    89  	coreVM := &fullVM{
    90  		TestVM: &block.TestVM{
    91  			TestVM: common.TestVM{
    92  				T: t,
    93  			},
    94  		},
    95  		TestStateSyncableVM: &block.TestStateSyncableVM{
    96  			T: t,
    97  		},
    98  	}
    99  
   100  	coreVM.InitializeF = func(context.Context, *snow.Context, database.Database,
   101  		[]byte, []byte, []byte, chan<- common.Message,
   102  		[]*common.Fx, common.AppSender,
   103  	) error {
   104  		return nil
   105  	}
   106  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   107  		return snowmantest.GenesisID, nil
   108  	}
   109  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   110  		switch blkID {
   111  		case snowmantest.GenesisID:
   112  			return snowmantest.Genesis, nil
   113  		default:
   114  			return nil, errUnknownBlock
   115  		}
   116  	}
   117  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   118  		switch {
   119  		case bytes.Equal(b, snowmantest.GenesisBytes):
   120  			return snowmantest.Genesis, nil
   121  		default:
   122  			return nil, errUnknownBlock
   123  		}
   124  	}
   125  
   126  	proVM := New(
   127  		coreVM,
   128  		Config{
   129  			ActivationTime:      proBlkStartTime,
   130  			DurangoTime:         durangoTime,
   131  			MinimumPChainHeight: minPChainHeight,
   132  			MinBlkDelay:         DefaultMinBlockDelay,
   133  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
   134  			StakingLeafSigner:   pTestSigner,
   135  			StakingCertLeaf:     pTestCert,
   136  			Registerer:          prometheus.NewRegistry(),
   137  		},
   138  	)
   139  
   140  	valState := &validators.TestState{
   141  		T: t,
   142  	}
   143  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   144  		return snowmantest.GenesisHeight, nil
   145  	}
   146  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   147  		return defaultPChainHeight, nil
   148  	}
   149  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   150  		var (
   151  			thisNode = proVM.ctx.NodeID
   152  			nodeID1  = ids.BuildTestNodeID([]byte{1})
   153  			nodeID2  = ids.BuildTestNodeID([]byte{2})
   154  			nodeID3  = ids.BuildTestNodeID([]byte{3})
   155  		)
   156  		return map[ids.NodeID]*validators.GetValidatorOutput{
   157  			thisNode: {
   158  				NodeID: thisNode,
   159  				Weight: 10,
   160  			},
   161  			nodeID1: {
   162  				NodeID: nodeID1,
   163  				Weight: 5,
   164  			},
   165  			nodeID2: {
   166  				NodeID: nodeID2,
   167  				Weight: 6,
   168  			},
   169  			nodeID3: {
   170  				NodeID: nodeID3,
   171  				Weight: 7,
   172  			},
   173  		}, nil
   174  	}
   175  
   176  	ctx := snowtest.Context(t, ids.ID{1})
   177  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
   178  	ctx.ValidatorState = valState
   179  
   180  	db := prefixdb.New([]byte{0}, memdb.New())
   181  
   182  	require.NoError(proVM.Initialize(
   183  		context.Background(),
   184  		ctx,
   185  		db,
   186  		initialState,
   187  		nil,
   188  		nil,
   189  		nil,
   190  		nil,
   191  		nil,
   192  	))
   193  
   194  	// Initialize shouldn't be called again
   195  	coreVM.InitializeF = nil
   196  
   197  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
   198  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
   199  
   200  	proVM.Set(snowmantest.GenesisTimestamp)
   201  
   202  	return coreVM, valState, proVM, db
   203  }
   204  
   205  func waitForProposerWindow(vm *VM, chainTip snowman.Block, pchainHeight uint64) error {
   206  	var (
   207  		ctx              = context.Background()
   208  		childBlockHeight = chainTip.Height() + 1
   209  		parentTimestamp  = chainTip.Timestamp()
   210  	)
   211  
   212  	for {
   213  		slot := proposer.TimeToSlot(parentTimestamp, vm.Clock.Time().Truncate(time.Second))
   214  		delay, err := vm.MinDelayForProposer(
   215  			ctx,
   216  			childBlockHeight,
   217  			pchainHeight,
   218  			vm.ctx.NodeID,
   219  			slot,
   220  		)
   221  		if err != nil {
   222  			return err
   223  		}
   224  
   225  		vm.Clock.Set(parentTimestamp.Add(delay))
   226  		if delay < proposer.MaxLookAheadWindow {
   227  			return nil
   228  		}
   229  	}
   230  }
   231  
   232  // VM.BuildBlock tests section
   233  
   234  func TestBuildBlockTimestampAreRoundedToSeconds(t *testing.T) {
   235  	require := require.New(t)
   236  
   237  	// given the same core block, BuildBlock returns the same proposer block
   238  	var (
   239  		activationTime = time.Unix(0, 0)
   240  		durangoTime    = activationTime
   241  	)
   242  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   243  	defer func() {
   244  		require.NoError(proVM.Shutdown(context.Background()))
   245  	}()
   246  
   247  	skewedTimestamp := time.Now().Truncate(time.Second).Add(time.Millisecond)
   248  	proVM.Set(skewedTimestamp)
   249  
   250  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   251  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   252  		return coreBlk, nil
   253  	}
   254  
   255  	// test
   256  	builtBlk, err := proVM.BuildBlock(context.Background())
   257  	require.NoError(err)
   258  
   259  	require.Equal(builtBlk.Timestamp().Truncate(time.Second), builtBlk.Timestamp())
   260  }
   261  
   262  func TestBuildBlockIsIdempotent(t *testing.T) {
   263  	require := require.New(t)
   264  
   265  	// given the same core block, BuildBlock returns the same proposer block
   266  	var (
   267  		activationTime = time.Unix(0, 0)
   268  		durangoTime    = activationTime
   269  	)
   270  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   271  	defer func() {
   272  		require.NoError(proVM.Shutdown(context.Background()))
   273  	}()
   274  
   275  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   276  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   277  		return coreBlk, nil
   278  	}
   279  
   280  	// Mock the clock time to make sure that block timestamps will be equal
   281  	proVM.Clock.Set(time.Now())
   282  
   283  	builtBlk1, err := proVM.BuildBlock(context.Background())
   284  	require.NoError(err)
   285  
   286  	builtBlk2, err := proVM.BuildBlock(context.Background())
   287  	require.NoError(err)
   288  
   289  	require.Equal(builtBlk1.Bytes(), builtBlk2.Bytes())
   290  }
   291  
   292  func TestFirstProposerBlockIsBuiltOnTopOfGenesis(t *testing.T) {
   293  	require := require.New(t)
   294  
   295  	// setup
   296  	var (
   297  		activationTime = time.Unix(0, 0)
   298  		durangoTime    = activationTime
   299  	)
   300  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   301  	defer func() {
   302  		require.NoError(proVM.Shutdown(context.Background()))
   303  	}()
   304  
   305  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   306  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   307  		return coreBlk, nil
   308  	}
   309  
   310  	// test
   311  	snowBlock, err := proVM.BuildBlock(context.Background())
   312  	require.NoError(err)
   313  
   314  	// checks
   315  	require.IsType(&postForkBlock{}, snowBlock)
   316  	proBlock := snowBlock.(*postForkBlock)
   317  
   318  	require.Equal(coreBlk, proBlock.innerBlk)
   319  }
   320  
   321  // both core blocks and pro blocks must be built on preferred
   322  func TestProposerBlocksAreBuiltOnPreferredProBlock(t *testing.T) {
   323  	require := require.New(t)
   324  
   325  	var (
   326  		activationTime = time.Unix(0, 0)
   327  		durangoTime    = activationTime
   328  	)
   329  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   330  	defer func() {
   331  		require.NoError(proVM.Shutdown(context.Background()))
   332  	}()
   333  
   334  	// add two proBlks...
   335  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   336  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   337  		return coreBlk1, nil
   338  	}
   339  	proBlk1, err := proVM.BuildBlock(context.Background())
   340  	require.NoError(err)
   341  
   342  	coreBlk2 := snowmantest.BuildChild(snowmantest.Genesis)
   343  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   344  		return coreBlk2, nil
   345  	}
   346  	proBlk2, err := proVM.BuildBlock(context.Background())
   347  	require.NoError(err)
   348  	require.NotEqual(proBlk2.ID(), proBlk1.ID())
   349  	require.NoError(proBlk2.Verify(context.Background()))
   350  
   351  	// ...and set one as preferred
   352  	var prefcoreBlk *snowmantest.Block
   353  	coreVM.SetPreferenceF = func(_ context.Context, prefID ids.ID) error {
   354  		switch prefID {
   355  		case coreBlk1.ID():
   356  			prefcoreBlk = coreBlk1
   357  			return nil
   358  		case coreBlk2.ID():
   359  			prefcoreBlk = coreBlk2
   360  			return nil
   361  		default:
   362  			require.FailNow("prefID does not match coreBlk1 or coreBlk2")
   363  			return nil
   364  		}
   365  	}
   366  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   367  		switch {
   368  		case bytes.Equal(b, coreBlk1.Bytes()):
   369  			return coreBlk1, nil
   370  		case bytes.Equal(b, coreBlk2.Bytes()):
   371  			return coreBlk2, nil
   372  		default:
   373  			require.FailNow("bytes do not match coreBlk1 or coreBlk2")
   374  			return nil, nil
   375  		}
   376  	}
   377  
   378  	require.NoError(proVM.SetPreference(context.Background(), proBlk2.ID()))
   379  
   380  	// build block...
   381  	coreBlk3 := snowmantest.BuildChild(prefcoreBlk)
   382  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   383  		return coreBlk3, nil
   384  	}
   385  
   386  	require.NoError(waitForProposerWindow(proVM, proBlk2, proBlk2.(*postForkBlock).PChainHeight()))
   387  	builtBlk, err := proVM.BuildBlock(context.Background())
   388  	require.NoError(err)
   389  
   390  	// ...show that parent is the preferred one
   391  	require.Equal(proBlk2.ID(), builtBlk.Parent())
   392  }
   393  
   394  func TestCoreBlocksMustBeBuiltOnPreferredCoreBlock(t *testing.T) {
   395  	require := require.New(t)
   396  
   397  	var (
   398  		activationTime = time.Unix(0, 0)
   399  		durangoTime    = activationTime
   400  	)
   401  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   402  	defer func() {
   403  		require.NoError(proVM.Shutdown(context.Background()))
   404  	}()
   405  
   406  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   407  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   408  		return coreBlk1, nil
   409  	}
   410  	proBlk1, err := proVM.BuildBlock(context.Background())
   411  	require.NoError(err)
   412  
   413  	coreBlk2 := snowmantest.BuildChild(snowmantest.Genesis)
   414  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   415  		return coreBlk2, nil
   416  	}
   417  	proBlk2, err := proVM.BuildBlock(context.Background())
   418  	require.NoError(err)
   419  	require.NotEqual(proBlk1.ID(), proBlk2.ID())
   420  
   421  	require.NoError(proBlk2.Verify(context.Background()))
   422  
   423  	// ...and set one as preferred
   424  	var wronglyPreferredcoreBlk *snowmantest.Block
   425  	coreVM.SetPreferenceF = func(_ context.Context, prefID ids.ID) error {
   426  		switch prefID {
   427  		case coreBlk1.ID():
   428  			wronglyPreferredcoreBlk = coreBlk2
   429  			return nil
   430  		case coreBlk2.ID():
   431  			wronglyPreferredcoreBlk = coreBlk1
   432  			return nil
   433  		default:
   434  			require.FailNow("Unknown core Blocks set as preferred")
   435  			return nil
   436  		}
   437  	}
   438  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   439  		switch {
   440  		case bytes.Equal(b, coreBlk1.Bytes()):
   441  			return coreBlk1, nil
   442  		case bytes.Equal(b, coreBlk2.Bytes()):
   443  			return coreBlk2, nil
   444  		default:
   445  			require.FailNow("Wrong bytes")
   446  			return nil, nil
   447  		}
   448  	}
   449  
   450  	require.NoError(proVM.SetPreference(context.Background(), proBlk2.ID()))
   451  
   452  	// build block...
   453  	coreBlk3 := snowmantest.BuildChild(wronglyPreferredcoreBlk)
   454  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   455  		return coreBlk3, nil
   456  	}
   457  
   458  	require.NoError(waitForProposerWindow(proVM, proBlk2, proBlk2.(*postForkBlock).PChainHeight()))
   459  	blk, err := proVM.BuildBlock(context.Background())
   460  	require.NoError(err)
   461  
   462  	err = blk.Verify(context.Background())
   463  	require.ErrorIs(err, errInnerParentMismatch)
   464  }
   465  
   466  // VM.ParseBlock tests section
   467  func TestCoreBlockFailureCauseProposerBlockParseFailure(t *testing.T) {
   468  	require := require.New(t)
   469  
   470  	var (
   471  		activationTime = time.Unix(0, 0)
   472  		durangoTime    = activationTime
   473  	)
   474  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   475  	defer func() {
   476  		require.NoError(proVM.Shutdown(context.Background()))
   477  	}()
   478  
   479  	coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) {
   480  		return nil, errMarshallingFailed
   481  	}
   482  
   483  	innerBlk := snowmantest.BuildChild(snowmantest.Genesis)
   484  	slb, err := statelessblock.Build(
   485  		proVM.preferred,
   486  		proVM.Time(),
   487  		100, // pChainHeight,
   488  		proVM.StakingCertLeaf,
   489  		innerBlk.Bytes(),
   490  		proVM.ctx.ChainID,
   491  		proVM.StakingLeafSigner,
   492  	)
   493  	require.NoError(err)
   494  	proBlk := postForkBlock{
   495  		SignedBlock: slb,
   496  		postForkCommonComponents: postForkCommonComponents{
   497  			vm:       proVM,
   498  			innerBlk: innerBlk,
   499  			status:   choices.Processing,
   500  		},
   501  	}
   502  
   503  	// test
   504  	_, err = proVM.ParseBlock(context.Background(), proBlk.Bytes())
   505  	require.ErrorIs(err, errMarshallingFailed)
   506  }
   507  
   508  func TestTwoProBlocksWrappingSameCoreBlockCanBeParsed(t *testing.T) {
   509  	require := require.New(t)
   510  
   511  	var (
   512  		activationTime = time.Unix(0, 0)
   513  		durangoTime    = activationTime
   514  	)
   515  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   516  	defer func() {
   517  		require.NoError(proVM.Shutdown(context.Background()))
   518  	}()
   519  
   520  	// create two Proposer blocks at the same height
   521  	innerBlk := snowmantest.BuildChild(snowmantest.Genesis)
   522  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   523  		require.Equal(innerBlk.Bytes(), b)
   524  		return innerBlk, nil
   525  	}
   526  
   527  	blkTimestamp := proVM.Time()
   528  
   529  	slb1, err := statelessblock.Build(
   530  		proVM.preferred,
   531  		blkTimestamp,
   532  		100, // pChainHeight,
   533  		proVM.StakingCertLeaf,
   534  		innerBlk.Bytes(),
   535  		proVM.ctx.ChainID,
   536  		proVM.StakingLeafSigner,
   537  	)
   538  	require.NoError(err)
   539  	proBlk1 := postForkBlock{
   540  		SignedBlock: slb1,
   541  		postForkCommonComponents: postForkCommonComponents{
   542  			vm:       proVM,
   543  			innerBlk: innerBlk,
   544  			status:   choices.Processing,
   545  		},
   546  	}
   547  
   548  	slb2, err := statelessblock.Build(
   549  		proVM.preferred,
   550  		blkTimestamp,
   551  		200, // pChainHeight,
   552  		proVM.StakingCertLeaf,
   553  		innerBlk.Bytes(),
   554  		proVM.ctx.ChainID,
   555  		proVM.StakingLeafSigner,
   556  	)
   557  	require.NoError(err)
   558  	proBlk2 := postForkBlock{
   559  		SignedBlock: slb2,
   560  		postForkCommonComponents: postForkCommonComponents{
   561  			vm:       proVM,
   562  			innerBlk: innerBlk,
   563  			status:   choices.Processing,
   564  		},
   565  	}
   566  
   567  	require.NotEqual(proBlk1.ID(), proBlk2.ID())
   568  
   569  	// Show that both can be parsed and retrieved
   570  	parsedBlk1, err := proVM.ParseBlock(context.Background(), proBlk1.Bytes())
   571  	require.NoError(err)
   572  	parsedBlk2, err := proVM.ParseBlock(context.Background(), proBlk2.Bytes())
   573  	require.NoError(err)
   574  
   575  	require.Equal(proBlk1.ID(), parsedBlk1.ID())
   576  	require.Equal(proBlk2.ID(), parsedBlk2.ID())
   577  }
   578  
   579  // VM.BuildBlock and VM.ParseBlock interoperability tests section
   580  func TestTwoProBlocksWithSameParentCanBothVerify(t *testing.T) {
   581  	require := require.New(t)
   582  
   583  	var (
   584  		activationTime = time.Unix(0, 0)
   585  		durangoTime    = activationTime
   586  	)
   587  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   588  	defer func() {
   589  		require.NoError(proVM.Shutdown(context.Background()))
   590  	}()
   591  
   592  	// one block is built from this proVM
   593  	localcoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   594  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   595  		return localcoreBlk, nil
   596  	}
   597  
   598  	builtBlk, err := proVM.BuildBlock(context.Background())
   599  	require.NoError(err)
   600  	require.NoError(builtBlk.Verify(context.Background()))
   601  
   602  	// another block with same parent comes from network and is parsed
   603  	netcoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   604  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   605  		switch {
   606  		case bytes.Equal(b, snowmantest.GenesisBytes):
   607  			return snowmantest.Genesis, nil
   608  		case bytes.Equal(b, localcoreBlk.Bytes()):
   609  			return localcoreBlk, nil
   610  		case bytes.Equal(b, netcoreBlk.Bytes()):
   611  			return netcoreBlk, nil
   612  		default:
   613  			require.FailNow("Unknown bytes")
   614  			return nil, nil
   615  		}
   616  	}
   617  
   618  	pChainHeight, err := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background())
   619  	require.NoError(err)
   620  
   621  	netSlb, err := statelessblock.BuildUnsigned(
   622  		proVM.preferred,
   623  		proVM.Time(),
   624  		pChainHeight,
   625  		netcoreBlk.Bytes(),
   626  	)
   627  	require.NoError(err)
   628  	netProBlk := postForkBlock{
   629  		SignedBlock: netSlb,
   630  		postForkCommonComponents: postForkCommonComponents{
   631  			vm:       proVM,
   632  			innerBlk: netcoreBlk,
   633  			status:   choices.Processing,
   634  		},
   635  	}
   636  
   637  	// prove that also block from network verifies
   638  	require.NoError(netProBlk.Verify(context.Background()))
   639  }
   640  
   641  // Pre Fork tests section
   642  func TestPreFork_Initialize(t *testing.T) {
   643  	require := require.New(t)
   644  
   645  	var (
   646  		activationTime = mockable.MaxTime
   647  		durangoTime    = activationTime
   648  	)
   649  	_, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   650  	defer func() {
   651  		require.NoError(proVM.Shutdown(context.Background()))
   652  	}()
   653  
   654  	// checks
   655  	blkID, err := proVM.LastAccepted(context.Background())
   656  	require.NoError(err)
   657  
   658  	rtvdBlk, err := proVM.GetBlock(context.Background(), blkID)
   659  	require.NoError(err)
   660  
   661  	require.IsType(&preForkBlock{}, rtvdBlk)
   662  	require.Equal(snowmantest.GenesisBytes, rtvdBlk.Bytes())
   663  }
   664  
   665  func TestPreFork_BuildBlock(t *testing.T) {
   666  	require := require.New(t)
   667  
   668  	var (
   669  		activationTime = mockable.MaxTime
   670  		durangoTime    = activationTime
   671  	)
   672  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   673  	defer func() {
   674  		require.NoError(proVM.Shutdown(context.Background()))
   675  	}()
   676  
   677  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   678  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   679  		return coreBlk, nil
   680  	}
   681  
   682  	// test
   683  	builtBlk, err := proVM.BuildBlock(context.Background())
   684  	require.NoError(err)
   685  	require.IsType(&preForkBlock{}, builtBlk)
   686  	require.Equal(coreBlk.ID(), builtBlk.ID())
   687  	require.Equal(coreBlk.Bytes(), builtBlk.Bytes())
   688  
   689  	// test
   690  	coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) {
   691  		return coreBlk, nil
   692  	}
   693  	storedBlk, err := proVM.GetBlock(context.Background(), builtBlk.ID())
   694  	require.NoError(err)
   695  	require.Equal(builtBlk.ID(), storedBlk.ID())
   696  }
   697  
   698  func TestPreFork_ParseBlock(t *testing.T) {
   699  	require := require.New(t)
   700  
   701  	var (
   702  		activationTime = mockable.MaxTime
   703  		durangoTime    = activationTime
   704  	)
   705  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   706  	defer func() {
   707  		require.NoError(proVM.Shutdown(context.Background()))
   708  	}()
   709  
   710  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   711  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   712  		require.Equal(coreBlk.Bytes(), b)
   713  		return coreBlk, nil
   714  	}
   715  
   716  	parsedBlk, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes())
   717  	require.NoError(err)
   718  	require.IsType(&preForkBlock{}, parsedBlk)
   719  	require.Equal(coreBlk.ID(), parsedBlk.ID())
   720  	require.Equal(coreBlk.Bytes(), parsedBlk.Bytes())
   721  
   722  	coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) {
   723  		require.Equal(coreBlk.ID(), id)
   724  		return coreBlk, nil
   725  	}
   726  	storedBlk, err := proVM.GetBlock(context.Background(), parsedBlk.ID())
   727  	require.NoError(err)
   728  	require.Equal(parsedBlk.ID(), storedBlk.ID())
   729  }
   730  
   731  func TestPreFork_SetPreference(t *testing.T) {
   732  	require := require.New(t)
   733  
   734  	var (
   735  		activationTime = mockable.MaxTime
   736  		durangoTime    = activationTime
   737  	)
   738  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   739  	defer func() {
   740  		require.NoError(proVM.Shutdown(context.Background()))
   741  	}()
   742  
   743  	coreBlk0 := snowmantest.BuildChild(snowmantest.Genesis)
   744  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   745  		return coreBlk0, nil
   746  	}
   747  	builtBlk, err := proVM.BuildBlock(context.Background())
   748  	require.NoError(err)
   749  
   750  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   751  		switch blkID {
   752  		case snowmantest.GenesisID:
   753  			return snowmantest.Genesis, nil
   754  		case coreBlk0.ID():
   755  			return coreBlk0, nil
   756  		default:
   757  			return nil, errUnknownBlock
   758  		}
   759  	}
   760  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   761  		switch {
   762  		case bytes.Equal(b, snowmantest.GenesisBytes):
   763  			return snowmantest.Genesis, nil
   764  		case bytes.Equal(b, coreBlk0.Bytes()):
   765  			return coreBlk0, nil
   766  		default:
   767  			return nil, errUnknownBlock
   768  		}
   769  	}
   770  	require.NoError(proVM.SetPreference(context.Background(), builtBlk.ID()))
   771  
   772  	coreBlk1 := snowmantest.BuildChild(coreBlk0)
   773  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   774  		return coreBlk1, nil
   775  	}
   776  	nextBlk, err := proVM.BuildBlock(context.Background())
   777  	require.NoError(err)
   778  	require.Equal(builtBlk.ID(), nextBlk.Parent())
   779  }
   780  
   781  func TestExpiredBuildBlock(t *testing.T) {
   782  	require := require.New(t)
   783  
   784  	coreVM := &block.TestVM{}
   785  	coreVM.T = t
   786  
   787  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   788  		return snowmantest.GenesisID, nil
   789  	}
   790  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   791  		switch blkID {
   792  		case snowmantest.GenesisID:
   793  			return snowmantest.Genesis, nil
   794  		default:
   795  			return nil, errUnknownBlock
   796  		}
   797  	}
   798  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   799  		switch {
   800  		case bytes.Equal(b, snowmantest.GenesisBytes):
   801  			return snowmantest.Genesis, nil
   802  		default:
   803  			return nil, errUnknownBlock
   804  		}
   805  	}
   806  
   807  	proVM := New(
   808  		coreVM,
   809  		Config{
   810  			ActivationTime:      time.Time{},
   811  			DurangoTime:         mockable.MaxTime,
   812  			MinimumPChainHeight: 0,
   813  			MinBlkDelay:         DefaultMinBlockDelay,
   814  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
   815  			StakingLeafSigner:   pTestSigner,
   816  			StakingCertLeaf:     pTestCert,
   817  			Registerer:          prometheus.NewRegistry(),
   818  		},
   819  	)
   820  
   821  	valState := &validators.TestState{
   822  		T: t,
   823  	}
   824  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   825  		return snowmantest.GenesisHeight, nil
   826  	}
   827  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   828  		return defaultPChainHeight, nil
   829  	}
   830  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   831  		nodeID := ids.BuildTestNodeID([]byte{1})
   832  		return map[ids.NodeID]*validators.GetValidatorOutput{
   833  			nodeID: {
   834  				NodeID: nodeID,
   835  				Weight: 100,
   836  			},
   837  		}, nil
   838  	}
   839  
   840  	ctx := snowtest.Context(t, snowtest.CChainID)
   841  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
   842  	ctx.ValidatorState = valState
   843  
   844  	toEngine := make(chan common.Message, 1)
   845  	var toScheduler chan<- common.Message
   846  
   847  	coreVM.InitializeF = func(
   848  		_ context.Context,
   849  		_ *snow.Context,
   850  		_ database.Database,
   851  		_ []byte,
   852  		_ []byte,
   853  		_ []byte,
   854  		toEngineChan chan<- common.Message,
   855  		_ []*common.Fx,
   856  		_ common.AppSender,
   857  	) error {
   858  		toScheduler = toEngineChan
   859  		return nil
   860  	}
   861  
   862  	// make sure that DBs are compressed correctly
   863  	require.NoError(proVM.Initialize(
   864  		context.Background(),
   865  		ctx,
   866  		memdb.New(),
   867  		nil,
   868  		nil,
   869  		nil,
   870  		toEngine,
   871  		nil,
   872  		nil,
   873  	))
   874  	defer func() {
   875  		require.NoError(proVM.Shutdown(context.Background()))
   876  	}()
   877  
   878  	// Initialize shouldn't be called again
   879  	coreVM.InitializeF = nil
   880  
   881  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
   882  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
   883  
   884  	// Notify the proposer VM of a new block on the inner block side
   885  	toScheduler <- common.PendingTxs
   886  	// The first notification will be read from the consensus engine
   887  	<-toEngine
   888  
   889  	// Before calling BuildBlock, verify a remote block and set it as the
   890  	// preferred block.
   891  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   892  	statelessBlock, err := statelessblock.BuildUnsigned(
   893  		snowmantest.GenesisID,
   894  		proVM.Time(),
   895  		0,
   896  		coreBlk.Bytes(),
   897  	)
   898  	require.NoError(err)
   899  
   900  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   901  		switch blkID {
   902  		case snowmantest.GenesisID:
   903  			return snowmantest.Genesis, nil
   904  		case coreBlk.ID():
   905  			return coreBlk, nil
   906  		default:
   907  			return nil, errUnknownBlock
   908  		}
   909  	}
   910  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   911  		switch {
   912  		case bytes.Equal(b, snowmantest.GenesisBytes):
   913  			return snowmantest.Genesis, nil
   914  		case bytes.Equal(b, coreBlk.Bytes()):
   915  			return coreBlk, nil
   916  		default:
   917  			return nil, errUnknownBlock
   918  		}
   919  	}
   920  
   921  	proVM.Clock.Set(statelessBlock.Timestamp())
   922  
   923  	parsedBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes())
   924  	require.NoError(err)
   925  
   926  	require.NoError(parsedBlock.Verify(context.Background()))
   927  	require.NoError(proVM.SetPreference(context.Background(), parsedBlock.ID()))
   928  
   929  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   930  		require.FailNow(fmt.Errorf("%w: BuildBlock", errUnexpectedCall).Error())
   931  		return nil, errUnexpectedCall
   932  	}
   933  
   934  	// Because we are now building on a different block, the proposer window
   935  	// shouldn't have started.
   936  	_, err = proVM.BuildBlock(context.Background())
   937  	require.ErrorIs(err, errProposerWindowNotStarted)
   938  
   939  	proVM.Set(statelessBlock.Timestamp().Add(proposer.MaxBuildDelay))
   940  	proVM.Scheduler.SetBuildBlockTime(time.Now())
   941  
   942  	// The engine should have been notified to attempt to build a block now that
   943  	// the window has started again. This is to guarantee that the inner VM has
   944  	// build block called after it sent a pendingTxs message on its internal
   945  	// engine channel.
   946  	<-toEngine
   947  }
   948  
   949  type wrappedBlock struct {
   950  	snowman.Block
   951  	verified bool
   952  }
   953  
   954  func (b *wrappedBlock) Accept(ctx context.Context) error {
   955  	if !b.verified {
   956  		return errUnverifiedBlock
   957  	}
   958  	return b.Block.Accept(ctx)
   959  }
   960  
   961  func (b *wrappedBlock) Verify(ctx context.Context) error {
   962  	if err := b.Block.Verify(ctx); err != nil {
   963  		return err
   964  	}
   965  	b.verified = true
   966  	return nil
   967  }
   968  
   969  func TestInnerBlockDeduplication(t *testing.T) {
   970  	require := require.New(t)
   971  
   972  	var (
   973  		activationTime = time.Unix(0, 0)
   974  		durangoTime    = activationTime
   975  	)
   976  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   977  	defer func() {
   978  		require.NoError(proVM.Shutdown(context.Background()))
   979  	}()
   980  
   981  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   982  	coreBlk0 := &wrappedBlock{
   983  		Block: coreBlk,
   984  	}
   985  	coreBlk1 := &wrappedBlock{
   986  		Block: coreBlk,
   987  	}
   988  	statelessBlock0, err := statelessblock.BuildUnsigned(
   989  		snowmantest.GenesisID,
   990  		coreBlk.Timestamp(),
   991  		0,
   992  		coreBlk.Bytes(),
   993  	)
   994  	require.NoError(err)
   995  	statelessBlock1, err := statelessblock.BuildUnsigned(
   996  		snowmantest.GenesisID,
   997  		coreBlk.Timestamp(),
   998  		1,
   999  		coreBlk.Bytes(),
  1000  	)
  1001  	require.NoError(err)
  1002  
  1003  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1004  		switch blkID {
  1005  		case snowmantest.GenesisID:
  1006  			return snowmantest.Genesis, nil
  1007  		case coreBlk0.ID():
  1008  			return coreBlk0, nil
  1009  		default:
  1010  			return nil, errUnknownBlock
  1011  		}
  1012  	}
  1013  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1014  		switch {
  1015  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1016  			return snowmantest.Genesis, nil
  1017  		case bytes.Equal(b, coreBlk0.Bytes()):
  1018  			return coreBlk0, nil
  1019  		default:
  1020  			return nil, errUnknownBlock
  1021  		}
  1022  	}
  1023  
  1024  	parsedBlock0, err := proVM.ParseBlock(context.Background(), statelessBlock0.Bytes())
  1025  	require.NoError(err)
  1026  
  1027  	require.NoError(parsedBlock0.Verify(context.Background()))
  1028  
  1029  	require.NoError(proVM.SetPreference(context.Background(), parsedBlock0.ID()))
  1030  
  1031  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1032  		switch blkID {
  1033  		case snowmantest.GenesisID:
  1034  			return snowmantest.Genesis, nil
  1035  		case coreBlk1.ID():
  1036  			return coreBlk1, nil
  1037  		default:
  1038  			return nil, errUnknownBlock
  1039  		}
  1040  	}
  1041  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1042  		switch {
  1043  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1044  			return snowmantest.Genesis, nil
  1045  		case bytes.Equal(b, coreBlk1.Bytes()):
  1046  			return coreBlk1, nil
  1047  		default:
  1048  			return nil, errUnknownBlock
  1049  		}
  1050  	}
  1051  
  1052  	parsedBlock1, err := proVM.ParseBlock(context.Background(), statelessBlock1.Bytes())
  1053  	require.NoError(err)
  1054  
  1055  	require.NoError(parsedBlock1.Verify(context.Background()))
  1056  
  1057  	require.NoError(proVM.SetPreference(context.Background(), parsedBlock1.ID()))
  1058  
  1059  	require.NoError(parsedBlock1.Accept(context.Background()))
  1060  }
  1061  
  1062  func TestInnerVMRollback(t *testing.T) {
  1063  	require := require.New(t)
  1064  
  1065  	valState := &validators.TestState{
  1066  		T: t,
  1067  	}
  1068  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
  1069  		return defaultPChainHeight, nil
  1070  	}
  1071  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
  1072  		nodeID := ids.BuildTestNodeID([]byte{1})
  1073  		return map[ids.NodeID]*validators.GetValidatorOutput{
  1074  			nodeID: {
  1075  				NodeID: nodeID,
  1076  				Weight: 100,
  1077  			},
  1078  		}, nil
  1079  	}
  1080  
  1081  	coreVM := &block.TestVM{}
  1082  	coreVM.T = t
  1083  
  1084  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
  1085  		return snowmantest.GenesisID, nil
  1086  	}
  1087  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1088  		switch blkID {
  1089  		case snowmantest.GenesisID:
  1090  			return snowmantest.Genesis, nil
  1091  		default:
  1092  			return nil, errUnknownBlock
  1093  		}
  1094  	}
  1095  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1096  		switch {
  1097  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1098  			return snowmantest.Genesis, nil
  1099  		default:
  1100  			return nil, errUnknownBlock
  1101  		}
  1102  	}
  1103  
  1104  	ctx := snowtest.Context(t, snowtest.CChainID)
  1105  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
  1106  	ctx.ValidatorState = valState
  1107  
  1108  	coreVM.InitializeF = func(
  1109  		context.Context,
  1110  		*snow.Context,
  1111  		database.Database,
  1112  		[]byte,
  1113  		[]byte,
  1114  		[]byte,
  1115  		chan<- common.Message,
  1116  		[]*common.Fx,
  1117  		common.AppSender,
  1118  	) error {
  1119  		return nil
  1120  	}
  1121  
  1122  	db := memdb.New()
  1123  
  1124  	proVM := New(
  1125  		coreVM,
  1126  		Config{
  1127  			ActivationTime:      time.Time{},
  1128  			DurangoTime:         mockable.MaxTime,
  1129  			MinimumPChainHeight: 0,
  1130  			MinBlkDelay:         DefaultMinBlockDelay,
  1131  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  1132  			StakingLeafSigner:   pTestSigner,
  1133  			StakingCertLeaf:     pTestCert,
  1134  			Registerer:          prometheus.NewRegistry(),
  1135  		},
  1136  	)
  1137  
  1138  	require.NoError(proVM.Initialize(
  1139  		context.Background(),
  1140  		ctx,
  1141  		db,
  1142  		nil,
  1143  		nil,
  1144  		nil,
  1145  		nil,
  1146  		nil,
  1147  		nil,
  1148  	))
  1149  
  1150  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  1151  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
  1152  
  1153  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
  1154  	statelessBlock, err := statelessblock.BuildUnsigned(
  1155  		snowmantest.GenesisID,
  1156  		coreBlk.Timestamp(),
  1157  		0,
  1158  		coreBlk.Bytes(),
  1159  	)
  1160  	require.NoError(err)
  1161  
  1162  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1163  		switch blkID {
  1164  		case snowmantest.GenesisID:
  1165  			return snowmantest.Genesis, nil
  1166  		case coreBlk.ID():
  1167  			return coreBlk, nil
  1168  		default:
  1169  			return nil, errUnknownBlock
  1170  		}
  1171  	}
  1172  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1173  		switch {
  1174  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1175  			return snowmantest.Genesis, nil
  1176  		case bytes.Equal(b, coreBlk.Bytes()):
  1177  			return coreBlk, nil
  1178  		default:
  1179  			return nil, errUnknownBlock
  1180  		}
  1181  	}
  1182  
  1183  	proVM.Clock.Set(statelessBlock.Timestamp())
  1184  
  1185  	parsedBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes())
  1186  	require.NoError(err)
  1187  
  1188  	require.Equal(choices.Processing, parsedBlock.Status())
  1189  
  1190  	require.NoError(parsedBlock.Verify(context.Background()))
  1191  	require.NoError(proVM.SetPreference(context.Background(), parsedBlock.ID()))
  1192  	require.NoError(parsedBlock.Accept(context.Background()))
  1193  
  1194  	fetchedBlock, err := proVM.GetBlock(context.Background(), parsedBlock.ID())
  1195  	require.NoError(err)
  1196  
  1197  	require.Equal(choices.Accepted, fetchedBlock.Status())
  1198  
  1199  	// Restart the node and have the inner VM rollback state.
  1200  	require.NoError(proVM.Shutdown(context.Background()))
  1201  	coreBlk.StatusV = choices.Processing
  1202  
  1203  	proVM = New(
  1204  		coreVM,
  1205  		Config{
  1206  			ActivationTime:      time.Time{},
  1207  			DurangoTime:         mockable.MaxTime,
  1208  			MinimumPChainHeight: 0,
  1209  			MinBlkDelay:         DefaultMinBlockDelay,
  1210  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  1211  			StakingLeafSigner:   pTestSigner,
  1212  			StakingCertLeaf:     pTestCert,
  1213  			Registerer:          prometheus.NewRegistry(),
  1214  		},
  1215  	)
  1216  
  1217  	require.NoError(proVM.Initialize(
  1218  		context.Background(),
  1219  		ctx,
  1220  		db,
  1221  		nil,
  1222  		nil,
  1223  		nil,
  1224  		nil,
  1225  		nil,
  1226  		nil,
  1227  	))
  1228  	defer func() {
  1229  		require.NoError(proVM.Shutdown(context.Background()))
  1230  	}()
  1231  
  1232  	lastAcceptedID, err := proVM.LastAccepted(context.Background())
  1233  	require.NoError(err)
  1234  
  1235  	require.Equal(snowmantest.GenesisID, lastAcceptedID)
  1236  
  1237  	parsedBlock, err = proVM.ParseBlock(context.Background(), statelessBlock.Bytes())
  1238  	require.NoError(err)
  1239  
  1240  	require.Equal(choices.Processing, parsedBlock.Status())
  1241  }
  1242  
  1243  func TestBuildBlockDuringWindow(t *testing.T) {
  1244  	require := require.New(t)
  1245  
  1246  	var (
  1247  		activationTime = time.Unix(0, 0)
  1248  		durangoTime    = mockable.MaxTime
  1249  	)
  1250  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1251  	defer func() {
  1252  		require.NoError(proVM.Shutdown(context.Background()))
  1253  	}()
  1254  
  1255  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
  1256  		return map[ids.NodeID]*validators.GetValidatorOutput{
  1257  			proVM.ctx.NodeID: {
  1258  				NodeID: proVM.ctx.NodeID,
  1259  				Weight: 10,
  1260  			},
  1261  		}, nil
  1262  	}
  1263  
  1264  	coreBlk0 := snowmantest.BuildChild(snowmantest.Genesis)
  1265  	coreBlk1 := snowmantest.BuildChild(coreBlk0)
  1266  	statelessBlock0, err := statelessblock.BuildUnsigned(
  1267  		snowmantest.GenesisID,
  1268  		proVM.Time(),
  1269  		0,
  1270  		coreBlk0.Bytes(),
  1271  	)
  1272  	require.NoError(err)
  1273  
  1274  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1275  		switch blkID {
  1276  		case snowmantest.GenesisID:
  1277  			return snowmantest.Genesis, nil
  1278  		case coreBlk0.ID():
  1279  			return coreBlk0, nil
  1280  		case coreBlk1.ID():
  1281  			return coreBlk1, nil
  1282  		default:
  1283  			return nil, errUnknownBlock
  1284  		}
  1285  	}
  1286  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1287  		switch {
  1288  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1289  			return snowmantest.Genesis, nil
  1290  		case bytes.Equal(b, coreBlk0.Bytes()):
  1291  			return coreBlk0, nil
  1292  		case bytes.Equal(b, coreBlk1.Bytes()):
  1293  			return coreBlk1, nil
  1294  		default:
  1295  			return nil, errUnknownBlock
  1296  		}
  1297  	}
  1298  
  1299  	proVM.Clock.Set(statelessBlock0.Timestamp())
  1300  
  1301  	statefulBlock0, err := proVM.ParseBlock(context.Background(), statelessBlock0.Bytes())
  1302  	require.NoError(err)
  1303  
  1304  	require.NoError(statefulBlock0.Verify(context.Background()))
  1305  
  1306  	require.NoError(proVM.SetPreference(context.Background(), statefulBlock0.ID()))
  1307  
  1308  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1309  		return coreBlk1, nil
  1310  	}
  1311  
  1312  	statefulBlock1, err := proVM.BuildBlock(context.Background())
  1313  	require.NoError(err)
  1314  
  1315  	require.NoError(statefulBlock1.Verify(context.Background()))
  1316  
  1317  	require.NoError(proVM.SetPreference(context.Background(), statefulBlock1.ID()))
  1318  
  1319  	require.NoError(statefulBlock0.Accept(context.Background()))
  1320  
  1321  	require.NoError(statefulBlock1.Accept(context.Background()))
  1322  }
  1323  
  1324  // Ensure that Accepting a PostForkBlock (A) containing core block (X) causes
  1325  // core block (Y) and (Z) to also be rejected.
  1326  //
  1327  //	     G
  1328  //	   /   \
  1329  //	A(X)   B(Y)
  1330  //	        |
  1331  //	       C(Z)
  1332  func TestTwoForks_OneIsAccepted(t *testing.T) {
  1333  	require := require.New(t)
  1334  
  1335  	var (
  1336  		activationTime = time.Unix(0, 0)
  1337  		durangoTime    = mockable.MaxTime
  1338  	)
  1339  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1340  	defer func() {
  1341  		require.NoError(proVM.Shutdown(context.Background()))
  1342  	}()
  1343  
  1344  	// create pre-fork block X and post-fork block A
  1345  	xBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1346  
  1347  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1348  		return xBlock, nil
  1349  	}
  1350  	aBlock, err := proVM.BuildBlock(context.Background())
  1351  	require.NoError(err)
  1352  	coreVM.BuildBlockF = nil
  1353  	require.NoError(aBlock.Verify(context.Background()))
  1354  
  1355  	// use a different way to construct pre-fork block Y and post-fork block B
  1356  	yBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1357  
  1358  	ySlb, err := statelessblock.BuildUnsigned(
  1359  		snowmantest.GenesisID,
  1360  		proVM.Time(),
  1361  		defaultPChainHeight,
  1362  		yBlock.Bytes(),
  1363  	)
  1364  	require.NoError(err)
  1365  
  1366  	bBlock := postForkBlock{
  1367  		SignedBlock: ySlb,
  1368  		postForkCommonComponents: postForkCommonComponents{
  1369  			vm:       proVM,
  1370  			innerBlk: yBlock,
  1371  			status:   choices.Processing,
  1372  		},
  1373  	}
  1374  
  1375  	require.NoError(bBlock.Verify(context.Background()))
  1376  
  1377  	// append Z/C to Y/B
  1378  	zBlock := snowmantest.BuildChild(yBlock)
  1379  
  1380  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1381  		return zBlock, nil
  1382  	}
  1383  	require.NoError(proVM.SetPreference(context.Background(), bBlock.ID()))
  1384  	proVM.Set(proVM.Time().Add(proposer.MaxBuildDelay))
  1385  	cBlock, err := proVM.BuildBlock(context.Background())
  1386  	require.NoError(err)
  1387  	coreVM.BuildBlockF = nil
  1388  
  1389  	require.NoError(cBlock.Verify(context.Background()))
  1390  
  1391  	require.Equal(bBlock.Parent(), aBlock.Parent())
  1392  	require.Equal(yBlock.ID(), zBlock.Parent())
  1393  	require.Equal(bBlock.ID(), cBlock.Parent())
  1394  
  1395  	require.NotEqual(choices.Rejected, yBlock.Status())
  1396  
  1397  	// accept A
  1398  	require.NoError(aBlock.Accept(context.Background()))
  1399  
  1400  	require.Equal(choices.Accepted, xBlock.Status())
  1401  	require.Equal(choices.Rejected, yBlock.Status())
  1402  	require.Equal(choices.Rejected, zBlock.Status())
  1403  }
  1404  
  1405  func TestTooFarAdvanced(t *testing.T) {
  1406  	require := require.New(t)
  1407  
  1408  	var (
  1409  		activationTime = time.Unix(0, 0)
  1410  		durangoTime    = mockable.MaxTime
  1411  	)
  1412  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1413  	defer func() {
  1414  		require.NoError(proVM.Shutdown(context.Background()))
  1415  	}()
  1416  
  1417  	xBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1418  	yBlock := snowmantest.BuildChild(xBlock)
  1419  
  1420  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1421  		return xBlock, nil
  1422  	}
  1423  	aBlock, err := proVM.BuildBlock(context.Background())
  1424  	require.NoError(err)
  1425  	require.NoError(aBlock.Verify(context.Background()))
  1426  
  1427  	ySlb, err := statelessblock.BuildUnsigned(
  1428  		aBlock.ID(),
  1429  		aBlock.Timestamp().Add(maxSkew),
  1430  		defaultPChainHeight,
  1431  		yBlock.Bytes(),
  1432  	)
  1433  	require.NoError(err)
  1434  
  1435  	bBlock := postForkBlock{
  1436  		SignedBlock: ySlb,
  1437  		postForkCommonComponents: postForkCommonComponents{
  1438  			vm:       proVM,
  1439  			innerBlk: yBlock,
  1440  			status:   choices.Processing,
  1441  		},
  1442  	}
  1443  
  1444  	err = bBlock.Verify(context.Background())
  1445  	require.ErrorIs(err, errProposerWindowNotStarted)
  1446  
  1447  	ySlb, err = statelessblock.BuildUnsigned(
  1448  		aBlock.ID(),
  1449  		aBlock.Timestamp().Add(proposer.MaxVerifyDelay),
  1450  		defaultPChainHeight,
  1451  		yBlock.Bytes(),
  1452  	)
  1453  
  1454  	require.NoError(err)
  1455  
  1456  	bBlock = postForkBlock{
  1457  		SignedBlock: ySlb,
  1458  		postForkCommonComponents: postForkCommonComponents{
  1459  			vm:       proVM,
  1460  			innerBlk: yBlock,
  1461  			status:   choices.Processing,
  1462  		},
  1463  	}
  1464  
  1465  	err = bBlock.Verify(context.Background())
  1466  	require.ErrorIs(err, errTimeTooAdvanced)
  1467  }
  1468  
  1469  // Ensure that Accepting a PostForkOption (B) causes both the other option and
  1470  // the core block in the other option to be rejected.
  1471  //
  1472  //	   G
  1473  //	   |
  1474  //	  A(X)
  1475  //	 /====\
  1476  //	B(...) C(...)
  1477  //
  1478  // B(...) is B(X.opts[0])
  1479  // B(...) is C(X.opts[1])
  1480  func TestTwoOptions_OneIsAccepted(t *testing.T) {
  1481  	require := require.New(t)
  1482  
  1483  	var (
  1484  		activationTime = time.Unix(0, 0)
  1485  		durangoTime    = mockable.MaxTime
  1486  	)
  1487  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1488  	defer func() {
  1489  		require.NoError(proVM.Shutdown(context.Background()))
  1490  	}()
  1491  
  1492  	xTestBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1493  	xBlock := &TestOptionsBlock{
  1494  		Block: *xTestBlock,
  1495  		opts: [2]snowman.Block{
  1496  			snowmantest.BuildChild(xTestBlock),
  1497  			snowmantest.BuildChild(xTestBlock),
  1498  		},
  1499  	}
  1500  
  1501  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1502  		return xBlock, nil
  1503  	}
  1504  	aBlockIntf, err := proVM.BuildBlock(context.Background())
  1505  	require.NoError(err)
  1506  
  1507  	require.IsType(&postForkBlock{}, aBlockIntf)
  1508  	aBlock := aBlockIntf.(*postForkBlock)
  1509  
  1510  	opts, err := aBlock.Options(context.Background())
  1511  	require.NoError(err)
  1512  
  1513  	require.NoError(aBlock.Verify(context.Background()))
  1514  	bBlock := opts[0]
  1515  	require.NoError(bBlock.Verify(context.Background()))
  1516  	cBlock := opts[1]
  1517  	require.NoError(cBlock.Verify(context.Background()))
  1518  
  1519  	require.NoError(aBlock.Accept(context.Background()))
  1520  
  1521  	require.NoError(bBlock.Accept(context.Background()))
  1522  
  1523  	// the other pre-fork option should be rejected
  1524  	require.Equal(choices.Rejected, xBlock.opts[1].Status())
  1525  
  1526  	// the other post-fork option should also be rejected
  1527  	require.NoError(cBlock.Reject(context.Background()))
  1528  
  1529  	require.Equal(choices.Rejected, cBlock.Status())
  1530  }
  1531  
  1532  // Ensure that given the chance, built blocks will reference a lagged P-chain
  1533  // height.
  1534  func TestLaggedPChainHeight(t *testing.T) {
  1535  	require := require.New(t)
  1536  
  1537  	var (
  1538  		activationTime = time.Unix(0, 0)
  1539  		durangoTime    = activationTime
  1540  	)
  1541  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1542  	defer func() {
  1543  		require.NoError(proVM.Shutdown(context.Background()))
  1544  	}()
  1545  
  1546  	innerBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1547  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1548  		return innerBlock, nil
  1549  	}
  1550  	blockIntf, err := proVM.BuildBlock(context.Background())
  1551  	require.NoError(err)
  1552  
  1553  	require.IsType(&postForkBlock{}, blockIntf)
  1554  	block := blockIntf.(*postForkBlock)
  1555  
  1556  	pChainHeight := block.PChainHeight()
  1557  	require.Equal(snowmantest.GenesisHeight, pChainHeight)
  1558  }
  1559  
  1560  // Ensure that rejecting a block does not modify the accepted block ID for the
  1561  // rejected height.
  1562  func TestRejectedHeightNotIndexed(t *testing.T) {
  1563  	require := require.New(t)
  1564  
  1565  	coreHeights := []ids.ID{snowmantest.GenesisID}
  1566  
  1567  	initialState := []byte("genesis state")
  1568  	coreVM := &block.TestVM{
  1569  		TestVM: common.TestVM{
  1570  			T: t,
  1571  		},
  1572  		GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) {
  1573  			if height >= uint64(len(coreHeights)) {
  1574  				return ids.Empty, errTooHigh
  1575  			}
  1576  			return coreHeights[height], nil
  1577  		},
  1578  	}
  1579  
  1580  	coreVM.InitializeF = func(context.Context, *snow.Context, database.Database,
  1581  		[]byte, []byte, []byte, chan<- common.Message,
  1582  		[]*common.Fx, common.AppSender,
  1583  	) error {
  1584  		return nil
  1585  	}
  1586  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
  1587  		return snowmantest.GenesisID, nil
  1588  	}
  1589  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1590  		switch blkID {
  1591  		case snowmantest.GenesisID:
  1592  			return snowmantest.Genesis, nil
  1593  		default:
  1594  			return nil, errUnknownBlock
  1595  		}
  1596  	}
  1597  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1598  		switch {
  1599  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1600  			return snowmantest.Genesis, nil
  1601  		default:
  1602  			return nil, errUnknownBlock
  1603  		}
  1604  	}
  1605  
  1606  	proVM := New(
  1607  		coreVM,
  1608  		Config{
  1609  			ActivationTime:      time.Unix(0, 0),
  1610  			DurangoTime:         time.Unix(0, 0),
  1611  			MinimumPChainHeight: 0,
  1612  			MinBlkDelay:         DefaultMinBlockDelay,
  1613  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  1614  			StakingLeafSigner:   pTestSigner,
  1615  			StakingCertLeaf:     pTestCert,
  1616  			Registerer:          prometheus.NewRegistry(),
  1617  		},
  1618  	)
  1619  
  1620  	valState := &validators.TestState{
  1621  		T: t,
  1622  	}
  1623  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
  1624  		return snowmantest.GenesisHeight, nil
  1625  	}
  1626  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
  1627  		return defaultPChainHeight, nil
  1628  	}
  1629  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
  1630  		var (
  1631  			thisNode = proVM.ctx.NodeID
  1632  			nodeID1  = ids.BuildTestNodeID([]byte{1})
  1633  			nodeID2  = ids.BuildTestNodeID([]byte{2})
  1634  			nodeID3  = ids.BuildTestNodeID([]byte{3})
  1635  		)
  1636  		return map[ids.NodeID]*validators.GetValidatorOutput{
  1637  			thisNode: {
  1638  				NodeID: thisNode,
  1639  				Weight: 10,
  1640  			},
  1641  			nodeID1: {
  1642  				NodeID: nodeID1,
  1643  				Weight: 5,
  1644  			},
  1645  			nodeID2: {
  1646  				NodeID: nodeID2,
  1647  				Weight: 6,
  1648  			},
  1649  			nodeID3: {
  1650  				NodeID: nodeID3,
  1651  				Weight: 7,
  1652  			},
  1653  		}, nil
  1654  	}
  1655  
  1656  	ctx := snowtest.Context(t, snowtest.CChainID)
  1657  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
  1658  	ctx.ValidatorState = valState
  1659  
  1660  	require.NoError(proVM.Initialize(
  1661  		context.Background(),
  1662  		ctx,
  1663  		prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly
  1664  		initialState,
  1665  		nil,
  1666  		nil,
  1667  		nil,
  1668  		nil,
  1669  		nil,
  1670  	))
  1671  	defer func() {
  1672  		require.NoError(proVM.Shutdown(context.Background()))
  1673  	}()
  1674  
  1675  	// Initialize shouldn't be called again
  1676  	coreVM.InitializeF = nil
  1677  
  1678  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  1679  
  1680  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
  1681  
  1682  	// create inner block X and outer block A
  1683  	xBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1684  
  1685  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1686  		return xBlock, nil
  1687  	}
  1688  	aBlock, err := proVM.BuildBlock(context.Background())
  1689  	require.NoError(err)
  1690  
  1691  	coreVM.BuildBlockF = nil
  1692  	require.NoError(aBlock.Verify(context.Background()))
  1693  
  1694  	// use a different way to construct inner block Y and outer block B
  1695  	yBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1696  
  1697  	ySlb, err := statelessblock.BuildUnsigned(
  1698  		snowmantest.GenesisID,
  1699  		snowmantest.GenesisTimestamp,
  1700  		defaultPChainHeight,
  1701  		yBlock.Bytes(),
  1702  	)
  1703  	require.NoError(err)
  1704  
  1705  	bBlock := postForkBlock{
  1706  		SignedBlock: ySlb,
  1707  		postForkCommonComponents: postForkCommonComponents{
  1708  			vm:       proVM,
  1709  			innerBlk: yBlock,
  1710  			status:   choices.Processing,
  1711  		},
  1712  	}
  1713  
  1714  	require.NoError(bBlock.Verify(context.Background()))
  1715  
  1716  	// accept A
  1717  	require.NoError(aBlock.Accept(context.Background()))
  1718  	coreHeights = append(coreHeights, xBlock.ID())
  1719  
  1720  	blkID, err := proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height())
  1721  	require.NoError(err)
  1722  	require.Equal(aBlock.ID(), blkID)
  1723  
  1724  	// reject B
  1725  	require.NoError(bBlock.Reject(context.Background()))
  1726  
  1727  	blkID, err = proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height())
  1728  	require.NoError(err)
  1729  	require.Equal(aBlock.ID(), blkID)
  1730  }
  1731  
  1732  // Ensure that rejecting an option block does not modify the accepted block ID
  1733  // for the rejected height.
  1734  func TestRejectedOptionHeightNotIndexed(t *testing.T) {
  1735  	require := require.New(t)
  1736  
  1737  	coreHeights := []ids.ID{snowmantest.GenesisID}
  1738  
  1739  	initialState := []byte("genesis state")
  1740  	coreVM := &block.TestVM{
  1741  		TestVM: common.TestVM{
  1742  			T: t,
  1743  		},
  1744  		GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) {
  1745  			if height >= uint64(len(coreHeights)) {
  1746  				return ids.Empty, errTooHigh
  1747  			}
  1748  			return coreHeights[height], nil
  1749  		},
  1750  	}
  1751  
  1752  	coreVM.InitializeF = func(context.Context, *snow.Context, database.Database,
  1753  		[]byte, []byte, []byte, chan<- common.Message,
  1754  		[]*common.Fx, common.AppSender,
  1755  	) error {
  1756  		return nil
  1757  	}
  1758  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
  1759  		return snowmantest.GenesisID, nil
  1760  	}
  1761  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1762  		switch blkID {
  1763  		case snowmantest.GenesisID:
  1764  			return snowmantest.Genesis, nil
  1765  		default:
  1766  			return nil, errUnknownBlock
  1767  		}
  1768  	}
  1769  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1770  		switch {
  1771  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1772  			return snowmantest.Genesis, nil
  1773  		default:
  1774  			return nil, errUnknownBlock
  1775  		}
  1776  	}
  1777  
  1778  	proVM := New(
  1779  		coreVM,
  1780  		Config{
  1781  			ActivationTime:      time.Unix(0, 0),
  1782  			DurangoTime:         time.Unix(0, 0),
  1783  			MinimumPChainHeight: 0,
  1784  			MinBlkDelay:         DefaultMinBlockDelay,
  1785  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  1786  			StakingLeafSigner:   pTestSigner,
  1787  			StakingCertLeaf:     pTestCert,
  1788  			Registerer:          prometheus.NewRegistry(),
  1789  		},
  1790  	)
  1791  
  1792  	valState := &validators.TestState{
  1793  		T: t,
  1794  	}
  1795  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
  1796  		return snowmantest.GenesisHeight, nil
  1797  	}
  1798  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
  1799  		return defaultPChainHeight, nil
  1800  	}
  1801  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
  1802  		var (
  1803  			thisNode = proVM.ctx.NodeID
  1804  			nodeID1  = ids.BuildTestNodeID([]byte{1})
  1805  			nodeID2  = ids.BuildTestNodeID([]byte{2})
  1806  			nodeID3  = ids.BuildTestNodeID([]byte{3})
  1807  		)
  1808  		return map[ids.NodeID]*validators.GetValidatorOutput{
  1809  			thisNode: {
  1810  				NodeID: thisNode,
  1811  				Weight: 10,
  1812  			},
  1813  			nodeID1: {
  1814  				NodeID: nodeID1,
  1815  				Weight: 5,
  1816  			},
  1817  			nodeID2: {
  1818  				NodeID: nodeID2,
  1819  				Weight: 6,
  1820  			},
  1821  			nodeID3: {
  1822  				NodeID: nodeID3,
  1823  				Weight: 7,
  1824  			},
  1825  		}, nil
  1826  	}
  1827  
  1828  	ctx := snowtest.Context(t, snowtest.CChainID)
  1829  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
  1830  	ctx.ValidatorState = valState
  1831  
  1832  	require.NoError(proVM.Initialize(
  1833  		context.Background(),
  1834  		ctx,
  1835  		prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly
  1836  		initialState,
  1837  		nil,
  1838  		nil,
  1839  		nil,
  1840  		nil,
  1841  		nil,
  1842  	))
  1843  	defer func() {
  1844  		require.NoError(proVM.Shutdown(context.Background()))
  1845  	}()
  1846  
  1847  	// Initialize shouldn't be called again
  1848  	coreVM.InitializeF = nil
  1849  
  1850  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  1851  
  1852  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
  1853  
  1854  	xTestBlock := snowmantest.BuildChild(snowmantest.Genesis)
  1855  	xBlock := &TestOptionsBlock{
  1856  		Block: *xTestBlock,
  1857  		opts: [2]snowman.Block{
  1858  			snowmantest.BuildChild(xTestBlock),
  1859  			snowmantest.BuildChild(xTestBlock),
  1860  		},
  1861  	}
  1862  
  1863  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1864  		return xBlock, nil
  1865  	}
  1866  	aBlockIntf, err := proVM.BuildBlock(context.Background())
  1867  	require.NoError(err)
  1868  
  1869  	require.IsType(&postForkBlock{}, aBlockIntf)
  1870  	aBlock := aBlockIntf.(*postForkBlock)
  1871  
  1872  	opts, err := aBlock.Options(context.Background())
  1873  	require.NoError(err)
  1874  
  1875  	require.NoError(aBlock.Verify(context.Background()))
  1876  
  1877  	bBlock := opts[0]
  1878  	require.NoError(bBlock.Verify(context.Background()))
  1879  
  1880  	cBlock := opts[1]
  1881  	require.NoError(cBlock.Verify(context.Background()))
  1882  
  1883  	// accept A
  1884  	require.NoError(aBlock.Accept(context.Background()))
  1885  	coreHeights = append(coreHeights, xBlock.ID())
  1886  
  1887  	blkID, err := proVM.GetBlockIDAtHeight(context.Background(), aBlock.Height())
  1888  	require.NoError(err)
  1889  	require.Equal(aBlock.ID(), blkID)
  1890  
  1891  	// accept B
  1892  	require.NoError(bBlock.Accept(context.Background()))
  1893  	coreHeights = append(coreHeights, xBlock.opts[0].ID())
  1894  
  1895  	blkID, err = proVM.GetBlockIDAtHeight(context.Background(), bBlock.Height())
  1896  	require.NoError(err)
  1897  	require.Equal(bBlock.ID(), blkID)
  1898  
  1899  	// reject C
  1900  	require.NoError(cBlock.Reject(context.Background()))
  1901  
  1902  	blkID, err = proVM.GetBlockIDAtHeight(context.Background(), cBlock.Height())
  1903  	require.NoError(err)
  1904  	require.Equal(bBlock.ID(), blkID)
  1905  }
  1906  
  1907  func TestVMInnerBlkCache(t *testing.T) {
  1908  	require := require.New(t)
  1909  	ctrl := gomock.NewController(t)
  1910  
  1911  	// Create a VM
  1912  	innerVM := block.NewMockChainVM(ctrl)
  1913  	vm := New(
  1914  		innerVM,
  1915  		Config{
  1916  			ActivationTime:      time.Unix(0, 0),
  1917  			DurangoTime:         time.Unix(0, 0),
  1918  			MinimumPChainHeight: 0,
  1919  			MinBlkDelay:         DefaultMinBlockDelay,
  1920  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  1921  			StakingLeafSigner:   pTestSigner,
  1922  			StakingCertLeaf:     pTestCert,
  1923  			Registerer:          prometheus.NewRegistry(),
  1924  		},
  1925  	)
  1926  
  1927  	innerVM.EXPECT().Initialize(
  1928  		gomock.Any(),
  1929  		gomock.Any(),
  1930  		gomock.Any(),
  1931  		gomock.Any(),
  1932  		gomock.Any(),
  1933  		gomock.Any(),
  1934  		gomock.Any(),
  1935  		gomock.Any(),
  1936  		gomock.Any(),
  1937  	).Return(nil)
  1938  	innerVM.EXPECT().Shutdown(gomock.Any()).Return(nil)
  1939  
  1940  	{
  1941  		innerBlk := snowmantest.NewMockBlock(ctrl)
  1942  		innerBlkID := ids.GenerateTestID()
  1943  		innerVM.EXPECT().LastAccepted(gomock.Any()).Return(innerBlkID, nil)
  1944  		innerVM.EXPECT().GetBlock(gomock.Any(), innerBlkID).Return(innerBlk, nil)
  1945  	}
  1946  
  1947  	ctx := snowtest.Context(t, snowtest.CChainID)
  1948  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
  1949  
  1950  	require.NoError(vm.Initialize(
  1951  		context.Background(),
  1952  		ctx,
  1953  		prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly
  1954  		nil,
  1955  		nil,
  1956  		nil,
  1957  		nil,
  1958  		nil,
  1959  		nil,
  1960  	))
  1961  	defer func() {
  1962  		require.NoError(vm.Shutdown(context.Background()))
  1963  	}()
  1964  
  1965  	state := state.NewMockState(ctrl) // mock state
  1966  	vm.State = state
  1967  
  1968  	// Create a block near the tip (0).
  1969  	blkNearTipInnerBytes := []byte{1}
  1970  	blkNearTip, err := statelessblock.Build(
  1971  		ids.GenerateTestID(), // parent
  1972  		time.Time{},          // timestamp
  1973  		1,                    // pChainHeight,
  1974  		vm.StakingCertLeaf,   // cert
  1975  		blkNearTipInnerBytes, // inner blk bytes
  1976  		vm.ctx.ChainID,       // chain ID
  1977  		vm.StakingLeafSigner, // key
  1978  	)
  1979  	require.NoError(err)
  1980  
  1981  	// Parse a block.
  1982  	// Not in the VM's state so need to parse it.
  1983  	state.EXPECT().GetBlock(blkNearTip.ID()).Return(blkNearTip, choices.Accepted, nil).Times(2)
  1984  	// We will ask the inner VM to parse.
  1985  	mockInnerBlkNearTip := snowmantest.NewMockBlock(ctrl)
  1986  	mockInnerBlkNearTip.EXPECT().Height().Return(uint64(1)).Times(2)
  1987  	mockInnerBlkNearTip.EXPECT().Bytes().Return(blkNearTipInnerBytes).Times(1)
  1988  
  1989  	innerVM.EXPECT().ParseBlock(gomock.Any(), blkNearTipInnerBytes).Return(mockInnerBlkNearTip, nil).Times(2)
  1990  	_, err = vm.ParseBlock(context.Background(), blkNearTip.Bytes())
  1991  	require.NoError(err)
  1992  
  1993  	// Block should now be in cache because it's a post-fork block
  1994  	// and close to the tip.
  1995  	gotBlk, ok := vm.innerBlkCache.Get(blkNearTip.ID())
  1996  	require.True(ok)
  1997  	require.Equal(mockInnerBlkNearTip, gotBlk)
  1998  	require.Zero(vm.lastAcceptedHeight)
  1999  
  2000  	// Clear the cache
  2001  	vm.innerBlkCache.Flush()
  2002  
  2003  	// Advance the tip height
  2004  	vm.lastAcceptedHeight = innerBlkCacheSize + 1
  2005  
  2006  	// Parse the block again. This time it shouldn't be cached
  2007  	// because it's not close to the tip.
  2008  	_, err = vm.ParseBlock(context.Background(), blkNearTip.Bytes())
  2009  	require.NoError(err)
  2010  
  2011  	_, ok = vm.innerBlkCache.Get(blkNearTip.ID())
  2012  	require.False(ok)
  2013  }
  2014  
  2015  func TestVMInnerBlkCacheDeduplicationRegression(t *testing.T) {
  2016  	require := require.New(t)
  2017  	var (
  2018  		activationTime = time.Unix(0, 0)
  2019  		durangoTime    = activationTime
  2020  	)
  2021  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  2022  	defer func() {
  2023  		require.NoError(proVM.Shutdown(context.Background()))
  2024  	}()
  2025  
  2026  	// create pre-fork block X and post-fork block A
  2027  	xBlock := snowmantest.BuildChild(snowmantest.Genesis)
  2028  
  2029  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  2030  		return xBlock, nil
  2031  	}
  2032  	aBlock, err := proVM.BuildBlock(context.Background())
  2033  	require.NoError(err)
  2034  	coreVM.BuildBlockF = nil
  2035  
  2036  	bStatelessBlock, err := statelessblock.BuildUnsigned(
  2037  		snowmantest.GenesisID,
  2038  		snowmantest.GenesisTimestamp,
  2039  		defaultPChainHeight,
  2040  		xBlock.Bytes(),
  2041  	)
  2042  	require.NoError(err)
  2043  
  2044  	xBlockCopy := *xBlock
  2045  	coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) {
  2046  		return &xBlockCopy, nil
  2047  	}
  2048  
  2049  	bBlockBytes := bStatelessBlock.Bytes()
  2050  	bBlock, err := proVM.ParseBlock(context.Background(), bBlockBytes)
  2051  	require.NoError(err)
  2052  
  2053  	require.NoError(aBlock.Verify(context.Background()))
  2054  	require.NoError(bBlock.Verify(context.Background()))
  2055  	require.NoError(aBlock.Accept(context.Background()))
  2056  	require.NoError(bBlock.Reject(context.Background()))
  2057  
  2058  	require.Equal(
  2059  		choices.Accepted,
  2060  		aBlock.(*postForkBlock).innerBlk.Status(),
  2061  	)
  2062  
  2063  	require.Equal(
  2064  		choices.Accepted,
  2065  		bBlock.(*postForkBlock).innerBlk.Status(),
  2066  	)
  2067  
  2068  	cachedXBlock, ok := proVM.innerBlkCache.Get(bBlock.ID())
  2069  	require.True(ok)
  2070  	require.Equal(
  2071  		choices.Accepted,
  2072  		cachedXBlock.Status(),
  2073  	)
  2074  }
  2075  
  2076  func TestVMInnerBlkMarkedAcceptedRegression(t *testing.T) {
  2077  	require := require.New(t)
  2078  	var (
  2079  		activationTime = time.Unix(0, 0)
  2080  		durangoTime    = activationTime
  2081  	)
  2082  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  2083  	defer func() {
  2084  		require.NoError(proVM.Shutdown(context.Background()))
  2085  	}()
  2086  
  2087  	// create an inner block and wrap it in an postForkBlock.
  2088  	innerBlock := snowmantest.BuildChild(snowmantest.Genesis)
  2089  
  2090  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  2091  		return innerBlock, nil
  2092  	}
  2093  	outerBlock, err := proVM.BuildBlock(context.Background())
  2094  	require.NoError(err)
  2095  	coreVM.BuildBlockF = nil
  2096  
  2097  	require.NoError(outerBlock.Verify(context.Background()))
  2098  	require.NoError(outerBlock.Accept(context.Background()))
  2099  
  2100  	coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) {
  2101  		require.Equal(innerBlock.ID(), id)
  2102  		return innerBlock, nil
  2103  	}
  2104  
  2105  	wrappedInnerBlock, err := proVM.GetBlock(context.Background(), innerBlock.ID())
  2106  	require.NoError(err)
  2107  	require.Equal(choices.Rejected, wrappedInnerBlock.Status())
  2108  }
  2109  
  2110  type blockWithVerifyContext struct {
  2111  	*snowmantest.MockBlock
  2112  	*block.MockWithVerifyContext
  2113  }
  2114  
  2115  // Ensures that we call [VerifyWithContext] rather than [Verify] on blocks that
  2116  // implement [block.WithVerifyContext] and that returns true for
  2117  // [ShouldVerifyWithContext].
  2118  func TestVM_VerifyBlockWithContext(t *testing.T) {
  2119  	require := require.New(t)
  2120  	ctrl := gomock.NewController(t)
  2121  
  2122  	// Create a VM
  2123  	innerVM := block.NewMockChainVM(ctrl)
  2124  	vm := New(
  2125  		innerVM,
  2126  		Config{
  2127  			ActivationTime:      time.Unix(0, 0),
  2128  			DurangoTime:         time.Unix(0, 0),
  2129  			MinimumPChainHeight: 0,
  2130  			MinBlkDelay:         DefaultMinBlockDelay,
  2131  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  2132  			StakingLeafSigner:   pTestSigner,
  2133  			StakingCertLeaf:     pTestCert,
  2134  			Registerer:          prometheus.NewRegistry(),
  2135  		},
  2136  	)
  2137  
  2138  	// make sure that DBs are compressed correctly
  2139  	db := prefixdb.New([]byte{}, memdb.New())
  2140  
  2141  	innerVM.EXPECT().Initialize(
  2142  		gomock.Any(),
  2143  		gomock.Any(),
  2144  		gomock.Any(),
  2145  		gomock.Any(),
  2146  		gomock.Any(),
  2147  		gomock.Any(),
  2148  		gomock.Any(),
  2149  		gomock.Any(),
  2150  		gomock.Any(),
  2151  	).Return(nil)
  2152  	innerVM.EXPECT().Shutdown(gomock.Any()).Return(nil)
  2153  
  2154  	{
  2155  		innerBlk := snowmantest.NewMockBlock(ctrl)
  2156  		innerBlkID := ids.GenerateTestID()
  2157  		innerVM.EXPECT().LastAccepted(gomock.Any()).Return(innerBlkID, nil)
  2158  		innerVM.EXPECT().GetBlock(gomock.Any(), innerBlkID).Return(innerBlk, nil)
  2159  	}
  2160  
  2161  	snowCtx := snowtest.Context(t, snowtest.CChainID)
  2162  	snowCtx.NodeID = ids.NodeIDFromCert(pTestCert)
  2163  
  2164  	require.NoError(vm.Initialize(
  2165  		context.Background(),
  2166  		snowCtx,
  2167  		db,
  2168  		nil,
  2169  		nil,
  2170  		nil,
  2171  		nil,
  2172  		nil,
  2173  		nil,
  2174  	))
  2175  	defer func() {
  2176  		require.NoError(vm.Shutdown(context.Background()))
  2177  	}()
  2178  
  2179  	{
  2180  		pChainHeight := uint64(0)
  2181  		innerBlk := blockWithVerifyContext{
  2182  			MockBlock:             snowmantest.NewMockBlock(ctrl),
  2183  			MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl),
  2184  		}
  2185  		innerBlk.MockWithVerifyContext.EXPECT().ShouldVerifyWithContext(gomock.Any()).Return(true, nil).Times(2)
  2186  		innerBlk.MockWithVerifyContext.EXPECT().VerifyWithContext(context.Background(),
  2187  			&block.Context{
  2188  				PChainHeight: pChainHeight,
  2189  			},
  2190  		).Return(nil)
  2191  		innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes()
  2192  		innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes()
  2193  		innerBlk.MockBlock.EXPECT().Bytes().Return(utils.RandomBytes(1024)).AnyTimes()
  2194  
  2195  		blk := NewMockPostForkBlock(ctrl)
  2196  		blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes()
  2197  		blkID := ids.GenerateTestID()
  2198  		blk.EXPECT().ID().Return(blkID).AnyTimes()
  2199  
  2200  		require.NoError(vm.verifyAndRecordInnerBlk(
  2201  			context.Background(),
  2202  			&block.Context{
  2203  				PChainHeight: pChainHeight,
  2204  			},
  2205  			blk,
  2206  		))
  2207  
  2208  		// Call VerifyWithContext again but with a different P-Chain height
  2209  		blk.EXPECT().setInnerBlk(innerBlk).AnyTimes()
  2210  		pChainHeight++
  2211  		innerBlk.MockWithVerifyContext.EXPECT().VerifyWithContext(context.Background(),
  2212  			&block.Context{
  2213  				PChainHeight: pChainHeight,
  2214  			},
  2215  		).Return(nil)
  2216  
  2217  		require.NoError(vm.verifyAndRecordInnerBlk(
  2218  			context.Background(),
  2219  			&block.Context{
  2220  				PChainHeight: pChainHeight,
  2221  			},
  2222  			blk,
  2223  		))
  2224  	}
  2225  
  2226  	{
  2227  		// Ensure we call Verify on a block that returns
  2228  		// false for ShouldVerifyWithContext
  2229  		innerBlk := blockWithVerifyContext{
  2230  			MockBlock:             snowmantest.NewMockBlock(ctrl),
  2231  			MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl),
  2232  		}
  2233  		innerBlk.MockWithVerifyContext.EXPECT().ShouldVerifyWithContext(gomock.Any()).Return(false, nil)
  2234  		innerBlk.MockBlock.EXPECT().Verify(gomock.Any()).Return(nil)
  2235  		innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes()
  2236  		innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes()
  2237  		blk := NewMockPostForkBlock(ctrl)
  2238  		blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes()
  2239  		blkID := ids.GenerateTestID()
  2240  		blk.EXPECT().ID().Return(blkID).AnyTimes()
  2241  		require.NoError(vm.verifyAndRecordInnerBlk(
  2242  			context.Background(),
  2243  			&block.Context{
  2244  				PChainHeight: 1,
  2245  			},
  2246  			blk,
  2247  		))
  2248  	}
  2249  
  2250  	{
  2251  		// Ensure we call Verify on a block that doesn't have a valid context
  2252  		innerBlk := blockWithVerifyContext{
  2253  			MockBlock:             snowmantest.NewMockBlock(ctrl),
  2254  			MockWithVerifyContext: block.NewMockWithVerifyContext(ctrl),
  2255  		}
  2256  		innerBlk.MockBlock.EXPECT().Verify(gomock.Any()).Return(nil)
  2257  		innerBlk.MockBlock.EXPECT().Parent().Return(ids.GenerateTestID()).AnyTimes()
  2258  		innerBlk.MockBlock.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes()
  2259  		blk := NewMockPostForkBlock(ctrl)
  2260  		blk.EXPECT().getInnerBlk().Return(innerBlk).AnyTimes()
  2261  		blkID := ids.GenerateTestID()
  2262  		blk.EXPECT().ID().Return(blkID).AnyTimes()
  2263  		require.NoError(vm.verifyAndRecordInnerBlk(context.Background(), nil, blk))
  2264  	}
  2265  }
  2266  
  2267  func TestHistoricalBlockDeletion(t *testing.T) {
  2268  	require := require.New(t)
  2269  
  2270  	acceptedBlocks := []*snowmantest.Block{snowmantest.Genesis}
  2271  	currentHeight := uint64(0)
  2272  
  2273  	initialState := []byte("genesis state")
  2274  	coreVM := &block.TestVM{
  2275  		TestVM: common.TestVM{
  2276  			T: t,
  2277  			InitializeF: func(context.Context, *snow.Context, database.Database, []byte, []byte, []byte, chan<- common.Message, []*common.Fx, common.AppSender) error {
  2278  				return nil
  2279  			},
  2280  		},
  2281  		LastAcceptedF: func(context.Context) (ids.ID, error) {
  2282  			return acceptedBlocks[currentHeight].ID(), nil
  2283  		},
  2284  		GetBlockF: func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  2285  			for _, blk := range acceptedBlocks {
  2286  				if blkID == blk.ID() {
  2287  					return blk, nil
  2288  				}
  2289  			}
  2290  			return nil, errUnknownBlock
  2291  		},
  2292  		ParseBlockF: func(_ context.Context, b []byte) (snowman.Block, error) {
  2293  			for _, blk := range acceptedBlocks {
  2294  				if bytes.Equal(b, blk.Bytes()) {
  2295  					return blk, nil
  2296  				}
  2297  			}
  2298  			return nil, errUnknownBlock
  2299  		},
  2300  		GetBlockIDAtHeightF: func(_ context.Context, height uint64) (ids.ID, error) {
  2301  			if height >= uint64(len(acceptedBlocks)) {
  2302  				return ids.Empty, errTooHigh
  2303  			}
  2304  			return acceptedBlocks[height].ID(), nil
  2305  		},
  2306  	}
  2307  
  2308  	ctx := snowtest.Context(t, snowtest.CChainID)
  2309  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
  2310  	ctx.ValidatorState = &validators.TestState{
  2311  		T: t,
  2312  		GetMinimumHeightF: func(context.Context) (uint64, error) {
  2313  			return snowmantest.GenesisHeight, nil
  2314  		},
  2315  		GetCurrentHeightF: func(context.Context) (uint64, error) {
  2316  			return defaultPChainHeight, nil
  2317  		},
  2318  		GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
  2319  			return nil, nil
  2320  		},
  2321  	}
  2322  
  2323  	// make sure that DBs are compressed correctly
  2324  	db := prefixdb.New([]byte{}, memdb.New())
  2325  
  2326  	proVM := New(
  2327  		coreVM,
  2328  		Config{
  2329  			ActivationTime:      time.Unix(0, 0),
  2330  			DurangoTime:         mockable.MaxTime,
  2331  			MinimumPChainHeight: 0,
  2332  			MinBlkDelay:         DefaultMinBlockDelay,
  2333  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
  2334  			StakingLeafSigner:   pTestSigner,
  2335  			StakingCertLeaf:     pTestCert,
  2336  			Registerer:          prometheus.NewRegistry(),
  2337  		},
  2338  	)
  2339  
  2340  	require.NoError(proVM.Initialize(
  2341  		context.Background(),
  2342  		ctx,
  2343  		db,
  2344  		initialState,
  2345  		nil,
  2346  		nil,
  2347  		nil,
  2348  		nil,
  2349  		nil,
  2350  	))
  2351  
  2352  	lastAcceptedID, err := proVM.LastAccepted(context.Background())
  2353  	require.NoError(err)
  2354  
  2355  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  2356  	require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID))
  2357  
  2358  	issueBlock := func() {
  2359  		lastAcceptedBlock := acceptedBlocks[currentHeight]
  2360  		innerBlock := snowmantest.BuildChild(lastAcceptedBlock)
  2361  
  2362  		coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  2363  			return innerBlock, nil
  2364  		}
  2365  		proBlock, err := proVM.BuildBlock(context.Background())
  2366  		require.NoError(err)
  2367  
  2368  		require.NoError(proBlock.Verify(context.Background()))
  2369  		require.NoError(proVM.SetPreference(context.Background(), proBlock.ID()))
  2370  		require.NoError(proBlock.Accept(context.Background()))
  2371  
  2372  		acceptedBlocks = append(acceptedBlocks, innerBlock)
  2373  		currentHeight++
  2374  	}
  2375  
  2376  	requireHeights := func(start, end uint64) {
  2377  		for i := start; i <= end; i++ {
  2378  			_, err := proVM.GetBlockIDAtHeight(context.Background(), i)
  2379  			require.NoError(err)
  2380  		}
  2381  	}
  2382  
  2383  	requireMissingHeights := func(start, end uint64) {
  2384  		for i := start; i <= end; i++ {
  2385  			_, err := proVM.GetBlockIDAtHeight(context.Background(), i)
  2386  			require.ErrorIs(err, database.ErrNotFound)
  2387  		}
  2388  	}
  2389  
  2390  	requireNumHeights := func(numIndexed uint64) {
  2391  		requireHeights(0, 0)
  2392  		requireMissingHeights(1, currentHeight-numIndexed-1)
  2393  		requireHeights(currentHeight-numIndexed, currentHeight)
  2394  	}
  2395  
  2396  	// Because block pruning is disabled by default, the heights should be
  2397  	// populated for every accepted block.
  2398  	requireHeights(0, currentHeight)
  2399  
  2400  	issueBlock()
  2401  	requireHeights(0, currentHeight)
  2402  
  2403  	issueBlock()
  2404  	requireHeights(0, currentHeight)
  2405  
  2406  	issueBlock()
  2407  	requireHeights(0, currentHeight)
  2408  
  2409  	issueBlock()
  2410  	requireHeights(0, currentHeight)
  2411  
  2412  	issueBlock()
  2413  	requireHeights(0, currentHeight)
  2414  
  2415  	require.NoError(proVM.Shutdown(context.Background()))
  2416  
  2417  	numHistoricalBlocks := uint64(2)
  2418  	proVM = New(
  2419  		coreVM,
  2420  		Config{
  2421  			ActivationTime:      time.Time{},
  2422  			DurangoTime:         mockable.MaxTime,
  2423  			MinimumPChainHeight: 0,
  2424  			MinBlkDelay:         DefaultMinBlockDelay,
  2425  			NumHistoricalBlocks: numHistoricalBlocks,
  2426  			StakingLeafSigner:   pTestSigner,
  2427  			StakingCertLeaf:     pTestCert,
  2428  			Registerer:          prometheus.NewRegistry(),
  2429  		},
  2430  	)
  2431  
  2432  	require.NoError(proVM.Initialize(
  2433  		context.Background(),
  2434  		ctx,
  2435  		db,
  2436  		initialState,
  2437  		nil,
  2438  		nil,
  2439  		nil,
  2440  		nil,
  2441  		nil,
  2442  	))
  2443  
  2444  	lastAcceptedID, err = proVM.LastAccepted(context.Background())
  2445  	require.NoError(err)
  2446  
  2447  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  2448  	require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID))
  2449  
  2450  	// Verify that old blocks were pruned during startup
  2451  	requireNumHeights(numHistoricalBlocks)
  2452  
  2453  	// As we issue new blocks, the oldest indexed height should be pruned.
  2454  	issueBlock()
  2455  	requireNumHeights(numHistoricalBlocks)
  2456  
  2457  	issueBlock()
  2458  	requireNumHeights(numHistoricalBlocks)
  2459  
  2460  	require.NoError(proVM.Shutdown(context.Background()))
  2461  
  2462  	newNumHistoricalBlocks := numHistoricalBlocks + 2
  2463  	proVM = New(
  2464  		coreVM,
  2465  		Config{
  2466  			ActivationTime:      time.Time{},
  2467  			DurangoTime:         mockable.MaxTime,
  2468  			MinimumPChainHeight: 0,
  2469  			MinBlkDelay:         DefaultMinBlockDelay,
  2470  			NumHistoricalBlocks: newNumHistoricalBlocks,
  2471  			StakingLeafSigner:   pTestSigner,
  2472  			StakingCertLeaf:     pTestCert,
  2473  			Registerer:          prometheus.NewRegistry(),
  2474  		},
  2475  	)
  2476  
  2477  	require.NoError(proVM.Initialize(
  2478  		context.Background(),
  2479  		ctx,
  2480  		db,
  2481  		initialState,
  2482  		nil,
  2483  		nil,
  2484  		nil,
  2485  		nil,
  2486  		nil,
  2487  	))
  2488  	defer func() {
  2489  		require.NoError(proVM.Shutdown(context.Background()))
  2490  	}()
  2491  
  2492  	lastAcceptedID, err = proVM.LastAccepted(context.Background())
  2493  	require.NoError(err)
  2494  
  2495  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
  2496  	require.NoError(proVM.SetPreference(context.Background(), lastAcceptedID))
  2497  
  2498  	// The height index shouldn't be modified at this point
  2499  	requireNumHeights(numHistoricalBlocks)
  2500  
  2501  	// As we issue new blocks, the number of indexed blocks should increase
  2502  	// until we hit our target again.
  2503  	issueBlock()
  2504  	requireNumHeights(numHistoricalBlocks + 1)
  2505  
  2506  	issueBlock()
  2507  	requireNumHeights(newNumHistoricalBlocks)
  2508  
  2509  	issueBlock()
  2510  	requireNumHeights(newNumHistoricalBlocks)
  2511  }