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