github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/post_fork_option_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/prometheus/client_golang/prometheus"
    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"
    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/common"
    22  	"github.com/MetalBlockchain/metalgo/vms/proposervm/block"
    23  )
    24  
    25  var _ snowman.OracleBlock = (*TestOptionsBlock)(nil)
    26  
    27  type TestOptionsBlock struct {
    28  	snowmantest.Block
    29  	opts    [2]snowman.Block
    30  	optsErr error
    31  }
    32  
    33  func (tob TestOptionsBlock) Options(context.Context) ([2]snowman.Block, error) {
    34  	return tob.opts, tob.optsErr
    35  }
    36  
    37  // ProposerBlock.Verify tests section
    38  func TestBlockVerify_PostForkOption_ParentChecks(t *testing.T) {
    39  	require := require.New(t)
    40  
    41  	var (
    42  		activationTime = time.Unix(0, 0)
    43  		durangoTime    = activationTime
    44  	)
    45  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    46  	defer func() {
    47  		require.NoError(proVM.Shutdown(context.Background()))
    48  	}()
    49  
    50  	// create post fork oracle block ...
    51  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
    52  	preferredBlk := snowmantest.BuildChild(coreTestBlk)
    53  	oracleCoreBlk := &TestOptionsBlock{
    54  		Block: *coreTestBlk,
    55  		opts: [2]snowman.Block{
    56  			preferredBlk,
    57  			snowmantest.BuildChild(coreTestBlk),
    58  		},
    59  	}
    60  
    61  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
    62  		return oracleCoreBlk, nil
    63  	}
    64  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
    65  		switch blkID {
    66  		case snowmantest.GenesisID:
    67  			return snowmantest.Genesis, nil
    68  		case oracleCoreBlk.ID():
    69  			return oracleCoreBlk, nil
    70  		case oracleCoreBlk.opts[0].ID():
    71  			return oracleCoreBlk.opts[0], nil
    72  		case oracleCoreBlk.opts[1].ID():
    73  			return oracleCoreBlk.opts[1], nil
    74  		default:
    75  			return nil, database.ErrNotFound
    76  		}
    77  	}
    78  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
    79  		switch {
    80  		case bytes.Equal(b, snowmantest.GenesisBytes):
    81  			return snowmantest.Genesis, nil
    82  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
    83  			return oracleCoreBlk, nil
    84  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
    85  			return oracleCoreBlk.opts[0], nil
    86  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
    87  			return oracleCoreBlk.opts[1], nil
    88  		default:
    89  			return nil, errUnknownBlock
    90  		}
    91  	}
    92  
    93  	parentBlk, err := proVM.BuildBlock(context.Background())
    94  	require.NoError(err)
    95  
    96  	require.NoError(parentBlk.Verify(context.Background()))
    97  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
    98  
    99  	// retrieve options ...
   100  	require.IsType(&postForkBlock{}, parentBlk)
   101  	postForkOracleBlk := parentBlk.(*postForkBlock)
   102  	opts, err := postForkOracleBlk.Options(context.Background())
   103  	require.NoError(err)
   104  	require.IsType(&postForkOption{}, opts[0])
   105  
   106  	// ... and verify them
   107  	require.NoError(opts[0].Verify(context.Background()))
   108  	require.NoError(opts[1].Verify(context.Background()))
   109  
   110  	// show we can build on options
   111  	require.NoError(proVM.SetPreference(context.Background(), opts[0].ID()))
   112  
   113  	childCoreBlk := snowmantest.BuildChild(preferredBlk)
   114  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   115  		return childCoreBlk, nil
   116  	}
   117  	require.NoError(waitForProposerWindow(proVM, opts[0], postForkOracleBlk.PChainHeight()))
   118  
   119  	proChild, err := proVM.BuildBlock(context.Background())
   120  	require.NoError(err)
   121  	require.IsType(&postForkBlock{}, proChild)
   122  	require.NoError(proChild.Verify(context.Background()))
   123  }
   124  
   125  // ProposerBlock.Accept tests section
   126  func TestBlockVerify_PostForkOption_CoreBlockVerifyIsCalledOnce(t *testing.T) {
   127  	require := require.New(t)
   128  
   129  	// Verify an option once; then show that another verify call would not call coreBlk.Verify()
   130  	var (
   131  		activationTime = time.Unix(0, 0)
   132  		durangoTime    = activationTime
   133  	)
   134  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   135  	defer func() {
   136  		require.NoError(proVM.Shutdown(context.Background()))
   137  	}()
   138  
   139  	// create post fork oracle block ...
   140  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   141  	coreOpt0 := snowmantest.BuildChild(coreTestBlk)
   142  	coreOpt1 := snowmantest.BuildChild(coreTestBlk)
   143  	oracleCoreBlk := &TestOptionsBlock{
   144  		Block: *coreTestBlk,
   145  		opts: [2]snowman.Block{
   146  			coreOpt0,
   147  			coreOpt1,
   148  		},
   149  	}
   150  
   151  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   152  		return oracleCoreBlk, nil
   153  	}
   154  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   155  		switch blkID {
   156  		case snowmantest.GenesisID:
   157  			return snowmantest.Genesis, nil
   158  		case oracleCoreBlk.ID():
   159  			return oracleCoreBlk, nil
   160  		case oracleCoreBlk.opts[0].ID():
   161  			return oracleCoreBlk.opts[0], nil
   162  		case oracleCoreBlk.opts[1].ID():
   163  			return oracleCoreBlk.opts[1], nil
   164  		default:
   165  			return nil, database.ErrNotFound
   166  		}
   167  	}
   168  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   169  		switch {
   170  		case bytes.Equal(b, snowmantest.GenesisBytes):
   171  			return snowmantest.Genesis, nil
   172  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
   173  			return oracleCoreBlk, nil
   174  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
   175  			return oracleCoreBlk.opts[0], nil
   176  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
   177  			return oracleCoreBlk.opts[1], nil
   178  		default:
   179  			return nil, errUnknownBlock
   180  		}
   181  	}
   182  
   183  	parentBlk, err := proVM.BuildBlock(context.Background())
   184  	require.NoError(err)
   185  
   186  	require.NoError(parentBlk.Verify(context.Background()))
   187  	require.NoError(proVM.SetPreference(context.Background(), parentBlk.ID()))
   188  
   189  	// retrieve options ...
   190  	require.IsType(&postForkBlock{}, parentBlk)
   191  	postForkOracleBlk := parentBlk.(*postForkBlock)
   192  	opts, err := postForkOracleBlk.Options(context.Background())
   193  	require.NoError(err)
   194  	require.IsType(&postForkOption{}, opts[0])
   195  
   196  	// ... and verify them the first time
   197  	require.NoError(opts[0].Verify(context.Background()))
   198  	require.NoError(opts[1].Verify(context.Background()))
   199  
   200  	// set error on coreBlock.Verify and recall Verify()
   201  	coreOpt0.VerifyV = errDuplicateVerify
   202  	coreOpt1.VerifyV = errDuplicateVerify
   203  
   204  	// ... and verify them again. They verify without call to innerBlk
   205  	require.NoError(opts[0].Verify(context.Background()))
   206  	require.NoError(opts[1].Verify(context.Background()))
   207  }
   208  
   209  func TestBlockAccept_PostForkOption_SetsLastAcceptedBlock(t *testing.T) {
   210  	require := require.New(t)
   211  
   212  	var (
   213  		activationTime = time.Unix(0, 0)
   214  		durangoTime    = activationTime
   215  	)
   216  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   217  	defer func() {
   218  		require.NoError(proVM.Shutdown(context.Background()))
   219  	}()
   220  
   221  	// create post fork oracle block ...
   222  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   223  	oracleCoreBlk := &TestOptionsBlock{
   224  		Block: *coreTestBlk,
   225  		opts: [2]snowman.Block{
   226  			snowmantest.BuildChild(coreTestBlk),
   227  			snowmantest.BuildChild(coreTestBlk),
   228  		},
   229  	}
   230  
   231  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   232  		return oracleCoreBlk, nil
   233  	}
   234  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   235  		switch blkID {
   236  		case snowmantest.GenesisID:
   237  			return snowmantest.Genesis, nil
   238  		case oracleCoreBlk.ID():
   239  			return oracleCoreBlk, nil
   240  		case oracleCoreBlk.opts[0].ID():
   241  			return oracleCoreBlk.opts[0], nil
   242  		case oracleCoreBlk.opts[1].ID():
   243  			return oracleCoreBlk.opts[1], nil
   244  		default:
   245  			return nil, database.ErrNotFound
   246  		}
   247  	}
   248  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   249  		switch {
   250  		case bytes.Equal(b, snowmantest.GenesisBytes):
   251  			return snowmantest.Genesis, nil
   252  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
   253  			return oracleCoreBlk, nil
   254  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
   255  			return oracleCoreBlk.opts[0], nil
   256  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
   257  			return oracleCoreBlk.opts[1], nil
   258  		default:
   259  			return nil, errUnknownBlock
   260  		}
   261  	}
   262  
   263  	parentBlk, err := proVM.BuildBlock(context.Background())
   264  	require.NoError(err)
   265  
   266  	// accept oracle block
   267  	require.NoError(parentBlk.Accept(context.Background()))
   268  
   269  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   270  		if oracleCoreBlk.Status() == choices.Accepted {
   271  			return oracleCoreBlk.ID(), nil
   272  		}
   273  		return snowmantest.GenesisID, nil
   274  	}
   275  	acceptedID, err := proVM.LastAccepted(context.Background())
   276  	require.NoError(err)
   277  	require.Equal(parentBlk.ID(), acceptedID)
   278  
   279  	// accept one of the options
   280  	require.IsType(&postForkBlock{}, parentBlk)
   281  	postForkOracleBlk := parentBlk.(*postForkBlock)
   282  	opts, err := postForkOracleBlk.Options(context.Background())
   283  	require.NoError(err)
   284  
   285  	require.NoError(opts[0].Accept(context.Background()))
   286  
   287  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   288  		if oracleCoreBlk.opts[0].Status() == choices.Accepted {
   289  			return oracleCoreBlk.opts[0].ID(), nil
   290  		}
   291  		return oracleCoreBlk.ID(), nil
   292  	}
   293  	acceptedID, err = proVM.LastAccepted(context.Background())
   294  	require.NoError(err)
   295  	require.Equal(opts[0].ID(), acceptedID)
   296  }
   297  
   298  // ProposerBlock.Reject tests section
   299  func TestBlockReject_InnerBlockIsNotRejected(t *testing.T) {
   300  	require := require.New(t)
   301  
   302  	var (
   303  		activationTime = time.Unix(0, 0)
   304  		durangoTime    = activationTime
   305  	)
   306  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   307  	defer func() {
   308  		require.NoError(proVM.Shutdown(context.Background()))
   309  	}()
   310  
   311  	// create post fork oracle block ...
   312  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   313  	oracleCoreBlk := &TestOptionsBlock{
   314  		Block: *coreTestBlk,
   315  		opts: [2]snowman.Block{
   316  			snowmantest.BuildChild(coreTestBlk),
   317  			snowmantest.BuildChild(coreTestBlk),
   318  		},
   319  	}
   320  
   321  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   322  		return oracleCoreBlk, nil
   323  	}
   324  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   325  		switch blkID {
   326  		case snowmantest.GenesisID:
   327  			return snowmantest.Genesis, nil
   328  		case oracleCoreBlk.ID():
   329  			return oracleCoreBlk, nil
   330  		case oracleCoreBlk.opts[0].ID():
   331  			return oracleCoreBlk.opts[0], nil
   332  		case oracleCoreBlk.opts[1].ID():
   333  			return oracleCoreBlk.opts[1], nil
   334  		default:
   335  			return nil, database.ErrNotFound
   336  		}
   337  	}
   338  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   339  		switch {
   340  		case bytes.Equal(b, snowmantest.GenesisBytes):
   341  			return snowmantest.Genesis, nil
   342  		case bytes.Equal(b, oracleCoreBlk.Bytes()):
   343  			return oracleCoreBlk, nil
   344  		case bytes.Equal(b, oracleCoreBlk.opts[0].Bytes()):
   345  			return oracleCoreBlk.opts[0], nil
   346  		case bytes.Equal(b, oracleCoreBlk.opts[1].Bytes()):
   347  			return oracleCoreBlk.opts[1], nil
   348  		default:
   349  			return nil, errUnknownBlock
   350  		}
   351  	}
   352  
   353  	builtBlk, err := proVM.BuildBlock(context.Background())
   354  	require.NoError(err)
   355  
   356  	// reject oracle block
   357  	require.NoError(builtBlk.Reject(context.Background()))
   358  	require.IsType(&postForkBlock{}, builtBlk)
   359  	proBlk := builtBlk.(*postForkBlock)
   360  
   361  	require.Equal(choices.Rejected, proBlk.Status())
   362  
   363  	require.NotEqual(choices.Rejected, proBlk.innerBlk.Status())
   364  
   365  	// reject an option
   366  	require.IsType(&postForkBlock{}, builtBlk)
   367  	postForkOracleBlk := builtBlk.(*postForkBlock)
   368  	opts, err := postForkOracleBlk.Options(context.Background())
   369  	require.NoError(err)
   370  
   371  	require.NoError(opts[0].Reject(context.Background()))
   372  	require.IsType(&postForkOption{}, opts[0])
   373  	proOpt := opts[0].(*postForkOption)
   374  
   375  	require.Equal(choices.Rejected, proOpt.Status())
   376  
   377  	require.NotEqual(choices.Rejected, proOpt.innerBlk.Status())
   378  }
   379  
   380  func TestBlockVerify_PostForkOption_ParentIsNotOracleWithError(t *testing.T) {
   381  	require := require.New(t)
   382  
   383  	// Verify an option once; then show that another verify call would not call coreBlk.Verify()
   384  	var (
   385  		activationTime = time.Unix(0, 0)
   386  		durangoTime    = activationTime
   387  	)
   388  	coreVM, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
   389  	defer func() {
   390  		require.NoError(proVM.Shutdown(context.Background()))
   391  	}()
   392  
   393  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   394  	coreBlk := &TestOptionsBlock{
   395  		Block:   *coreTestBlk,
   396  		optsErr: snowman.ErrNotOracle,
   397  	}
   398  
   399  	coreChildBlk := snowmantest.BuildChild(coreTestBlk)
   400  
   401  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   402  		return coreBlk, nil
   403  	}
   404  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   405  		switch blkID {
   406  		case snowmantest.GenesisID:
   407  			return snowmantest.Genesis, nil
   408  		case coreBlk.ID():
   409  			return coreBlk, nil
   410  		case coreChildBlk.ID():
   411  			return coreChildBlk, nil
   412  		default:
   413  			return nil, database.ErrNotFound
   414  		}
   415  	}
   416  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   417  		switch {
   418  		case bytes.Equal(b, snowmantest.GenesisBytes):
   419  			return snowmantest.Genesis, nil
   420  		case bytes.Equal(b, coreBlk.Bytes()):
   421  			return coreBlk, nil
   422  		case bytes.Equal(b, coreChildBlk.Bytes()):
   423  			return coreChildBlk, nil
   424  		default:
   425  			return nil, errUnknownBlock
   426  		}
   427  	}
   428  
   429  	parentBlk, err := proVM.BuildBlock(context.Background())
   430  	require.NoError(err)
   431  
   432  	require.IsType(&postForkBlock{}, parentBlk)
   433  	postForkBlk := parentBlk.(*postForkBlock)
   434  	_, err = postForkBlk.Options(context.Background())
   435  	require.Equal(snowman.ErrNotOracle, err)
   436  
   437  	// Build the child
   438  	statelessChild, err := block.BuildOption(
   439  		postForkBlk.ID(),
   440  		coreChildBlk.Bytes(),
   441  	)
   442  	require.NoError(err)
   443  
   444  	invalidChild, err := proVM.ParseBlock(context.Background(), statelessChild.Bytes())
   445  	if err != nil {
   446  		// A failure to parse is okay here
   447  		return
   448  	}
   449  
   450  	err = invalidChild.Verify(context.Background())
   451  	require.ErrorIs(err, database.ErrNotFound)
   452  }
   453  
   454  func TestOptionTimestampValidity(t *testing.T) {
   455  	require := require.New(t)
   456  
   457  	var (
   458  		activationTime = time.Unix(0, 0)
   459  		durangoTime    = activationTime
   460  	)
   461  	coreVM, _, proVM, db := initTestProposerVM(t, activationTime, durangoTime, 0)
   462  
   463  	coreTestBlk := snowmantest.BuildChild(snowmantest.Genesis)
   464  	coreOracleBlk := &TestOptionsBlock{
   465  		Block: *coreTestBlk,
   466  		opts: [2]snowman.Block{
   467  			snowmantest.BuildChild(coreTestBlk),
   468  			snowmantest.BuildChild(coreTestBlk),
   469  		},
   470  	}
   471  
   472  	oracleBlkTime := proVM.Time().Truncate(time.Second)
   473  	statelessBlock, err := block.BuildUnsigned(
   474  		snowmantest.GenesisID,
   475  		oracleBlkTime,
   476  		0,
   477  		coreOracleBlk.Bytes(),
   478  	)
   479  	require.NoError(err)
   480  
   481  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   482  		switch blkID {
   483  		case snowmantest.GenesisID:
   484  			return snowmantest.Genesis, nil
   485  		case coreOracleBlk.ID():
   486  			return coreOracleBlk, nil
   487  		case coreOracleBlk.opts[0].ID():
   488  			return coreOracleBlk.opts[0], nil
   489  		case coreOracleBlk.opts[1].ID():
   490  			return coreOracleBlk.opts[1], nil
   491  		default:
   492  			return nil, errUnknownBlock
   493  		}
   494  	}
   495  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   496  		switch {
   497  		case bytes.Equal(b, snowmantest.GenesisBytes):
   498  			return snowmantest.Genesis, nil
   499  		case bytes.Equal(b, coreOracleBlk.Bytes()):
   500  			return coreOracleBlk, nil
   501  		case bytes.Equal(b, coreOracleBlk.opts[0].Bytes()):
   502  			return coreOracleBlk.opts[0], nil
   503  		case bytes.Equal(b, coreOracleBlk.opts[1].Bytes()):
   504  			return coreOracleBlk.opts[1], nil
   505  		default:
   506  			return nil, errUnknownBlock
   507  		}
   508  	}
   509  
   510  	statefulBlock, err := proVM.ParseBlock(context.Background(), statelessBlock.Bytes())
   511  	require.NoError(err)
   512  
   513  	require.NoError(statefulBlock.Verify(context.Background()))
   514  
   515  	statefulOracleBlock, ok := statefulBlock.(snowman.OracleBlock)
   516  	require.True(ok)
   517  
   518  	options, err := statefulOracleBlock.Options(context.Background())
   519  	require.NoError(err)
   520  
   521  	option := options[0]
   522  	require.NoError(option.Verify(context.Background()))
   523  
   524  	require.NoError(statefulBlock.Accept(context.Background()))
   525  
   526  	coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) {
   527  		require.FailNow("called GetBlock when unable to handle the error")
   528  		return nil, nil
   529  	}
   530  	coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) {
   531  		require.FailNow("called ParseBlock when unable to handle the error")
   532  		return nil, nil
   533  	}
   534  
   535  	require.Equal(oracleBlkTime, option.Timestamp())
   536  
   537  	require.NoError(option.Accept(context.Background()))
   538  	require.NoError(proVM.Shutdown(context.Background()))
   539  
   540  	// Restart the node.
   541  	ctx := proVM.ctx
   542  	proVM = New(
   543  		coreVM,
   544  		Config{
   545  			ActivationTime:      time.Unix(0, 0),
   546  			DurangoTime:         time.Unix(0, 0),
   547  			MinimumPChainHeight: 0,
   548  			MinBlkDelay:         DefaultMinBlockDelay,
   549  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
   550  			StakingLeafSigner:   pTestSigner,
   551  			StakingCertLeaf:     pTestCert,
   552  			Registerer:          prometheus.NewRegistry(),
   553  		},
   554  	)
   555  
   556  	coreVM.InitializeF = func(
   557  		context.Context,
   558  		*snow.Context,
   559  		database.Database,
   560  		[]byte,
   561  		[]byte,
   562  		[]byte,
   563  		chan<- common.Message,
   564  		[]*common.Fx,
   565  		common.AppSender,
   566  	) error {
   567  		return nil
   568  	}
   569  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   570  		return coreOracleBlk.opts[0].ID(), nil
   571  	}
   572  
   573  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   574  		switch blkID {
   575  		case snowmantest.GenesisID:
   576  			return snowmantest.Genesis, nil
   577  		case coreOracleBlk.ID():
   578  			return coreOracleBlk, nil
   579  		case coreOracleBlk.opts[0].ID():
   580  			return coreOracleBlk.opts[0], nil
   581  		case coreOracleBlk.opts[1].ID():
   582  			return coreOracleBlk.opts[1], nil
   583  		default:
   584  			return nil, errUnknownBlock
   585  		}
   586  	}
   587  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   588  		switch {
   589  		case bytes.Equal(b, snowmantest.GenesisBytes):
   590  			return snowmantest.Genesis, nil
   591  		case bytes.Equal(b, coreOracleBlk.Bytes()):
   592  			return coreOracleBlk, nil
   593  		case bytes.Equal(b, coreOracleBlk.opts[0].Bytes()):
   594  			return coreOracleBlk.opts[0], nil
   595  		case bytes.Equal(b, coreOracleBlk.opts[1].Bytes()):
   596  			return coreOracleBlk.opts[1], nil
   597  		default:
   598  			return nil, errUnknownBlock
   599  		}
   600  	}
   601  
   602  	require.NoError(proVM.Initialize(
   603  		context.Background(),
   604  		ctx,
   605  		db,
   606  		nil,
   607  		nil,
   608  		nil,
   609  		nil,
   610  		nil,
   611  		nil,
   612  	))
   613  	defer func() {
   614  		require.NoError(proVM.Shutdown(context.Background()))
   615  	}()
   616  
   617  	statefulOptionBlock, err := proVM.ParseBlock(context.Background(), option.Bytes())
   618  	require.NoError(err)
   619  
   620  	require.Equal(choices.Accepted, statefulOptionBlock.Status())
   621  
   622  	coreVM.GetBlockF = func(context.Context, ids.ID) (snowman.Block, error) {
   623  		require.FailNow("called GetBlock when unable to handle the error")
   624  		return nil, nil
   625  	}
   626  	coreVM.ParseBlockF = func(context.Context, []byte) (snowman.Block, error) {
   627  		require.FailNow("called ParseBlock when unable to handle the error")
   628  		return nil, nil
   629  	}
   630  
   631  	require.Equal(oracleBlkTime, statefulOptionBlock.Timestamp())
   632  }