github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/post_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  	"errors"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/MetalBlockchain/metalgo/database"
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/snow/choices"
    18  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    19  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest"
    20  	"github.com/MetalBlockchain/metalgo/snow/validators"
    21  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    22  	"github.com/MetalBlockchain/metalgo/vms/proposervm/block"
    23  	"github.com/MetalBlockchain/metalgo/vms/proposervm/proposer"
    24  )
    25  
    26  var errDuplicateVerify = errors.New("duplicate verify")
    27  
    28  // ProposerBlock Option interface tests section
    29  func TestOracle_PostForkBlock_ImplementsInterface(t *testing.T) {
    30  	require := require.New(t)
    31  
    32  	// setup
    33  	proBlk := postForkBlock{
    34  		postForkCommonComponents: postForkCommonComponents{
    35  			innerBlk: snowmantest.BuildChild(snowmantest.Genesis),
    36  		},
    37  	}
    38  
    39  	// test
    40  	_, err := proBlk.Options(context.Background())
    41  	require.Equal(snowman.ErrNotOracle, err)
    42  
    43  	// setup
    44  	var (
    45  		activationTime = time.Unix(0, 0)
    46  		durangoTime    = activationTime
    47  	)
    48  	_, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    49  	defer func() {
    50  		require.NoError(proVM.Shutdown(context.Background()))
    51  	}()
    52  
    53  	innerTestBlock := snowmantest.BuildChild(snowmantest.Genesis)
    54  	innerOracleBlk := &TestOptionsBlock{
    55  		Block: *innerTestBlock,
    56  		opts: [2]snowman.Block{
    57  			snowmantest.BuildChild(innerTestBlock),
    58  			snowmantest.BuildChild(innerTestBlock),
    59  		},
    60  	}
    61  
    62  	slb, err := block.Build(
    63  		ids.Empty, // refer unknown parent
    64  		time.Time{},
    65  		0, // pChainHeight,
    66  		proVM.StakingCertLeaf,
    67  		innerOracleBlk.Bytes(),
    68  		proVM.ctx.ChainID,
    69  		proVM.StakingLeafSigner,
    70  	)
    71  	require.NoError(err)
    72  	proBlk = postForkBlock{
    73  		SignedBlock: slb,
    74  		postForkCommonComponents: postForkCommonComponents{
    75  			vm:       proVM,
    76  			innerBlk: innerOracleBlk,
    77  			status:   choices.Processing,
    78  		},
    79  	}
    80  
    81  	// test
    82  	_, err = proBlk.Options(context.Background())
    83  	require.NoError(err)
    84  }
    85  
    86  // ProposerBlock.Verify tests section
    87  func TestBlockVerify_PostForkBlock_PreDurango_ParentChecks(t *testing.T) {
    88  	require := require.New(t)
    89  
    90  	var (
    91  		activationTime = time.Unix(0, 0)
    92  		durangoTime    = mockable.MaxTime // pre Durango
    93  	)
    94  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    95  	defer func() {
    96  		require.NoError(proVM.Shutdown(context.Background()))
    97  	}()
    98  
    99  	pChainHeight := uint64(100)
   100  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   101  		return pChainHeight, nil
   102  	}
   103  
   104  	// create parent block ...
   105  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   106  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   107  		return parentCoreBlk, 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  		case parentCoreBlk.ID():
   114  			return parentCoreBlk, nil
   115  		default:
   116  			return nil, database.ErrNotFound
   117  		}
   118  	}
   119  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   120  		switch {
   121  		case bytes.Equal(b, snowmantest.GenesisBytes):
   122  			return snowmantest.Genesis, nil
   123  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   124  			return parentCoreBlk, nil
   125  		default:
   126  			return nil, errUnknownBlock
   127  		}
   128  	}
   129  
   130  	parentBlk, err := proVM.BuildBlock(context.Background())
   131  	require.NoError(err)
   132  
   133  	require.NoError(parentBlk.Verify(context.Background()))
   134  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   135  
   136  	// .. create child block ...
   137  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   138  	childBlk := postForkBlock{
   139  		postForkCommonComponents: postForkCommonComponents{
   140  			vm:       proVM,
   141  			innerBlk: childCoreBlk,
   142  			status:   choices.Processing,
   143  		},
   144  	}
   145  
   146  	// set proVM to be able to build unsigned blocks
   147  	proVM.Set(proVM.Time().Add(proposer.MaxVerifyDelay))
   148  
   149  	{
   150  		// child block referring unknown parent does not verify
   151  		childSlb, err := block.BuildUnsigned(
   152  			ids.Empty, // refer unknown parent
   153  			proVM.Time(),
   154  			pChainHeight,
   155  			childCoreBlk.Bytes(),
   156  		)
   157  		require.NoError(err)
   158  		childBlk.SignedBlock = childSlb
   159  
   160  		err = childBlk.Verify(context.Background())
   161  		require.ErrorIs(err, database.ErrNotFound)
   162  	}
   163  
   164  	{
   165  		// child block referring known parent does verify
   166  		childSlb, err := block.BuildUnsigned(
   167  			parentBlk.ID(), // refer known parent
   168  			proVM.Time(),
   169  			pChainHeight,
   170  			childCoreBlk.Bytes(),
   171  		)
   172  		require.NoError(err)
   173  		childBlk.SignedBlock = childSlb
   174  
   175  		require.NoError(childBlk.Verify(context.Background()))
   176  	}
   177  }
   178  
   179  func TestBlockVerify_PostForkBlock_PostDurango_ParentChecks(t *testing.T) {
   180  	require := require.New(t)
   181  
   182  	var (
   183  		activationTime = time.Unix(0, 0)
   184  		durangoTime    = activationTime // post Durango
   185  	)
   186  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   187  	defer func() {
   188  		require.NoError(proVM.Shutdown(context.Background()))
   189  	}()
   190  
   191  	pChainHeight := uint64(100)
   192  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   193  		return pChainHeight, nil
   194  	}
   195  
   196  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   197  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   198  		return parentCoreBlk, nil
   199  	}
   200  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   201  		switch blkID {
   202  		case snowmantest.GenesisID:
   203  			return snowmantest.Genesis, nil
   204  		case parentCoreBlk.ID():
   205  			return parentCoreBlk, nil
   206  		default:
   207  			return nil, database.ErrNotFound
   208  		}
   209  	}
   210  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   211  		switch {
   212  		case bytes.Equal(b, snowmantest.GenesisBytes):
   213  			return snowmantest.Genesis, nil
   214  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   215  			return parentCoreBlk, nil
   216  		default:
   217  			return nil, errUnknownBlock
   218  		}
   219  	}
   220  
   221  	parentBlk, err := proVM.BuildBlock(context.Background())
   222  	require.NoError(err)
   223  
   224  	require.NoError(parentBlk.Verify(context.Background()))
   225  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   226  
   227  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   228  	childBlk := postForkBlock{
   229  		postForkCommonComponents: postForkCommonComponents{
   230  			vm:       proVM,
   231  			innerBlk: childCoreBlk,
   232  			status:   choices.Processing,
   233  		},
   234  	}
   235  
   236  	require.NoError(waitForProposerWindow(proVM, parentBlk, parentBlk.(*postForkBlock).PChainHeight()))
   237  
   238  	{
   239  		// child block referring unknown parent does not verify
   240  		childSlb, err := block.Build(
   241  			ids.Empty, // refer unknown parent
   242  			proVM.Time(),
   243  			pChainHeight,
   244  			proVM.StakingCertLeaf,
   245  			childCoreBlk.Bytes(),
   246  			proVM.ctx.ChainID,
   247  			proVM.StakingLeafSigner,
   248  		)
   249  		require.NoError(err)
   250  		childBlk.SignedBlock = childSlb
   251  
   252  		err = childBlk.Verify(context.Background())
   253  		require.ErrorIs(err, database.ErrNotFound)
   254  	}
   255  
   256  	{
   257  		// child block referring known parent does verify
   258  		childSlb, err := block.Build(
   259  			parentBlk.ID(),
   260  			proVM.Time(),
   261  			pChainHeight,
   262  			proVM.StakingCertLeaf,
   263  			childCoreBlk.Bytes(),
   264  			proVM.ctx.ChainID,
   265  			proVM.StakingLeafSigner,
   266  		)
   267  
   268  		require.NoError(err)
   269  		childBlk.SignedBlock = childSlb
   270  
   271  		proVM.Set(childSlb.Timestamp())
   272  		require.NoError(childBlk.Verify(context.Background()))
   273  	}
   274  }
   275  
   276  func TestBlockVerify_PostForkBlock_TimestampChecks(t *testing.T) {
   277  	require := require.New(t)
   278  
   279  	var (
   280  		activationTime = time.Unix(0, 0)
   281  		durangoTime    = mockable.MaxTime
   282  	)
   283  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   284  	defer func() {
   285  		require.NoError(proVM.Shutdown(context.Background()))
   286  	}()
   287  
   288  	// reduce validator state to allow proVM.ctx.NodeID to be easily selected as proposer
   289  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   290  		var (
   291  			thisNode = proVM.ctx.NodeID
   292  			nodeID1  = ids.BuildTestNodeID([]byte{1})
   293  		)
   294  		return map[ids.NodeID]*validators.GetValidatorOutput{
   295  			thisNode: {
   296  				NodeID: thisNode,
   297  				Weight: 5,
   298  			},
   299  			nodeID1: {
   300  				NodeID: nodeID1,
   301  				Weight: 100,
   302  			},
   303  		}, nil
   304  	}
   305  	proVM.ctx.ValidatorState = valState
   306  
   307  	pChainHeight := uint64(100)
   308  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   309  		return pChainHeight, nil
   310  	}
   311  
   312  	// create parent block ...
   313  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   314  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   315  		return parentCoreBlk, nil
   316  	}
   317  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   318  		switch blkID {
   319  		case snowmantest.GenesisID:
   320  			return snowmantest.Genesis, nil
   321  		case parentCoreBlk.ID():
   322  			return parentCoreBlk, nil
   323  		default:
   324  			return nil, database.ErrNotFound
   325  		}
   326  	}
   327  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   328  		switch {
   329  		case bytes.Equal(b, snowmantest.GenesisBytes):
   330  			return snowmantest.Genesis, nil
   331  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   332  			return parentCoreBlk, nil
   333  		default:
   334  			return nil, errUnknownBlock
   335  		}
   336  	}
   337  
   338  	parentBlk, err := proVM.BuildBlock(context.Background())
   339  	require.NoError(err)
   340  
   341  	require.NoError(parentBlk.Verify(context.Background()))
   342  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   343  
   344  	var (
   345  		parentTimestamp    = parentBlk.Timestamp()
   346  		parentPChainHeight = parentBlk.(*postForkBlock).PChainHeight()
   347  	)
   348  
   349  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   350  	childBlk := postForkBlock{
   351  		postForkCommonComponents: postForkCommonComponents{
   352  			vm:       proVM,
   353  			innerBlk: childCoreBlk,
   354  			status:   choices.Processing,
   355  		},
   356  	}
   357  
   358  	{
   359  		// child block timestamp cannot be lower than parent timestamp
   360  		newTime := parentTimestamp.Add(-1 * time.Second)
   361  		proVM.Clock.Set(newTime)
   362  
   363  		childSlb, err := block.Build(
   364  			parentBlk.ID(),
   365  			newTime,
   366  			pChainHeight,
   367  			proVM.StakingCertLeaf,
   368  			childCoreBlk.Bytes(),
   369  			proVM.ctx.ChainID,
   370  			proVM.StakingLeafSigner,
   371  		)
   372  		require.NoError(err)
   373  		childBlk.SignedBlock = childSlb
   374  
   375  		err = childBlk.Verify(context.Background())
   376  		require.ErrorIs(err, errTimeNotMonotonic)
   377  	}
   378  
   379  	blkWinDelay, err := proVM.Delay(context.Background(), childCoreBlk.Height(), parentPChainHeight, proVM.ctx.NodeID, proposer.MaxVerifyWindows)
   380  	require.NoError(err)
   381  
   382  	{
   383  		// block cannot arrive before its creator window starts
   384  		beforeWinStart := parentTimestamp.Add(blkWinDelay).Add(-1 * time.Second)
   385  		proVM.Clock.Set(beforeWinStart)
   386  
   387  		childSlb, err := block.Build(
   388  			parentBlk.ID(),
   389  			beforeWinStart,
   390  			pChainHeight,
   391  			proVM.StakingCertLeaf,
   392  			childCoreBlk.Bytes(),
   393  			proVM.ctx.ChainID,
   394  			proVM.StakingLeafSigner,
   395  		)
   396  		require.NoError(err)
   397  		childBlk.SignedBlock = childSlb
   398  
   399  		err = childBlk.Verify(context.Background())
   400  		require.ErrorIs(err, errProposerWindowNotStarted)
   401  	}
   402  
   403  	{
   404  		// block can arrive at its creator window starts
   405  		atWindowStart := parentTimestamp.Add(blkWinDelay)
   406  		proVM.Clock.Set(atWindowStart)
   407  
   408  		childSlb, err := block.Build(
   409  			parentBlk.ID(),
   410  			atWindowStart,
   411  			pChainHeight,
   412  			proVM.StakingCertLeaf,
   413  			childCoreBlk.Bytes(),
   414  			proVM.ctx.ChainID,
   415  			proVM.StakingLeafSigner,
   416  		)
   417  		require.NoError(err)
   418  		childBlk.SignedBlock = childSlb
   419  
   420  		require.NoError(childBlk.Verify(context.Background()))
   421  	}
   422  
   423  	{
   424  		// block can arrive after its creator window starts
   425  		afterWindowStart := parentTimestamp.Add(blkWinDelay).Add(5 * time.Second)
   426  		proVM.Clock.Set(afterWindowStart)
   427  
   428  		childSlb, err := block.Build(
   429  			parentBlk.ID(),
   430  			afterWindowStart,
   431  			pChainHeight,
   432  			proVM.StakingCertLeaf,
   433  			childCoreBlk.Bytes(),
   434  			proVM.ctx.ChainID,
   435  			proVM.StakingLeafSigner,
   436  		)
   437  		require.NoError(err)
   438  		childBlk.SignedBlock = childSlb
   439  
   440  		require.NoError(childBlk.Verify(context.Background()))
   441  	}
   442  
   443  	{
   444  		// block can arrive within submission window
   445  		atSubWindowEnd := proVM.Time().Add(proposer.MaxVerifyDelay)
   446  		proVM.Clock.Set(atSubWindowEnd)
   447  
   448  		childSlb, err := block.BuildUnsigned(
   449  			parentBlk.ID(),
   450  			atSubWindowEnd,
   451  			pChainHeight,
   452  			childCoreBlk.Bytes(),
   453  		)
   454  		require.NoError(err)
   455  		childBlk.SignedBlock = childSlb
   456  
   457  		require.NoError(childBlk.Verify(context.Background()))
   458  	}
   459  
   460  	{
   461  		// block timestamp cannot be too much in the future
   462  		afterSubWinEnd := proVM.Time().Add(maxSkew).Add(time.Second)
   463  
   464  		childSlb, err := block.Build(
   465  			parentBlk.ID(),
   466  			afterSubWinEnd,
   467  			pChainHeight,
   468  			proVM.StakingCertLeaf,
   469  			childCoreBlk.Bytes(),
   470  			proVM.ctx.ChainID,
   471  			proVM.StakingLeafSigner,
   472  		)
   473  		require.NoError(err)
   474  		childBlk.SignedBlock = childSlb
   475  
   476  		err = childBlk.Verify(context.Background())
   477  		require.ErrorIs(err, errTimeTooAdvanced)
   478  	}
   479  }
   480  
   481  func TestBlockVerify_PostForkBlock_PChainHeightChecks(t *testing.T) {
   482  	require := require.New(t)
   483  
   484  	var (
   485  		activationTime = time.Unix(0, 0)
   486  		durangoTime    = activationTime
   487  	)
   488  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   489  	defer func() {
   490  		require.NoError(proVM.Shutdown(context.Background()))
   491  	}()
   492  
   493  	pChainHeight := uint64(100)
   494  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   495  		return pChainHeight, nil
   496  	}
   497  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   498  		return pChainHeight / 50, nil
   499  	}
   500  
   501  	// create parent block ...
   502  	parentCoreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   503  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   504  		return parentCoreBlk, nil
   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 parentCoreBlk.ID():
   511  			return parentCoreBlk, nil
   512  		default:
   513  			return nil, database.ErrNotFound
   514  		}
   515  	}
   516  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   517  		switch {
   518  		case bytes.Equal(b, snowmantest.GenesisBytes):
   519  			return snowmantest.Genesis, nil
   520  		case bytes.Equal(b, parentCoreBlk.Bytes()):
   521  			return parentCoreBlk, nil
   522  		default:
   523  			return nil, errUnknownBlock
   524  		}
   525  	}
   526  
   527  	parentBlk, err := proVM.BuildBlock(context.Background())
   528  	require.NoError(err)
   529  
   530  	require.NoError(parentBlk.Verify(context.Background()))
   531  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   532  
   533  	// set VM to be ready to build next block. We set it to generate unsigned blocks
   534  	// for simplicity.
   535  	parentBlkPChainHeight := parentBlk.(*postForkBlock).PChainHeight()
   536  	require.NoError(waitForProposerWindow(proVM, parentBlk, parentBlkPChainHeight))
   537  
   538  	childCoreBlk := snowmantest.BuildChild(parentCoreBlk)
   539  	childBlk := postForkBlock{
   540  		postForkCommonComponents: postForkCommonComponents{
   541  			vm:       proVM,
   542  			innerBlk: childCoreBlk,
   543  			status:   choices.Processing,
   544  		},
   545  	}
   546  
   547  	{
   548  		// child P-Chain height must not precede parent P-Chain height
   549  		childSlb, err := block.Build(
   550  			parentBlk.ID(),
   551  			proVM.Time(),
   552  			parentBlkPChainHeight-1,
   553  			proVM.StakingCertLeaf,
   554  			childCoreBlk.Bytes(),
   555  			proVM.ctx.ChainID,
   556  			proVM.StakingLeafSigner,
   557  		)
   558  		require.NoError(err)
   559  		childBlk.SignedBlock = childSlb
   560  
   561  		err = childBlk.Verify(context.Background())
   562  		require.ErrorIs(err, errPChainHeightNotMonotonic)
   563  	}
   564  
   565  	{
   566  		// child P-Chain height can be equal to parent P-Chain height
   567  		childSlb, err := block.Build(
   568  			parentBlk.ID(),
   569  			proVM.Time(),
   570  			parentBlkPChainHeight,
   571  			proVM.StakingCertLeaf,
   572  			childCoreBlk.Bytes(),
   573  			proVM.ctx.ChainID,
   574  			proVM.StakingLeafSigner,
   575  		)
   576  		require.NoError(err)
   577  		childBlk.SignedBlock = childSlb
   578  
   579  		require.NoError(childBlk.Verify(context.Background()))
   580  	}
   581  
   582  	{
   583  		// child P-Chain height may follow parent P-Chain height
   584  		childSlb, err := block.Build(
   585  			parentBlk.ID(),
   586  			proVM.Time(),
   587  			parentBlkPChainHeight,
   588  			proVM.StakingCertLeaf,
   589  			childCoreBlk.Bytes(),
   590  			proVM.ctx.ChainID,
   591  			proVM.StakingLeafSigner,
   592  		)
   593  		require.NoError(err)
   594  		childBlk.SignedBlock = childSlb
   595  
   596  		require.NoError(childBlk.Verify(context.Background()))
   597  	}
   598  
   599  	currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background())
   600  	{
   601  		// block P-Chain height can be equal to current P-Chain height
   602  		childSlb, err := block.Build(
   603  			parentBlk.ID(),
   604  			proVM.Time(),
   605  			currPChainHeight,
   606  			proVM.StakingCertLeaf,
   607  			childCoreBlk.Bytes(),
   608  			proVM.ctx.ChainID,
   609  			proVM.StakingLeafSigner,
   610  		)
   611  		require.NoError(err)
   612  		childBlk.SignedBlock = childSlb
   613  
   614  		require.NoError(childBlk.Verify(context.Background()))
   615  	}
   616  
   617  	{
   618  		// block P-Chain height cannot be at higher than current P-Chain height
   619  		childSlb, err := block.Build(
   620  			parentBlk.ID(),
   621  			proVM.Time(),
   622  			currPChainHeight*2,
   623  			proVM.StakingCertLeaf,
   624  			childCoreBlk.Bytes(),
   625  			proVM.ctx.ChainID,
   626  			proVM.StakingLeafSigner,
   627  		)
   628  		require.NoError(err)
   629  		childBlk.SignedBlock = childSlb
   630  
   631  		err = childBlk.Verify(context.Background())
   632  		require.ErrorIs(err, errPChainHeightNotReached)
   633  	}
   634  }
   635  
   636  func TestBlockVerify_PostForkBlockBuiltOnOption_PChainHeightChecks(t *testing.T) {
   637  	require := require.New(t)
   638  
   639  	var (
   640  		activationTime = time.Unix(0, 0)
   641  		durangoTime    = mockable.MaxTime
   642  	)
   643  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   644  	defer func() {
   645  		require.NoError(proVM.Shutdown(context.Background()))
   646  	}()
   647  
   648  	pChainHeight := uint64(100)
   649  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   650  		return pChainHeight, nil
   651  	}
   652  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   653  		return pChainHeight / 50, nil
   654  	}
   655  
   656  	// create post fork oracle block ...
   657  	innerTestBlock := snowmantest.BuildChild(snowmantest.Genesis)
   658  	oracleCoreBlk := &TestOptionsBlock{
   659  		Block: *innerTestBlock,
   660  	}
   661  	preferredOracleBlkChild := snowmantest.BuildChild(innerTestBlock)
   662  	oracleCoreBlk.opts = [2]snowman.Block{
   663  		preferredOracleBlkChild,
   664  		snowmantest.BuildChild(innerTestBlock),
   665  	}
   666  
   667  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   668  		return oracleCoreBlk, nil
   669  	}
   670  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   671  		switch blkID {
   672  		case snowmantest.GenesisID:
   673  			return snowmantest.Genesis, nil
   674  		case oracleCoreBlk.ID():
   675  			return oracleCoreBlk, nil
   676  		case oracleCoreBlk.opts[0].ID():
   677  			return oracleCoreBlk.opts[0], nil
   678  		case oracleCoreBlk.opts[1].ID():
   679  			return oracleCoreBlk.opts[1], nil
   680  		default:
   681  			return nil, database.ErrNotFound
   682  		}
   683  	}
   684  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   685  		switch {
   686  		case bytes.Equal(b, snowmantest.GenesisBytes):
   687  			return snowmantest.Genesis, nil
   688  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
   689  			return oracleCoreBlk, nil
   690  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
   691  			return oracleCoreBlk.opts[0], nil
   692  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
   693  			return oracleCoreBlk.opts[1], nil
   694  		default:
   695  			return nil, errUnknownBlock
   696  		}
   697  	}
   698  
   699  	oracleBlk, err := proVM.BuildBlock(context.Background())
   700  	require.NoError(err)
   701  
   702  	require.NoError(oracleBlk.Verify(context.Background()))
   703  	require.NoError(proVM.SetPreference(context.Background(), oracleBlk.ID()))
   704  
   705  	// retrieve one option and verify block built on it
   706  	require.IsType(&postForkBlock{}, oracleBlk)
   707  	postForkOracleBlk := oracleBlk.(*postForkBlock)
   708  	opts, err := postForkOracleBlk.Options(context.Background())
   709  	require.NoError(err)
   710  	parentBlk := opts[0]
   711  
   712  	require.NoError(parentBlk.Verify(context.Background()))
   713  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   714  
   715  	// set VM to be ready to build next block. We set it to generate unsigned blocks
   716  	// for simplicity.
   717  	nextTime := parentBlk.Timestamp().Add(proposer.MaxVerifyDelay)
   718  	proVM.Set(nextTime)
   719  
   720  	parentBlkPChainHeight := postForkOracleBlk.PChainHeight() // option takes proposal blocks' Pchain height
   721  
   722  	childCoreBlk := snowmantest.BuildChild(preferredOracleBlkChild)
   723  	childBlk := postForkBlock{
   724  		postForkCommonComponents: postForkCommonComponents{
   725  			vm:       proVM,
   726  			innerBlk: childCoreBlk,
   727  			status:   choices.Processing,
   728  		},
   729  	}
   730  
   731  	{
   732  		// child P-Chain height must not precede parent P-Chain height
   733  		childSlb, err := block.BuildUnsigned(
   734  			parentBlk.ID(),
   735  			nextTime,
   736  			parentBlkPChainHeight-1,
   737  			childCoreBlk.Bytes(),
   738  		)
   739  		require.NoError(err)
   740  		childBlk.SignedBlock = childSlb
   741  
   742  		err = childBlk.Verify(context.Background())
   743  		require.ErrorIs(err, errPChainHeightNotMonotonic)
   744  	}
   745  
   746  	{
   747  		// child P-Chain height can be equal to parent P-Chain height
   748  		childSlb, err := block.BuildUnsigned(
   749  			parentBlk.ID(),
   750  			nextTime,
   751  			parentBlkPChainHeight,
   752  			childCoreBlk.Bytes(),
   753  		)
   754  		require.NoError(err)
   755  		childBlk.SignedBlock = childSlb
   756  
   757  		require.NoError(childBlk.Verify(context.Background()))
   758  	}
   759  
   760  	{
   761  		// child P-Chain height may follow parent P-Chain height
   762  		childSlb, err := block.BuildUnsigned(
   763  			parentBlk.ID(),
   764  			nextTime,
   765  			parentBlkPChainHeight+1,
   766  			childCoreBlk.Bytes(),
   767  		)
   768  		require.NoError(err)
   769  		childBlk.SignedBlock = childSlb
   770  
   771  		require.NoError(childBlk.Verify(context.Background()))
   772  	}
   773  
   774  	currPChainHeight, _ := proVM.ctx.ValidatorState.GetCurrentHeight(context.Background())
   775  	{
   776  		// block P-Chain height can be equal to current P-Chain height
   777  		childSlb, err := block.BuildUnsigned(
   778  			parentBlk.ID(),
   779  			nextTime,
   780  			currPChainHeight,
   781  			childCoreBlk.Bytes(),
   782  		)
   783  		require.NoError(err)
   784  		childBlk.SignedBlock = childSlb
   785  
   786  		require.NoError(childBlk.Verify(context.Background()))
   787  	}
   788  
   789  	{
   790  		// block P-Chain height cannot be at higher than current P-Chain height
   791  		childSlb, err := block.BuildUnsigned(
   792  			parentBlk.ID(),
   793  			nextTime,
   794  			currPChainHeight*2,
   795  			childCoreBlk.Bytes(),
   796  		)
   797  		require.NoError(err)
   798  		childBlk.SignedBlock = childSlb
   799  		err = childBlk.Verify(context.Background())
   800  		require.ErrorIs(err, errPChainHeightNotReached)
   801  	}
   802  }
   803  
   804  func TestBlockVerify_PostForkBlock_CoreBlockVerifyIsCalledOnce(t *testing.T) {
   805  	require := require.New(t)
   806  
   807  	// Verify a block once (in this test by building it).
   808  	// Show that other verify call would not call coreBlk.Verify()
   809  	var (
   810  		activationTime = time.Unix(0, 0)
   811  		durangoTime    = activationTime
   812  	)
   813  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   814  	defer func() {
   815  		require.NoError(proVM.Shutdown(context.Background()))
   816  	}()
   817  
   818  	pChainHeight := uint64(2000)
   819  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   820  		return pChainHeight, nil
   821  	}
   822  
   823  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   824  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   825  		return coreBlk, nil
   826  	}
   827  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   828  		switch blkID {
   829  		case snowmantest.GenesisID:
   830  			return snowmantest.Genesis, nil
   831  		case coreBlk.ID():
   832  			return coreBlk, nil
   833  		default:
   834  			return nil, database.ErrNotFound
   835  		}
   836  	}
   837  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   838  		switch {
   839  		case bytes.Equal(b, snowmantest.GenesisBytes):
   840  			return snowmantest.Genesis, nil
   841  		case bytes.Equal(b, coreBlk.Bytes()):
   842  			return coreBlk, nil
   843  		default:
   844  			return nil, errUnknownBlock
   845  		}
   846  	}
   847  
   848  	builtBlk, err := proVM.BuildBlock(context.Background())
   849  	require.NoError(err)
   850  
   851  	require.NoError(builtBlk.Verify(context.Background()))
   852  
   853  	// set error on coreBlock.Verify and recall Verify()
   854  	coreBlk.VerifyV = errDuplicateVerify
   855  	require.NoError(builtBlk.Verify(context.Background()))
   856  
   857  	// rebuild a block with the same core block
   858  	pChainHeight++
   859  	_, err = proVM.BuildBlock(context.Background())
   860  	require.NoError(err)
   861  }
   862  
   863  // ProposerBlock.Accept tests section
   864  func TestBlockAccept_PostForkBlock_SetsLastAcceptedBlock(t *testing.T) {
   865  	require := require.New(t)
   866  
   867  	// setup
   868  	var (
   869  		activationTime = time.Unix(0, 0)
   870  		durangoTime    = activationTime
   871  	)
   872  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   873  	defer func() {
   874  		require.NoError(proVM.Shutdown(context.Background()))
   875  	}()
   876  
   877  	pChainHeight := uint64(2000)
   878  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   879  		return pChainHeight, nil
   880  	}
   881  
   882  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   883  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   884  		return coreBlk, nil
   885  	}
   886  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   887  		switch blkID {
   888  		case snowmantest.GenesisID:
   889  			return snowmantest.Genesis, nil
   890  		case coreBlk.ID():
   891  			return coreBlk, nil
   892  		default:
   893  			return nil, database.ErrNotFound
   894  		}
   895  	}
   896  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   897  		switch {
   898  		case bytes.Equal(b, snowmantest.GenesisBytes):
   899  			return snowmantest.Genesis, nil
   900  		case bytes.Equal(b, coreBlk.Bytes()):
   901  			return coreBlk, nil
   902  		default:
   903  			return nil, errUnknownBlock
   904  		}
   905  	}
   906  
   907  	builtBlk, err := proVM.BuildBlock(context.Background())
   908  	require.NoError(err)
   909  
   910  	// test
   911  	require.NoError(builtBlk.Accept(context.Background()))
   912  
   913  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   914  		if coreBlk.Status() == choices.Accepted {
   915  			return coreBlk.ID(), nil
   916  		}
   917  		return snowmantest.GenesisID, nil
   918  	}
   919  	acceptedID, err := proVM.LastAccepted(context.Background())
   920  	require.NoError(err)
   921  	require.Equal(builtBlk.ID(), acceptedID)
   922  }
   923  
   924  func TestBlockAccept_PostForkBlock_TwoProBlocksWithSameCoreBlock_OneIsAccepted(t *testing.T) {
   925  	require := require.New(t)
   926  
   927  	var (
   928  		activationTime = time.Unix(0, 0)
   929  		durangoTime    = activationTime
   930  	)
   931  	coreVM, valState, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   932  	defer func() {
   933  		require.NoError(proVM.Shutdown(context.Background()))
   934  	}()
   935  
   936  	var minimumHeight uint64
   937  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   938  		return minimumHeight, nil
   939  	}
   940  
   941  	// generate two blocks with the same core block and store them
   942  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   943  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   944  		return coreBlk, nil
   945  	}
   946  
   947  	minimumHeight = snowmantest.GenesisHeight
   948  
   949  	proBlk1, err := proVM.BuildBlock(context.Background())
   950  	require.NoError(err)
   951  
   952  	minimumHeight++
   953  	proBlk2, err := proVM.BuildBlock(context.Background())
   954  	require.NoError(err)
   955  	require.NotEqual(proBlk2.ID(), proBlk1.ID())
   956  
   957  	// set proBlk1 as preferred
   958  	require.NoError(proBlk1.Accept(context.Background()))
   959  	require.Equal(choices.Accepted, coreBlk.Status())
   960  
   961  	acceptedID, err := proVM.LastAccepted(context.Background())
   962  	require.NoError(err)
   963  	require.Equal(proBlk1.ID(), acceptedID)
   964  }
   965  
   966  // ProposerBlock.Reject tests section
   967  func TestBlockReject_PostForkBlock_InnerBlockIsNotRejected(t *testing.T) {
   968  	require := require.New(t)
   969  
   970  	var (
   971  		activationTime = time.Unix(0, 0)
   972  		durangoTime    = activationTime
   973  	)
   974  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   975  	defer func() {
   976  		require.NoError(proVM.Shutdown(context.Background()))
   977  	}()
   978  
   979  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
   980  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   981  		return coreBlk, nil
   982  	}
   983  
   984  	sb, err := proVM.BuildBlock(context.Background())
   985  	require.NoError(err)
   986  	require.IsType(&postForkBlock{}, sb)
   987  	proBlk := sb.(*postForkBlock)
   988  
   989  	require.NoError(proBlk.Reject(context.Background()))
   990  
   991  	require.Equal(choices.Rejected, proBlk.Status())
   992  	require.NotEqual(choices.Rejected, proBlk.innerBlk.Status())
   993  }
   994  
   995  func TestBlockVerify_PostForkBlock_ShouldBePostForkOption(t *testing.T) {
   996  	require := require.New(t)
   997  
   998  	var (
   999  		activationTime = time.Unix(0, 0)
  1000  		durangoTime    = activationTime
  1001  	)
  1002  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
  1003  	defer func() {
  1004  		require.NoError(proVM.Shutdown(context.Background()))
  1005  	}()
  1006  
  1007  	// create post fork oracle block ...
  1008  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
  1009  	oracleCoreBlk := &TestOptionsBlock{
  1010  		Block: *coreTestBlk,
  1011  		opts: [2]snowman.Block{
  1012  			snowmantest.BuildChild(coreTestBlk),
  1013  			snowmantest.BuildChild(coreTestBlk),
  1014  		},
  1015  	}
  1016  
  1017  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
  1018  		return oracleCoreBlk, nil
  1019  	}
  1020  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1021  		switch blkID {
  1022  		case snowmantest.GenesisID:
  1023  			return snowmantest.Genesis, nil
  1024  		case oracleCoreBlk.ID():
  1025  			return oracleCoreBlk, nil
  1026  		case oracleCoreBlk.opts[0].ID():
  1027  			return oracleCoreBlk.opts[0], nil
  1028  		case oracleCoreBlk.opts[1].ID():
  1029  			return oracleCoreBlk.opts[1], nil
  1030  		default:
  1031  			return nil, database.ErrNotFound
  1032  		}
  1033  	}
  1034  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1035  		switch {
  1036  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1037  			return snowmantest.Genesis, nil
  1038  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
  1039  			return oracleCoreBlk, nil
  1040  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
  1041  			return oracleCoreBlk.opts[0], nil
  1042  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
  1043  			return oracleCoreBlk.opts[1], nil
  1044  		default:
  1045  			return nil, errUnknownBlock
  1046  		}
  1047  	}
  1048  
  1049  	parentBlk, err := proVM.BuildBlock(context.Background())
  1050  	require.NoError(err)
  1051  
  1052  	require.NoError(parentBlk.Verify(context.Background()))
  1053  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
  1054  
  1055  	// retrieve options ...
  1056  	require.IsType(&postForkBlock{}, parentBlk)
  1057  	postForkOracleBlk := parentBlk.(*postForkBlock)
  1058  	opts, err := postForkOracleBlk.Options(context.Background())
  1059  	require.NoError(err)
  1060  	require.IsType(&postForkOption{}, opts[0])
  1061  
  1062  	// ... and verify them the first time
  1063  	require.NoError(opts[0].Verify(context.Background()))
  1064  	require.NoError(opts[1].Verify(context.Background()))
  1065  
  1066  	// Build the child
  1067  	statelessChild, err := block.Build(
  1068  		postForkOracleBlk.ID(),
  1069  		postForkOracleBlk.Timestamp().Add(proposer.WindowDuration),
  1070  		postForkOracleBlk.PChainHeight(),
  1071  		proVM.StakingCertLeaf,
  1072  		oracleCoreBlk.opts[0].Bytes(),
  1073  		proVM.ctx.ChainID,
  1074  		proVM.StakingLeafSigner,
  1075  	)
  1076  	require.NoError(err)
  1077  
  1078  	invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes())
  1079  	if err != nil {
  1080  		// A failure to parse is okay here
  1081  		return
  1082  	}
  1083  
  1084  	err = invalidChild.Verify(context.Background())
  1085  	require.ErrorIs(err, errUnexpectedBlockType)
  1086  }
  1087  
  1088  func TestBlockVerify_PostForkBlock_PChainTooLow(t *testing.T) {
  1089  	require := require.New(t)
  1090  
  1091  	var (
  1092  		activationTime = time.Unix(0, 0)
  1093  		durangoTime    = activationTime
  1094  	)
  1095  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 5)
  1096  	defer func() {
  1097  		require.NoError(proVM.Shutdown(context.Background()))
  1098  	}()
  1099  
  1100  	coreBlk := snowmantest.BuildChild(snowmantest.Genesis)
  1101  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
  1102  		switch blkID {
  1103  		case snowmantest.GenesisID:
  1104  			return snowmantest.Genesis, nil
  1105  		case coreBlk.ID():
  1106  			return coreBlk, nil
  1107  		default:
  1108  			return nil, database.ErrNotFound
  1109  		}
  1110  	}
  1111  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
  1112  		switch {
  1113  		case bytes.Equal(b, snowmantest.GenesisBytes):
  1114  			return snowmantest.Genesis, nil
  1115  		case bytes.Equal(b, coreBlk.Bytes()):
  1116  			return coreBlk, nil
  1117  		default:
  1118  			return nil, errUnknownBlock
  1119  		}
  1120  	}
  1121  
  1122  	statelessChild, err := block.BuildUnsigned(
  1123  		snowmantest.GenesisID,
  1124  		snowmantest.GenesisTimestamp,
  1125  		4,
  1126  		coreBlk.Bytes(),
  1127  	)
  1128  	require.NoError(err)
  1129  
  1130  	invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes())
  1131  	if err != nil {
  1132  		// A failure to parse is okay here
  1133  		return
  1134  	}
  1135  
  1136  	err = invalidChild.Verify(context.Background())
  1137  	require.ErrorIs(err, errPChainHeightTooLow)
  1138  }