github.com/ava-labs/avalanchego@v1.11.11/vms/proposervm/pre_fork_block_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  	"testing"
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"go.uber.org/mock/gomock"
    14  
    15  	"github.com/ava-labs/avalanchego/database"
    16  	"github.com/ava-labs/avalanchego/ids"
    17  	"github.com/ava-labs/avalanchego/snow"
    18  	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
    19  	"github.com/ava-labs/avalanchego/snow/consensus/snowman/snowmanmock"
    20  	"github.com/ava-labs/avalanchego/snow/consensus/snowman/snowmantest"
    21  	"github.com/ava-labs/avalanchego/snow/engine/snowman/block/blockmock"
    22  	"github.com/ava-labs/avalanchego/snow/snowtest"
    23  	"github.com/ava-labs/avalanchego/snow/validators/validatorsmock"
    24  	"github.com/ava-labs/avalanchego/utils/logging"
    25  	"github.com/ava-labs/avalanchego/utils/timer/mockable"
    26  
    27  	statelessblock "github.com/ava-labs/avalanchego/vms/proposervm/block"
    28  )
    29  
    30  func TestOracle_PreForkBlkImplementsInterface(t *testing.T) {
    31  	require := require.New(t)
    32  
    33  	// setup
    34  	proBlk := preForkBlock{
    35  		Block: snowmantest.BuildChild(snowmantest.Genesis),
    36  	}
    37  
    38  	// test
    39  	_, err := proBlk.Options(context.Background())
    40  	require.Equal(snowman.ErrNotOracle, err)
    41  
    42  	// setup
    43  	proBlk = preForkBlock{
    44  		Block: &TestOptionsBlock{},
    45  	}
    46  
    47  	// test
    48  	_, err = proBlk.Options(context.Background())
    49  	require.NoError(err)
    50  }
    51  
    52  func TestOracle_PreForkBlkCanBuiltOnPreForkOption(t *testing.T) {
    53  	require := require.New(t)
    54  
    55  	var (
    56  		activationTime = mockable.MaxTime
    57  		durangoTime    = activationTime
    58  	)
    59  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    60  	defer func() {
    61  		require.NoError(proVM.Shutdown(context.Background()))
    62  	}()
    63  
    64  	// create pre fork oracle block ...
    65  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
    66  	preferredTestBlk := snowmantest.BuildChild(coreTestBlk)
    67  	oracleCoreBlk := &TestOptionsBlock{
    68  		Block: *coreTestBlk,
    69  		opts: [2]*snowmantest.Block{
    70  			preferredTestBlk,
    71  			snowmantest.BuildChild(coreTestBlk),
    72  		},
    73  	}
    74  
    75  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
    76  		return oracleCoreBlk, nil
    77  	}
    78  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
    79  		switch blkID {
    80  		case snowmantest.GenesisID:
    81  			return snowmantest.Genesis, nil
    82  		case oracleCoreBlk.ID():
    83  			return oracleCoreBlk, nil
    84  		case oracleCoreBlk.opts[0].ID():
    85  			return oracleCoreBlk.opts[0], nil
    86  		case oracleCoreBlk.opts[1].ID():
    87  			return oracleCoreBlk.opts[1], nil
    88  		default:
    89  			return nil, database.ErrNotFound
    90  		}
    91  	}
    92  
    93  	parentBlk, err := proVM.BuildBlock(context.Background())
    94  	require.NoError(err)
    95  
    96  	// retrieve options ...
    97  	require.IsType(&preForkBlock{}, parentBlk)
    98  	preForkOracleBlk := parentBlk.(*preForkBlock)
    99  	opts, err := preForkOracleBlk.Options(context.Background())
   100  	require.NoError(err)
   101  	require.NoError(opts[0].Verify(context.Background()))
   102  
   103  	// ... show a block can be built on top of an option
   104  	require.NoError(proVM.SetPreference(context.Background(), opts[0].ID()))
   105  
   106  	lastCoreBlk := &TestOptionsBlock{
   107  		Block: *snowmantest.BuildChild(preferredTestBlk),
   108  	}
   109  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   110  		return lastCoreBlk, nil
   111  	}
   112  
   113  	preForkChild, err := proVM.BuildBlock(context.Background())
   114  	require.NoError(err)
   115  	require.IsType(&preForkBlock{}, preForkChild)
   116  }
   117  
   118  func TestOracle_PostForkBlkCanBuiltOnPreForkOption(t *testing.T) {
   119  	require := require.New(t)
   120  
   121  	var (
   122  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   123  		durangoTime    = activationTime
   124  	)
   125  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   126  	defer func() {
   127  		require.NoError(proVM.Shutdown(context.Background()))
   128  	}()
   129  
   130  	// create pre fork oracle block pre activation time...
   131  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   132  	coreTestBlk.TimestampV = activationTime.Add(-1 * time.Second)
   133  
   134  	// ... whose options are post activation time
   135  	preferredBlk := snowmantest.BuildChild(coreTestBlk)
   136  	preferredBlk.TimestampV = activationTime.Add(time.Second)
   137  
   138  	unpreferredBlk := snowmantest.BuildChild(coreTestBlk)
   139  	unpreferredBlk.TimestampV = activationTime.Add(time.Second)
   140  
   141  	oracleCoreBlk := &TestOptionsBlock{
   142  		Block: *coreTestBlk,
   143  		opts: [2]*snowmantest.Block{
   144  			preferredBlk,
   145  			unpreferredBlk,
   146  		},
   147  	}
   148  
   149  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   150  		return oracleCoreBlk, nil
   151  	}
   152  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   153  		switch blkID {
   154  		case snowmantest.GenesisID:
   155  			return snowmantest.Genesis, nil
   156  		case oracleCoreBlk.ID():
   157  			return oracleCoreBlk, nil
   158  		case oracleCoreBlk.opts[0].ID():
   159  			return oracleCoreBlk.opts[0], nil
   160  		case oracleCoreBlk.opts[1].ID():
   161  			return oracleCoreBlk.opts[1], nil
   162  		default:
   163  			return nil, database.ErrNotFound
   164  		}
   165  	}
   166  
   167  	parentBlk, err := proVM.BuildBlock(context.Background())
   168  	require.NoError(err)
   169  
   170  	// retrieve options ...
   171  	require.IsType(&preForkBlock{}, parentBlk)
   172  	preForkOracleBlk := parentBlk.(*preForkBlock)
   173  	opts, err := preForkOracleBlk.Options(context.Background())
   174  	require.NoError(err)
   175  	require.NoError(opts[0].Verify(context.Background()))
   176  
   177  	// ... show a block can be built on top of an option
   178  	require.NoError(proVM.SetPreference(context.Background(), opts[0].ID()))
   179  
   180  	lastCoreBlk := &TestOptionsBlock{
   181  		Block: *snowmantest.BuildChild(preferredBlk),
   182  	}
   183  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   184  		return lastCoreBlk, nil
   185  	}
   186  
   187  	postForkChild, err := proVM.BuildBlock(context.Background())
   188  	require.NoError(err)
   189  	require.IsType(&postForkBlock{}, postForkChild)
   190  }
   191  
   192  func TestBlockVerify_PreFork_ParentChecks(t *testing.T) {
   193  	require := require.New(t)
   194  
   195  	var (
   196  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   197  		durangoTime    = activationTime
   198  	)
   199  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   200  	defer func() {
   201  		require.NoError(proVM.Shutdown(context.Background()))
   202  	}()
   203  
   204  	// create parent block ...
   205  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   206  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   207  		return parentCoreBlk, nil
   208  	}
   209  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   210  		switch blkID {
   211  		case snowmantest.GenesisID:
   212  			return snowmantest.Genesis, nil
   213  		case parentCoreBlk.ID():
   214  			return parentCoreBlk, nil
   215  		default:
   216  			return nil, database.ErrNotFound
   217  		}
   218  	}
   219  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   220  		switch {
   221  		case bytes.Equal(b, snowmantest.GenesisBytes):
   222  			return snowmantest.Genesis, nil
   223  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   224  			return parentCoreBlk, nil
   225  		default:
   226  			return nil, database.ErrNotFound
   227  		}
   228  	}
   229  
   230  	parentBlk, err := proVM.BuildBlock(context.Background())
   231  	require.NoError(err)
   232  
   233  	// .. create child block ...
   234  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   235  	childBlk := preForkBlock{
   236  		Block: childCoreBlk,
   237  		vm:    proVM,
   238  	}
   239  
   240  	{
   241  		// child block referring unknown parent does not verify
   242  		childCoreBlk.ParentV = ids.Empty
   243  		err = childBlk.Verify(context.Background())
   244  		require.ErrorIs(err, database.ErrNotFound)
   245  	}
   246  
   247  	{
   248  		// child block referring known parent does verify
   249  		childCoreBlk.ParentV = parentBlk.ID()
   250  		require.NoError(childBlk.Verify(context.Background()))
   251  	}
   252  }
   253  
   254  func TestBlockVerify_BlocksBuiltOnPreForkGenesis(t *testing.T) {
   255  	require := require.New(t)
   256  
   257  	var (
   258  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   259  		durangoTime    = activationTime
   260  	)
   261  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   262  	defer func() {
   263  		require.NoError(proVM.Shutdown(context.Background()))
   264  	}()
   265  
   266  	preActivationTime := activationTime.Add(-1 * time.Second)
   267  	proVM.Set(preActivationTime)
   268  
   269  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   270  	coreBlk.TimestampV = preActivationTime
   271  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   272  		return coreBlk, nil
   273  	}
   274  
   275  	// preFork block verifies if parent is before fork activation time
   276  	preForkChild, err := proVM.BuildBlock(context.Background())
   277  	require.NoError(err)
   278  	require.IsType(&preForkBlock{}, preForkChild)
   279  
   280  	require.NoError(preForkChild.Verify(context.Background()))
   281  
   282  	// postFork block does NOT verify if parent is before fork activation time
   283  	postForkStatelessChild, err := statelessblock.Build(
   284  		snowmantest.GenesisID,
   285  		coreBlk.Timestamp(),
   286  		0, // pChainHeight
   287  		proVM.StakingCertLeaf,
   288  		coreBlk.Bytes(),
   289  		proVM.ctx.ChainID,
   290  		proVM.StakingLeafSigner,
   291  	)
   292  	require.NoError(err)
   293  	postForkChild := &postForkBlock{
   294  		SignedBlock: postForkStatelessChild,
   295  		postForkCommonComponents: postForkCommonComponents{
   296  			vm:       proVM,
   297  			innerBlk: coreBlk,
   298  		},
   299  	}
   300  
   301  	require.True(postForkChild.Timestamp().Before(activationTime))
   302  	err = postForkChild.Verify(context.Background())
   303  	require.ErrorIs(err, errProposersNotActivated)
   304  
   305  	// once activation time is crossed postForkBlock are produced
   306  	postActivationTime := activationTime.Add(time.Second)
   307  	proVM.Set(postActivationTime)
   308  
   309  	coreVM.SetPreferenceF = func(context.Context, ids.ID) error {
   310  		return nil
   311  	}
   312  	require.NoError(proVM.SetPreference(context.Background(), preForkChild.ID()))
   313  
   314  	secondCoreBlk := snowmantest.BuildChild(coreBlk)
   315  	secondCoreBlk.TimestampV = postActivationTime
   316  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   317  		return secondCoreBlk, nil
   318  	}
   319  	coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) {
   320  		switch id {
   321  		case snowmantest.GenesisID:
   322  			return snowmantest.Genesis, nil
   323  		case coreBlk.ID():
   324  			return coreBlk, nil
   325  		default:
   326  			require.FailNow("attempt to get unknown block")
   327  			return nil, nil
   328  		}
   329  	}
   330  
   331  	lastPreForkBlk, err := proVM.BuildBlock(context.Background())
   332  	require.NoError(err)
   333  	require.IsType(&preForkBlock{}, lastPreForkBlk)
   334  
   335  	require.NoError(lastPreForkBlk.Verify(context.Background()))
   336  
   337  	require.NoError(proVM.SetPreference(context.Background(), lastPreForkBlk.ID()))
   338  	thirdCoreBlk := snowmantest.BuildChild(secondCoreBlk)
   339  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   340  		return thirdCoreBlk, nil
   341  	}
   342  	coreVM.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) {
   343  		switch id {
   344  		case snowmantest.GenesisID:
   345  			return snowmantest.Genesis, nil
   346  		case coreBlk.ID():
   347  			return coreBlk, nil
   348  		case secondCoreBlk.ID():
   349  			return secondCoreBlk, nil
   350  		default:
   351  			require.FailNow("attempt to get unknown block")
   352  			return nil, nil
   353  		}
   354  	}
   355  
   356  	firstPostForkBlk, err := proVM.BuildBlock(context.Background())
   357  	require.NoError(err)
   358  	require.IsType(&postForkBlock{}, firstPostForkBlk)
   359  
   360  	require.NoError(firstPostForkBlk.Verify(context.Background()))
   361  }
   362  
   363  func TestBlockVerify_BlocksBuiltOnPostForkGenesis(t *testing.T) {
   364  	require := require.New(t)
   365  
   366  	var (
   367  		activationTime = snowmantest.GenesisTimestamp.Add(-1 * time.Second)
   368  		durangoTime    = activationTime
   369  	)
   370  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   371  	proVM.Set(activationTime)
   372  	defer func() {
   373  		require.NoError(proVM.Shutdown(context.Background()))
   374  	}()
   375  
   376  	// build parent block after fork activation time ...
   377  	coreBlock := snowmantest.BuildChild(snowmantest.Genesis)
   378  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   379  		return coreBlock, nil
   380  	}
   381  
   382  	// postFork block verifies if parent is after fork activation time
   383  	postForkChild, err := proVM.BuildBlock(context.Background())
   384  	require.NoError(err)
   385  	require.IsType(&postForkBlock{}, postForkChild)
   386  
   387  	require.NoError(postForkChild.Verify(context.Background()))
   388  
   389  	// preFork block does NOT verify if parent is after fork activation time
   390  	preForkChild := preForkBlock{
   391  		Block: coreBlock,
   392  		vm:    proVM,
   393  	}
   394  	err = preForkChild.Verify(context.Background())
   395  	require.ErrorIs(err, errUnexpectedBlockType)
   396  }
   397  
   398  func TestBlockAccept_PreFork_SetsLastAcceptedBlock(t *testing.T) {
   399  	require := require.New(t)
   400  
   401  	// setup
   402  	var (
   403  		activationTime = mockable.MaxTime
   404  		durangoTime    = activationTime
   405  	)
   406  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   407  	defer func() {
   408  		require.NoError(proVM.Shutdown(context.Background()))
   409  	}()
   410  
   411  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   412  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   413  		return coreBlk, nil
   414  	}
   415  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   416  		switch blkID {
   417  		case snowmantest.GenesisID:
   418  			return snowmantest.Genesis, nil
   419  		case coreBlk.ID():
   420  			return coreBlk, nil
   421  		default:
   422  			return nil, database.ErrNotFound
   423  		}
   424  	}
   425  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   426  		switch {
   427  		case bytes.Equal(b, snowmantest.GenesisBytes):
   428  			return snowmantest.Genesis, nil
   429  		case bytes.Equal(b, coreBlk.Bytes()):
   430  			return coreBlk, nil
   431  		default:
   432  			return nil, errUnknownBlock
   433  		}
   434  	}
   435  
   436  	builtBlk, err := proVM.BuildBlock(context.Background())
   437  	require.NoError(err)
   438  
   439  	// test
   440  	require.NoError(builtBlk.Accept(context.Background()))
   441  
   442  	coreVM.LastAcceptedF = snowmantest.MakeLastAcceptedBlockF(
   443  		[]*snowmantest.Block{
   444  			snowmantest.Genesis,
   445  			coreBlk,
   446  		},
   447  	)
   448  	acceptedID, err := proVM.LastAccepted(context.Background())
   449  	require.NoError(err)
   450  	require.Equal(builtBlk.ID(), acceptedID)
   451  }
   452  
   453  // ProposerBlock.Reject tests section
   454  func TestBlockReject_PreForkBlock_InnerBlockIsRejected(t *testing.T) {
   455  	require := require.New(t)
   456  
   457  	var (
   458  		activationTime = mockable.MaxTime
   459  		durangoTime    = activationTime
   460  	)
   461  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   462  	defer func() {
   463  		require.NoError(proVM.Shutdown(context.Background()))
   464  	}()
   465  
   466  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   467  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   468  		return coreBlk, nil
   469  	}
   470  
   471  	sb, err := proVM.BuildBlock(context.Background())
   472  	require.NoError(err)
   473  	require.IsType(&preForkBlock{}, sb)
   474  	proBlk := sb.(*preForkBlock)
   475  
   476  	require.NoError(proBlk.Reject(context.Background()))
   477  	require.Equal(snowtest.Rejected, coreBlk.Status)
   478  }
   479  
   480  func TestBlockVerify_ForkBlockIsOracleBlock(t *testing.T) {
   481  	require := require.New(t)
   482  
   483  	var (
   484  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   485  		durangoTime    = activationTime
   486  	)
   487  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   488  	defer func() {
   489  		require.NoError(proVM.Shutdown(context.Background()))
   490  	}()
   491  
   492  	postActivationTime := activationTime.Add(time.Second)
   493  	proVM.Set(postActivationTime)
   494  
   495  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   496  	coreTestBlk.TimestampV = postActivationTime
   497  	coreBlk := &TestOptionsBlock{
   498  		Block: *coreTestBlk,
   499  		opts: [2]*snowmantest.Block{
   500  			snowmantest.BuildChild(coreTestBlk),
   501  			snowmantest.BuildChild(coreTestBlk),
   502  		},
   503  	}
   504  
   505  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   506  		switch blkID {
   507  		case snowmantest.GenesisID:
   508  			return snowmantest.Genesis, nil
   509  		case coreBlk.ID():
   510  			return coreBlk, nil
   511  		case coreBlk.opts[0].ID():
   512  			return coreBlk.opts[0], nil
   513  		case coreBlk.opts[1].ID():
   514  			return coreBlk.opts[1], nil
   515  		default:
   516  			return nil, database.ErrNotFound
   517  		}
   518  	}
   519  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   520  		switch {
   521  		case bytes.Equal(b, snowmantest.GenesisBytes):
   522  			return snowmantest.Genesis, nil
   523  		case bytes.Equal(b, coreBlk.Bytes()):
   524  			return coreBlk, nil
   525  		case bytes.Equal(b, coreBlk.opts[0].Bytes()):
   526  			return coreBlk.opts[0], nil
   527  		case bytes.Equal(b, coreBlk.opts[1].Bytes()):
   528  			return coreBlk.opts[1], nil
   529  		default:
   530  			return nil, errUnknownBlock
   531  		}
   532  	}
   533  
   534  	firstBlock, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes())
   535  	require.NoError(err)
   536  
   537  	require.NoError(firstBlock.Verify(context.Background()))
   538  
   539  	oracleBlock, ok := firstBlock.(snowman.OracleBlock)
   540  	require.True(ok)
   541  
   542  	options, err := oracleBlock.Options(context.Background())
   543  	require.NoError(err)
   544  
   545  	require.NoError(options[0].Verify(context.Background()))
   546  
   547  	require.NoError(options[1].Verify(context.Background()))
   548  }
   549  
   550  func TestBlockVerify_ForkBlockIsOracleBlockButChildrenAreSigned(t *testing.T) {
   551  	require := require.New(t)
   552  
   553  	var (
   554  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   555  		durangoTime    = activationTime
   556  	)
   557  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   558  	defer func() {
   559  		require.NoError(proVM.Shutdown(context.Background()))
   560  	}()
   561  
   562  	postActivationTime := activationTime.Add(time.Second)
   563  	proVM.Set(postActivationTime)
   564  
   565  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   566  	coreTestBlk.TimestampV = postActivationTime
   567  	coreBlk := &TestOptionsBlock{
   568  		Block: *coreTestBlk,
   569  		opts: [2]*snowmantest.Block{
   570  			snowmantest.BuildChild(coreTestBlk),
   571  			snowmantest.BuildChild(coreTestBlk),
   572  		},
   573  	}
   574  
   575  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   576  		switch blkID {
   577  		case snowmantest.GenesisID:
   578  			return snowmantest.Genesis, nil
   579  		case coreBlk.ID():
   580  			return coreBlk, nil
   581  		case coreBlk.opts[0].ID():
   582  			return coreBlk.opts[0], nil
   583  		case coreBlk.opts[1].ID():
   584  			return coreBlk.opts[1], nil
   585  		default:
   586  			return nil, database.ErrNotFound
   587  		}
   588  	}
   589  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   590  		switch {
   591  		case bytes.Equal(b, snowmantest.GenesisBytes):
   592  			return snowmantest.Genesis, nil
   593  		case bytes.Equal(b, coreBlk.Bytes()):
   594  			return coreBlk, nil
   595  		case bytes.Equal(b, coreBlk.opts[0].Bytes()):
   596  			return coreBlk.opts[0], nil
   597  		case bytes.Equal(b, coreBlk.opts[1].Bytes()):
   598  			return coreBlk.opts[1], nil
   599  		default:
   600  			return nil, errUnknownBlock
   601  		}
   602  	}
   603  
   604  	firstBlock, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes())
   605  	require.NoError(err)
   606  
   607  	require.NoError(firstBlock.Verify(context.Background()))
   608  
   609  	slb, err := statelessblock.Build(
   610  		firstBlock.ID(), // refer unknown parent
   611  		firstBlock.Timestamp(),
   612  		0, // pChainHeight,
   613  		proVM.StakingCertLeaf,
   614  		coreBlk.opts[0].Bytes(),
   615  		proVM.ctx.ChainID,
   616  		proVM.StakingLeafSigner,
   617  	)
   618  	require.NoError(err)
   619  
   620  	invalidChild, err := proVM.ParseBlock(context.Background(), slb.Bytes())
   621  	if err != nil {
   622  		// A failure to parse is okay here
   623  		return
   624  	}
   625  
   626  	err = invalidChild.Verify(context.Background())
   627  	require.ErrorIs(err, errUnexpectedBlockType)
   628  }
   629  
   630  // Assert that when the underlying VM implements ChainVMWithBuildBlockContext
   631  // and the proposervm is activated, we only call the VM's BuildBlockWithContext
   632  // when a P-chain height can be correctly provided from the parent block.
   633  func TestPreForkBlock_BuildBlockWithContext(t *testing.T) {
   634  	require := require.New(t)
   635  	ctrl := gomock.NewController(t)
   636  
   637  	pChainHeight := uint64(1337)
   638  	blkID := ids.GenerateTestID()
   639  	innerBlk := snowmanmock.NewBlock(ctrl)
   640  	innerBlk.EXPECT().ID().Return(blkID).AnyTimes()
   641  	innerBlk.EXPECT().Timestamp().Return(mockable.MaxTime)
   642  	builtBlk := snowmanmock.NewBlock(ctrl)
   643  	builtBlk.EXPECT().Bytes().Return([]byte{1, 2, 3}).AnyTimes()
   644  	builtBlk.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes()
   645  	builtBlk.EXPECT().Height().Return(pChainHeight).AnyTimes()
   646  	innerVM := blockmock.NewChainVM(ctrl)
   647  	innerVM.EXPECT().BuildBlock(gomock.Any()).Return(builtBlk, nil).AnyTimes()
   648  	vdrState := validatorsmock.NewState(ctrl)
   649  	vdrState.EXPECT().GetMinimumHeight(context.Background()).Return(pChainHeight, nil).AnyTimes()
   650  
   651  	vm := &VM{
   652  		ChainVM: innerVM,
   653  		ctx: &snow.Context{
   654  			ValidatorState: vdrState,
   655  			Log:            logging.NoLog{},
   656  		},
   657  	}
   658  
   659  	blk := &preForkBlock{
   660  		Block: innerBlk,
   661  		vm:    vm,
   662  	}
   663  
   664  	// Should call BuildBlock since proposervm won't have a P-chain height
   665  	gotChild, err := blk.buildChild(context.Background())
   666  	require.NoError(err)
   667  	require.Equal(builtBlk, gotChild.(*postForkBlock).innerBlk)
   668  
   669  	// Should call BuildBlock since proposervm is not activated
   670  	innerBlk.EXPECT().Timestamp().Return(time.Time{})
   671  	vm.Upgrades.ApricotPhase4Time = mockable.MaxTime
   672  
   673  	gotChild, err = blk.buildChild(context.Background())
   674  	require.NoError(err)
   675  	require.Equal(builtBlk, gotChild.(*preForkBlock).Block)
   676  }