github.com/MetalBlockchain/metalgo@v1.11.9/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/MetalBlockchain/metalgo/database"
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/snow"
    18  	"github.com/MetalBlockchain/metalgo/snow/choices"
    19  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    20  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest"
    21  	"github.com/MetalBlockchain/metalgo/snow/engine/snowman/block"
    22  	"github.com/MetalBlockchain/metalgo/snow/validators"
    23  	"github.com/MetalBlockchain/metalgo/utils/logging"
    24  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    25  
    26  	statelessblock "github.com/MetalBlockchain/metalgo/vms/proposervm/block"
    27  )
    28  
    29  func TestOracle_PreForkBlkImplementsInterface(t *testing.T) {
    30  	require := require.New(t)
    31  
    32  	// setup
    33  	proBlk := preForkBlock{
    34  		Block: snowmantest.BuildChild(snowmantest.Genesis),
    35  	}
    36  
    37  	// test
    38  	_, err := proBlk.Options(context.Background())
    39  	require.Equal(snowman.ErrNotOracle, err)
    40  
    41  	// setup
    42  	proBlk = preForkBlock{
    43  		Block: &TestOptionsBlock{},
    44  	}
    45  
    46  	// test
    47  	_, err = proBlk.Options(context.Background())
    48  	require.NoError(err)
    49  }
    50  
    51  func TestOracle_PreForkBlkCanBuiltOnPreForkOption(t *testing.T) {
    52  	require := require.New(t)
    53  
    54  	var (
    55  		activationTime = mockable.MaxTime
    56  		durangoTime    = activationTime
    57  	)
    58  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    59  	defer func() {
    60  		require.NoError(proVM.Shutdown(context.Background()))
    61  	}()
    62  
    63  	// create pre fork oracle block ...
    64  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
    65  	preferredTestBlk := snowmantest.BuildChild(coreTestBlk)
    66  	oracleCoreBlk := &TestOptionsBlock{
    67  		Block: *coreTestBlk,
    68  		opts: [2]snowman.Block{
    69  			preferredTestBlk,
    70  			snowmantest.BuildChild(coreTestBlk),
    71  		},
    72  	}
    73  
    74  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
    75  		return oracleCoreBlk, nil
    76  	}
    77  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
    78  		switch blkID {
    79  		case snowmantest.GenesisID:
    80  			return snowmantest.Genesis, nil
    81  		case oracleCoreBlk.ID():
    82  			return oracleCoreBlk, nil
    83  		case oracleCoreBlk.opts[0].ID():
    84  			return oracleCoreBlk.opts[0], nil
    85  		case oracleCoreBlk.opts[1].ID():
    86  			return oracleCoreBlk.opts[1], nil
    87  		default:
    88  			return nil, database.ErrNotFound
    89  		}
    90  	}
    91  
    92  	parentBlk, err := proVM.BuildBlock(context.Background())
    93  	require.NoError(err)
    94  
    95  	// retrieve options ...
    96  	require.IsType(&preForkBlock{}, parentBlk)
    97  	preForkOracleBlk := parentBlk.(*preForkBlock)
    98  	opts, err := preForkOracleBlk.Options(context.Background())
    99  	require.NoError(err)
   100  	require.NoError(opts[0].Verify(context.Background()))
   101  
   102  	// ... show a block can be built on top of an option
   103  	require.NoError(proVM.SetPreference(context.Background(), opts[0].ID()))
   104  
   105  	lastCoreBlk := &TestOptionsBlock{
   106  		Block: *snowmantest.BuildChild(preferredTestBlk),
   107  	}
   108  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   109  		return lastCoreBlk, nil
   110  	}
   111  
   112  	preForkChild, err := proVM.BuildBlock(context.Background())
   113  	require.NoError(err)
   114  	require.IsType(&preForkBlock{}, preForkChild)
   115  }
   116  
   117  func TestOracle_PostForkBlkCanBuiltOnPreForkOption(t *testing.T) {
   118  	require := require.New(t)
   119  
   120  	var (
   121  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   122  		durangoTime    = activationTime
   123  	)
   124  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   125  	defer func() {
   126  		require.NoError(proVM.Shutdown(context.Background()))
   127  	}()
   128  
   129  	// create pre fork oracle block pre activation time...
   130  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   131  	coreTestBlk.TimestampV = activationTime.Add(-1 * time.Second)
   132  
   133  	// ... whose options are post activation time
   134  	preferredBlk := snowmantest.BuildChild(coreTestBlk)
   135  	preferredBlk.TimestampV = activationTime.Add(time.Second)
   136  
   137  	unpreferredBlk := snowmantest.BuildChild(coreTestBlk)
   138  	unpreferredBlk.TimestampV = activationTime.Add(time.Second)
   139  
   140  	oracleCoreBlk := &TestOptionsBlock{
   141  		Block: *coreTestBlk,
   142  		opts: [2]snowman.Block{
   143  			preferredBlk,
   144  			unpreferredBlk,
   145  		},
   146  	}
   147  
   148  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   149  		return oracleCoreBlk, nil
   150  	}
   151  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   152  		switch blkID {
   153  		case snowmantest.GenesisID:
   154  			return snowmantest.Genesis, nil
   155  		case oracleCoreBlk.ID():
   156  			return oracleCoreBlk, nil
   157  		case oracleCoreBlk.opts[0].ID():
   158  			return oracleCoreBlk.opts[0], nil
   159  		case oracleCoreBlk.opts[1].ID():
   160  			return oracleCoreBlk.opts[1], nil
   161  		default:
   162  			return nil, database.ErrNotFound
   163  		}
   164  	}
   165  
   166  	parentBlk, err := proVM.BuildBlock(context.Background())
   167  	require.NoError(err)
   168  
   169  	// retrieve options ...
   170  	require.IsType(&preForkBlock{}, parentBlk)
   171  	preForkOracleBlk := parentBlk.(*preForkBlock)
   172  	opts, err := preForkOracleBlk.Options(context.Background())
   173  	require.NoError(err)
   174  	require.NoError(opts[0].Verify(context.Background()))
   175  
   176  	// ... show a block can be built on top of an option
   177  	require.NoError(proVM.SetPreference(context.Background(), opts[0].ID()))
   178  
   179  	lastCoreBlk := &TestOptionsBlock{
   180  		Block: *snowmantest.BuildChild(preferredBlk),
   181  	}
   182  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   183  		return lastCoreBlk, nil
   184  	}
   185  
   186  	postForkChild, err := proVM.BuildBlock(context.Background())
   187  	require.NoError(err)
   188  	require.IsType(&postForkBlock{}, postForkChild)
   189  }
   190  
   191  func TestBlockVerify_PreFork_ParentChecks(t *testing.T) {
   192  	require := require.New(t)
   193  
   194  	var (
   195  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   196  		durangoTime    = activationTime
   197  	)
   198  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   199  	defer func() {
   200  		require.NoError(proVM.Shutdown(context.Background()))
   201  	}()
   202  
   203  	// create parent block ...
   204  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   205  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   206  		return parentCoreBlk, nil
   207  	}
   208  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   209  		switch blkID {
   210  		case snowmantest.GenesisID:
   211  			return snowmantest.Genesis, nil
   212  		case parentCoreBlk.ID():
   213  			return parentCoreBlk, nil
   214  		default:
   215  			return nil, database.ErrNotFound
   216  		}
   217  	}
   218  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   219  		switch {
   220  		case bytes.Equal(b, snowmantest.GenesisBytes):
   221  			return snowmantest.Genesis, nil
   222  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   223  			return parentCoreBlk, nil
   224  		default:
   225  			return nil, database.ErrNotFound
   226  		}
   227  	}
   228  
   229  	parentBlk, err := proVM.BuildBlock(context.Background())
   230  	require.NoError(err)
   231  
   232  	// .. create child block ...
   233  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   234  	childBlk := preForkBlock{
   235  		Block: childCoreBlk,
   236  		vm:    proVM,
   237  	}
   238  
   239  	{
   240  		// child block referring unknown parent does not verify
   241  		childCoreBlk.ParentV = ids.Empty
   242  		err = childBlk.Verify(context.Background())
   243  		require.ErrorIs(err, database.ErrNotFound)
   244  	}
   245  
   246  	{
   247  		// child block referring known parent does verify
   248  		childCoreBlk.ParentV = parentBlk.ID()
   249  		require.NoError(childBlk.Verify(context.Background()))
   250  	}
   251  }
   252  
   253  func TestBlockVerify_BlocksBuiltOnPreForkGenesis(t *testing.T) {
   254  	require := require.New(t)
   255  
   256  	var (
   257  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   258  		durangoTime    = activationTime
   259  	)
   260  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   261  	defer func() {
   262  		require.NoError(proVM.Shutdown(context.Background()))
   263  	}()
   264  
   265  	preActivationTime := activationTime.Add(-1 * time.Second)
   266  	proVM.Set(preActivationTime)
   267  
   268  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   269  	coreBlk.TimestampV = preActivationTime
   270  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   271  		return coreBlk, nil
   272  	}
   273  
   274  	// preFork block verifies if parent is before fork activation time
   275  	preForkChild, err := proVM.BuildBlock(context.Background())
   276  	require.NoError(err)
   277  	require.IsType(&preForkBlock{}, preForkChild)
   278  
   279  	require.NoError(preForkChild.Verify(context.Background()))
   280  
   281  	// postFork block does NOT verify if parent is before fork activation time
   282  	postForkStatelessChild, err := statelessblock.Build(
   283  		snowmantest.GenesisID,
   284  		coreBlk.Timestamp(),
   285  		0, // pChainHeight
   286  		proVM.StakingCertLeaf,
   287  		coreBlk.Bytes(),
   288  		proVM.ctx.ChainID,
   289  		proVM.StakingLeafSigner,
   290  	)
   291  	require.NoError(err)
   292  	postForkChild := &postForkBlock{
   293  		SignedBlock: postForkStatelessChild,
   294  		postForkCommonComponents: postForkCommonComponents{
   295  			vm:       proVM,
   296  			innerBlk: coreBlk,
   297  			status:   choices.Processing,
   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 = func(context.Context) (ids.ID, error) {
   443  		if coreBlk.Status() == choices.Accepted {
   444  			return coreBlk.ID(), nil
   445  		}
   446  		return snowmantest.GenesisID, nil
   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(choices.Rejected, proBlk.Status())
   478  	require.Equal(choices.Rejected, proBlk.Block.Status())
   479  }
   480  
   481  func TestBlockVerify_ForkBlockIsOracleBlock(t *testing.T) {
   482  	require := require.New(t)
   483  
   484  	var (
   485  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   486  		durangoTime    = activationTime
   487  	)
   488  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   489  	defer func() {
   490  		require.NoError(proVM.Shutdown(context.Background()))
   491  	}()
   492  
   493  	postActivationTime := activationTime.Add(time.Second)
   494  	proVM.Set(postActivationTime)
   495  
   496  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   497  	coreTestBlk.TimestampV = postActivationTime
   498  	coreBlk := &TestOptionsBlock{
   499  		Block: *coreTestBlk,
   500  		opts: [2]snowman.Block{
   501  			snowmantest.BuildChild(coreTestBlk),
   502  			snowmantest.BuildChild(coreTestBlk),
   503  		},
   504  	}
   505  
   506  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   507  		switch blkID {
   508  		case snowmantest.GenesisID:
   509  			return snowmantest.Genesis, nil
   510  		case coreBlk.ID():
   511  			return coreBlk, nil
   512  		case coreBlk.opts[0].ID():
   513  			return coreBlk.opts[0], nil
   514  		case coreBlk.opts[1].ID():
   515  			return coreBlk.opts[1], nil
   516  		default:
   517  			return nil, database.ErrNotFound
   518  		}
   519  	}
   520  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   521  		switch {
   522  		case bytes.Equal(b, snowmantest.GenesisBytes):
   523  			return snowmantest.Genesis, nil
   524  		case bytes.Equal(b, coreBlk.Bytes()):
   525  			return coreBlk, nil
   526  		case bytes.Equal(b, coreBlk.opts[0].Bytes()):
   527  			return coreBlk.opts[0], nil
   528  		case bytes.Equal(b, coreBlk.opts[1].Bytes()):
   529  			return coreBlk.opts[1], nil
   530  		default:
   531  			return nil, errUnknownBlock
   532  		}
   533  	}
   534  
   535  	firstBlock, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes())
   536  	require.NoError(err)
   537  
   538  	require.NoError(firstBlock.Verify(context.Background()))
   539  
   540  	oracleBlock, ok := firstBlock.(snowman.OracleBlock)
   541  	require.True(ok)
   542  
   543  	options, err := oracleBlock.Options(context.Background())
   544  	require.NoError(err)
   545  
   546  	require.NoError(options[0].Verify(context.Background()))
   547  
   548  	require.NoError(options[1].Verify(context.Background()))
   549  }
   550  
   551  func TestBlockVerify_ForkBlockIsOracleBlockButChildrenAreSigned(t *testing.T) {
   552  	require := require.New(t)
   553  
   554  	var (
   555  		activationTime = snowmantest.GenesisTimestamp.Add(10 * time.Second)
   556  		durangoTime    = activationTime
   557  	)
   558  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   559  	defer func() {
   560  		require.NoError(proVM.Shutdown(context.Background()))
   561  	}()
   562  
   563  	postActivationTime := activationTime.Add(time.Second)
   564  	proVM.Set(postActivationTime)
   565  
   566  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   567  	coreTestBlk.TimestampV = postActivationTime
   568  	coreBlk := &TestOptionsBlock{
   569  		Block: *coreTestBlk,
   570  		opts: [2]snowman.Block{
   571  			snowmantest.BuildChild(coreTestBlk),
   572  			snowmantest.BuildChild(coreTestBlk),
   573  		},
   574  	}
   575  
   576  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   577  		switch blkID {
   578  		case snowmantest.GenesisID:
   579  			return snowmantest.Genesis, nil
   580  		case coreBlk.ID():
   581  			return coreBlk, nil
   582  		case coreBlk.opts[0].ID():
   583  			return coreBlk.opts[0], nil
   584  		case coreBlk.opts[1].ID():
   585  			return coreBlk.opts[1], nil
   586  		default:
   587  			return nil, database.ErrNotFound
   588  		}
   589  	}
   590  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   591  		switch {
   592  		case bytes.Equal(b, snowmantest.GenesisBytes):
   593  			return snowmantest.Genesis, nil
   594  		case bytes.Equal(b, coreBlk.Bytes()):
   595  			return coreBlk, nil
   596  		case bytes.Equal(b, coreBlk.opts[0].Bytes()):
   597  			return coreBlk.opts[0], nil
   598  		case bytes.Equal(b, coreBlk.opts[1].Bytes()):
   599  			return coreBlk.opts[1], nil
   600  		default:
   601  			return nil, errUnknownBlock
   602  		}
   603  	}
   604  
   605  	firstBlock, err := proVM.ParseBlock(context.Background(), coreBlk.Bytes())
   606  	require.NoError(err)
   607  
   608  	require.NoError(firstBlock.Verify(context.Background()))
   609  
   610  	slb, err := statelessblock.Build(
   611  		firstBlock.ID(), // refer unknown parent
   612  		firstBlock.Timestamp(),
   613  		0, // pChainHeight,
   614  		proVM.StakingCertLeaf,
   615  		coreBlk.opts[0].Bytes(),
   616  		proVM.ctx.ChainID,
   617  		proVM.StakingLeafSigner,
   618  	)
   619  	require.NoError(err)
   620  
   621  	invalidChild, err := proVM.ParseBlock(context.Background(), slb.Bytes())
   622  	if err != nil {
   623  		// A failure to parse is okay here
   624  		return
   625  	}
   626  
   627  	err = invalidChild.Verify(context.Background())
   628  	require.ErrorIs(err, errUnexpectedBlockType)
   629  }
   630  
   631  // Assert that when the underlying VM implements ChainVMWithBuildBlockContext
   632  // and the proposervm is activated, we only call the VM's BuildBlockWithContext
   633  // when a P-chain height can be correctly provided from the parent block.
   634  func TestPreForkBlock_BuildBlockWithContext(t *testing.T) {
   635  	require := require.New(t)
   636  	ctrl := gomock.NewController(t)
   637  
   638  	pChainHeight := uint64(1337)
   639  	blkID := ids.GenerateTestID()
   640  	innerBlk := snowmantest.NewMockBlock(ctrl)
   641  	innerBlk.EXPECT().ID().Return(blkID).AnyTimes()
   642  	innerBlk.EXPECT().Timestamp().Return(mockable.MaxTime)
   643  	builtBlk := snowmantest.NewMockBlock(ctrl)
   644  	builtBlk.EXPECT().Bytes().Return([]byte{1, 2, 3}).AnyTimes()
   645  	builtBlk.EXPECT().ID().Return(ids.GenerateTestID()).AnyTimes()
   646  	builtBlk.EXPECT().Height().Return(pChainHeight).AnyTimes()
   647  	innerVM := block.NewMockChainVM(ctrl)
   648  	innerVM.EXPECT().BuildBlock(gomock.Any()).Return(builtBlk, nil).AnyTimes()
   649  	vdrState := validators.NewMockState(ctrl)
   650  	vdrState.EXPECT().GetMinimumHeight(context.Background()).Return(pChainHeight, nil).AnyTimes()
   651  
   652  	vm := &VM{
   653  		ChainVM: innerVM,
   654  		ctx: &snow.Context{
   655  			ValidatorState: vdrState,
   656  			Log:            logging.NoLog{},
   657  		},
   658  	}
   659  
   660  	blk := &preForkBlock{
   661  		Block: innerBlk,
   662  		vm:    vm,
   663  	}
   664  
   665  	// Should call BuildBlock since proposervm won't have a P-chain height
   666  	gotChild, err := blk.buildChild(context.Background())
   667  	require.NoError(err)
   668  	require.Equal(builtBlk, gotChild.(*postForkBlock).innerBlk)
   669  
   670  	// Should call BuildBlock since proposervm is not activated
   671  	innerBlk.EXPECT().Timestamp().Return(time.Time{})
   672  	vm.ActivationTime = mockable.MaxTime
   673  
   674  	gotChild, err = blk.buildChild(context.Background())
   675  	require.NoError(err)
   676  	require.Equal(builtBlk, gotChild.(*preForkBlock).Block)
   677  }