github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/batched_vm_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/database/memdb"
    17  	"github.com/MetalBlockchain/metalgo/database/prefixdb"
    18  	"github.com/MetalBlockchain/metalgo/ids"
    19  	"github.com/MetalBlockchain/metalgo/snow"
    20  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman"
    21  	"github.com/MetalBlockchain/metalgo/snow/consensus/snowman/snowmantest"
    22  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    23  	"github.com/MetalBlockchain/metalgo/snow/engine/snowman/block"
    24  	"github.com/MetalBlockchain/metalgo/snow/snowtest"
    25  	"github.com/MetalBlockchain/metalgo/snow/validators"
    26  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    27  )
    28  
    29  func TestCoreVMNotRemote(t *testing.T) {
    30  	// if coreVM is not remote VM, a specific error is returned
    31  	require := require.New(t)
    32  	var (
    33  		activationTime = time.Unix(0, 0)
    34  		durangoTime    = activationTime
    35  	)
    36  	_, _, proVM, _ := initTestProposerVM(t, activationTime, durangoTime, 0)
    37  	defer func() {
    38  		require.NoError(proVM.Shutdown(context.Background()))
    39  	}()
    40  
    41  	blkID := ids.Empty
    42  	maxBlocksNum := 1000               // a high value to get all built blocks
    43  	maxBlocksSize := 1000000           // a high value to get all built blocks
    44  	maxBlocksRetrivalTime := time.Hour // a high value to get all built blocks
    45  	_, err := proVM.GetAncestors(
    46  		context.Background(),
    47  		blkID,
    48  		maxBlocksNum,
    49  		maxBlocksSize,
    50  		maxBlocksRetrivalTime,
    51  	)
    52  	require.ErrorIs(err, block.ErrRemoteVMNotImplemented)
    53  
    54  	var blks [][]byte
    55  	_, err = proVM.BatchedParseBlock(context.Background(), blks)
    56  	require.ErrorIs(err, block.ErrRemoteVMNotImplemented)
    57  }
    58  
    59  func TestGetAncestorsPreForkOnly(t *testing.T) {
    60  	require := require.New(t)
    61  	var (
    62  		activationTime = mockable.MaxTime
    63  		durangoTime    = activationTime
    64  	)
    65  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, activationTime, durangoTime)
    66  	defer func() {
    67  		require.NoError(proRemoteVM.Shutdown(context.Background()))
    68  	}()
    69  
    70  	// Build some prefork blocks....
    71  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
    72  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
    73  		return coreBlk1, nil
    74  	}
    75  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
    76  	require.NoError(err)
    77  
    78  	// prepare build of next block
    79  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
    80  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
    81  		switch blkID {
    82  		case coreBlk1.ID():
    83  			return coreBlk1, nil
    84  		default:
    85  			return nil, errUnknownBlock
    86  		}
    87  	}
    88  
    89  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
    90  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
    91  		return coreBlk2, nil
    92  	}
    93  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
    94  	require.NoError(err)
    95  
    96  	// prepare build of next block
    97  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
    98  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
    99  		switch blkID {
   100  		case coreBlk2.ID():
   101  			return coreBlk2, nil
   102  		default:
   103  			return nil, errUnknownBlock
   104  		}
   105  	}
   106  
   107  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   108  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   109  		return coreBlk3, nil
   110  	}
   111  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   112  	require.NoError(err)
   113  
   114  	// ...Call GetAncestors on them ...
   115  	// Note: we assumed that if blkID is not known, that's NOT an error.
   116  	// Simply return an empty result
   117  	coreVM.GetAncestorsF = func(_ context.Context, blkID ids.ID, _, _ int, _ time.Duration) ([][]byte, error) {
   118  		res := make([][]byte, 0, 3)
   119  		switch blkID {
   120  		case coreBlk3.ID():
   121  			res = append(res, coreBlk3.Bytes())
   122  			res = append(res, coreBlk2.Bytes())
   123  			res = append(res, coreBlk1.Bytes())
   124  			return res, nil
   125  		case coreBlk2.ID():
   126  			res = append(res, coreBlk2.Bytes())
   127  			res = append(res, coreBlk1.Bytes())
   128  			return res, nil
   129  		case coreBlk1.ID():
   130  			res = append(res, coreBlk1.Bytes())
   131  			return res, nil
   132  		default:
   133  			return res, nil
   134  		}
   135  	}
   136  
   137  	reqBlkID := builtBlk3.ID()
   138  	maxBlocksNum := 1000               // a high value to get all built blocks
   139  	maxBlocksSize := 1000000           // a high value to get all built blocks
   140  	maxBlocksRetrivalTime := time.Hour // a high value to get all built blocks
   141  	res, err := proRemoteVM.GetAncestors(
   142  		context.Background(),
   143  		reqBlkID,
   144  		maxBlocksNum,
   145  		maxBlocksSize,
   146  		maxBlocksRetrivalTime,
   147  	)
   148  
   149  	// ... and check returned values are as expected
   150  	require.NoError(err)
   151  	require.Len(res, 3)
   152  	require.Equal(builtBlk3.Bytes(), res[0])
   153  	require.Equal(builtBlk2.Bytes(), res[1])
   154  	require.Equal(builtBlk1.Bytes(), res[2])
   155  
   156  	// another good call
   157  	reqBlkID = builtBlk1.ID()
   158  	res, err = proRemoteVM.GetAncestors(
   159  		context.Background(),
   160  		reqBlkID,
   161  		maxBlocksNum,
   162  		maxBlocksSize,
   163  		maxBlocksRetrivalTime,
   164  	)
   165  	require.NoError(err)
   166  	require.Len(res, 1)
   167  	require.Equal(builtBlk1.Bytes(), res[0])
   168  
   169  	// a faulty call
   170  	reqBlkID = ids.Empty
   171  	res, err = proRemoteVM.GetAncestors(
   172  		context.Background(),
   173  		reqBlkID,
   174  		maxBlocksNum,
   175  		maxBlocksSize,
   176  		maxBlocksRetrivalTime,
   177  	)
   178  	require.NoError(err)
   179  	require.Empty(res)
   180  }
   181  
   182  func TestGetAncestorsPostForkOnly(t *testing.T) {
   183  	require := require.New(t)
   184  	var (
   185  		activationTime = time.Unix(0, 0)
   186  		durangoTime    = activationTime
   187  	)
   188  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, activationTime, durangoTime)
   189  	defer func() {
   190  		require.NoError(proRemoteVM.Shutdown(context.Background()))
   191  	}()
   192  
   193  	// Build some post-Fork blocks....
   194  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   195  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   196  		return coreBlk1, nil
   197  	}
   198  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
   199  	require.NoError(err)
   200  
   201  	// prepare build of next block
   202  	require.NoError(builtBlk1.Verify(context.Background()))
   203  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
   204  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk1, 0))
   205  
   206  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
   207  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   208  		return coreBlk2, nil
   209  	}
   210  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
   211  	require.NoError(err)
   212  
   213  	// prepare build of next block
   214  	require.NoError(builtBlk2.Verify(context.Background()))
   215  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
   216  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk2, 0))
   217  
   218  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   219  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   220  		return coreBlk3, nil
   221  	}
   222  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   223  	require.NoError(err)
   224  
   225  	require.NoError(builtBlk3.Verify(context.Background()))
   226  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk3.ID()))
   227  
   228  	// ...Call GetAncestors on them ...
   229  	// Note: we assumed that if blkID is not known, that's NOT an error.
   230  	// Simply return an empty result
   231  	coreVM.GetAncestorsF = func(_ context.Context, blkID ids.ID, _, _ int, _ time.Duration) ([][]byte, error) {
   232  		res := make([][]byte, 0, 3)
   233  		switch blkID {
   234  		case coreBlk3.ID():
   235  			res = append(res, coreBlk3.Bytes())
   236  			res = append(res, coreBlk2.Bytes())
   237  			res = append(res, coreBlk1.Bytes())
   238  			return res, nil
   239  		case coreBlk2.ID():
   240  			res = append(res, coreBlk2.Bytes())
   241  			res = append(res, coreBlk1.Bytes())
   242  			return res, nil
   243  		case coreBlk1.ID():
   244  			res = append(res, coreBlk1.Bytes())
   245  			return res, nil
   246  		default:
   247  			return res, nil
   248  		}
   249  	}
   250  
   251  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   252  		switch {
   253  		case bytes.Equal(b, snowmantest.GenesisBytes):
   254  			return snowmantest.Genesis, nil
   255  		case bytes.Equal(b, coreBlk1.Bytes()):
   256  			return coreBlk1, nil
   257  		case bytes.Equal(b, coreBlk2.Bytes()):
   258  			return coreBlk2, nil
   259  		case bytes.Equal(b, coreBlk3.Bytes()):
   260  			return coreBlk3, nil
   261  		default:
   262  			return nil, errUnknownBlock
   263  		}
   264  	}
   265  
   266  	reqBlkID := builtBlk3.ID()
   267  	maxBlocksNum := 1000               // a high value to get all built blocks
   268  	maxBlocksSize := 1000000           // a high value to get all built blocks
   269  	maxBlocksRetrivalTime := time.Hour // a high value to get all built blocks
   270  	res, err := proRemoteVM.GetAncestors(
   271  		context.Background(),
   272  		reqBlkID,
   273  		maxBlocksNum,
   274  		maxBlocksSize,
   275  		maxBlocksRetrivalTime,
   276  	)
   277  
   278  	// ... and check returned values are as expected
   279  	require.NoError(err)
   280  	require.Len(res, 3)
   281  	require.Equal(builtBlk3.Bytes(), res[0])
   282  	require.Equal(builtBlk2.Bytes(), res[1])
   283  	require.Equal(builtBlk1.Bytes(), res[2])
   284  
   285  	// another good call
   286  	reqBlkID = builtBlk1.ID()
   287  	res, err = proRemoteVM.GetAncestors(
   288  		context.Background(),
   289  		reqBlkID,
   290  		maxBlocksNum,
   291  		maxBlocksSize,
   292  		maxBlocksRetrivalTime,
   293  	)
   294  	require.NoError(err)
   295  	require.Len(res, 1)
   296  	require.Equal(builtBlk1.Bytes(), res[0])
   297  
   298  	// a faulty call
   299  	reqBlkID = ids.Empty
   300  	res, err = proRemoteVM.GetAncestors(
   301  		context.Background(),
   302  		reqBlkID,
   303  		maxBlocksNum,
   304  		maxBlocksSize,
   305  		maxBlocksRetrivalTime,
   306  	)
   307  	require.NoError(err)
   308  	require.Empty(res)
   309  }
   310  
   311  func TestGetAncestorsAtSnomanPlusPlusFork(t *testing.T) {
   312  	require := require.New(t)
   313  
   314  	var (
   315  		currentTime  = time.Now().Truncate(time.Second)
   316  		preForkTime  = currentTime.Add(5 * time.Minute)
   317  		forkTime     = currentTime.Add(10 * time.Minute)
   318  		postForkTime = currentTime.Add(15 * time.Minute)
   319  
   320  		durangoTime = forkTime
   321  	)
   322  
   323  	// enable ProBlks in next future
   324  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, forkTime, durangoTime)
   325  	defer func() {
   326  		require.NoError(proRemoteVM.Shutdown(context.Background()))
   327  	}()
   328  
   329  	// Build some prefork blocks....
   330  	proRemoteVM.Set(preForkTime)
   331  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   332  	coreBlk1.TimestampV = preForkTime
   333  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   334  		return coreBlk1, nil
   335  	}
   336  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
   337  	require.NoError(err)
   338  	require.IsType(&preForkBlock{}, builtBlk1)
   339  
   340  	// prepare build of next block
   341  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
   342  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   343  		switch {
   344  		case blkID == coreBlk1.ID():
   345  			return coreBlk1, nil
   346  		default:
   347  			return nil, errUnknownBlock
   348  		}
   349  	}
   350  
   351  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
   352  	coreBlk2.TimestampV = postForkTime
   353  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   354  		return coreBlk2, nil
   355  	}
   356  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
   357  	require.NoError(err)
   358  	require.IsType(&preForkBlock{}, builtBlk2)
   359  
   360  	// prepare build of next block
   361  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
   362  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   363  		switch {
   364  		case blkID == coreBlk2.ID():
   365  			return coreBlk2, nil
   366  		default:
   367  			return nil, errUnknownBlock
   368  		}
   369  	}
   370  
   371  	// .. and some post-fork
   372  	proRemoteVM.Set(postForkTime)
   373  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   374  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   375  		return coreBlk3, nil
   376  	}
   377  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   378  	require.NoError(err)
   379  	require.IsType(&postForkBlock{}, builtBlk3)
   380  
   381  	// prepare build of next block
   382  	require.NoError(builtBlk3.Verify(context.Background()))
   383  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk3.ID()))
   384  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk3, builtBlk3.(*postForkBlock).PChainHeight()))
   385  
   386  	coreBlk4 := snowmantest.BuildChild(coreBlk3)
   387  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   388  		return coreBlk4, nil
   389  	}
   390  	builtBlk4, err := proRemoteVM.BuildBlock(context.Background())
   391  	require.NoError(err)
   392  	require.IsType(&postForkBlock{}, builtBlk4)
   393  	require.NoError(builtBlk4.Verify(context.Background()))
   394  
   395  	// ...Call GetAncestors on them ...
   396  	// Note: we assumed that if blkID is not known, that's NOT an error.
   397  	// Simply return an empty result
   398  	coreVM.GetAncestorsF = func(_ context.Context, blkID ids.ID, maxBlocksNum, _ int, _ time.Duration) ([][]byte, error) {
   399  		sortedBlocks := [][]byte{
   400  			coreBlk4.Bytes(),
   401  			coreBlk3.Bytes(),
   402  			coreBlk2.Bytes(),
   403  			coreBlk1.Bytes(),
   404  		}
   405  		var startIndex int
   406  		switch blkID {
   407  		case coreBlk4.ID():
   408  			startIndex = 0
   409  		case coreBlk3.ID():
   410  			startIndex = 1
   411  		case coreBlk2.ID():
   412  			startIndex = 2
   413  		case coreBlk1.ID():
   414  			startIndex = 3
   415  		default:
   416  			return nil, nil // unknown blockID
   417  		}
   418  
   419  		endIndex := min(startIndex+maxBlocksNum, len(sortedBlocks))
   420  		return sortedBlocks[startIndex:endIndex], nil
   421  	}
   422  
   423  	// load all known blocks
   424  	reqBlkID := builtBlk4.ID()
   425  	maxBlocksNum := 1000                      // an high value to get all built blocks
   426  	maxBlocksSize := 1000000                  // an high value to get all built blocks
   427  	maxBlocksRetrivalTime := 10 * time.Minute // an high value to get all built blocks
   428  	res, err := proRemoteVM.GetAncestors(
   429  		context.Background(),
   430  		reqBlkID,
   431  		maxBlocksNum,
   432  		maxBlocksSize,
   433  		maxBlocksRetrivalTime,
   434  	)
   435  
   436  	// ... and check returned values are as expected
   437  	require.NoError(err)
   438  	require.Len(res, 4)
   439  	require.Equal(builtBlk4.Bytes(), res[0])
   440  	require.Equal(builtBlk3.Bytes(), res[1])
   441  	require.Equal(builtBlk2.Bytes(), res[2])
   442  	require.Equal(builtBlk1.Bytes(), res[3])
   443  
   444  	// Regression case: load some prefork and some postfork blocks.
   445  	reqBlkID = builtBlk4.ID()
   446  	maxBlocksNum = 3
   447  	res, err = proRemoteVM.GetAncestors(
   448  		context.Background(),
   449  		reqBlkID,
   450  		maxBlocksNum,
   451  		maxBlocksSize,
   452  		maxBlocksRetrivalTime,
   453  	)
   454  
   455  	// ... and check returned values are as expected
   456  	require.NoError(err)
   457  	require.Len(res, 3)
   458  	require.Equal(builtBlk4.Bytes(), res[0])
   459  	require.Equal(builtBlk3.Bytes(), res[1])
   460  	require.Equal(builtBlk2.Bytes(), res[2])
   461  
   462  	// another good call
   463  	reqBlkID = builtBlk1.ID()
   464  	res, err = proRemoteVM.GetAncestors(
   465  		context.Background(),
   466  		reqBlkID,
   467  		maxBlocksNum,
   468  		maxBlocksSize,
   469  		maxBlocksRetrivalTime,
   470  	)
   471  	require.NoError(err)
   472  	require.Len(res, 1)
   473  	require.Equal(builtBlk1.Bytes(), res[0])
   474  
   475  	// a faulty call
   476  	reqBlkID = ids.Empty
   477  	res, err = proRemoteVM.GetAncestors(
   478  		context.Background(),
   479  		reqBlkID,
   480  		maxBlocksNum,
   481  		maxBlocksSize,
   482  		maxBlocksRetrivalTime,
   483  	)
   484  	require.NoError(err)
   485  	require.Empty(res)
   486  }
   487  
   488  func TestBatchedParseBlockPreForkOnly(t *testing.T) {
   489  	require := require.New(t)
   490  	var (
   491  		activationTime = mockable.MaxTime
   492  		durangoTime    = activationTime
   493  	)
   494  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, activationTime, durangoTime)
   495  	defer func() {
   496  		require.NoError(proRemoteVM.Shutdown(context.Background()))
   497  	}()
   498  
   499  	// Build some prefork blocks....
   500  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   501  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   502  		return coreBlk1, nil
   503  	}
   504  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
   505  	require.NoError(err)
   506  
   507  	// prepare build of next block
   508  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
   509  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   510  		switch blkID {
   511  		case coreBlk1.ID():
   512  			return coreBlk1, nil
   513  		default:
   514  			return nil, errUnknownBlock
   515  		}
   516  	}
   517  
   518  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
   519  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   520  		return coreBlk2, nil
   521  	}
   522  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
   523  	require.NoError(err)
   524  
   525  	// prepare build of next block
   526  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
   527  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   528  		switch {
   529  		case blkID == coreBlk2.ID():
   530  			return coreBlk2, nil
   531  		default:
   532  			return nil, errUnknownBlock
   533  		}
   534  	}
   535  
   536  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   537  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   538  		return coreBlk3, nil
   539  	}
   540  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   541  	require.NoError(err)
   542  
   543  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   544  		switch {
   545  		case bytes.Equal(b, coreBlk1.Bytes()):
   546  			return coreBlk1, nil
   547  		case bytes.Equal(b, coreBlk2.Bytes()):
   548  			return coreBlk2, nil
   549  		case bytes.Equal(b, coreBlk3.Bytes()):
   550  			return coreBlk3, nil
   551  		default:
   552  			return nil, errUnknownBlock
   553  		}
   554  	}
   555  
   556  	coreVM.BatchedParseBlockF = func(_ context.Context, blks [][]byte) ([]snowman.Block, error) {
   557  		res := make([]snowman.Block, 0, len(blks))
   558  		for _, blkBytes := range blks {
   559  			switch {
   560  			case bytes.Equal(blkBytes, coreBlk1.Bytes()):
   561  				res = append(res, coreBlk1)
   562  			case bytes.Equal(blkBytes, coreBlk2.Bytes()):
   563  				res = append(res, coreBlk2)
   564  			case bytes.Equal(blkBytes, coreBlk3.Bytes()):
   565  				res = append(res, coreBlk3)
   566  			default:
   567  				return nil, errUnknownBlock
   568  			}
   569  		}
   570  		return res, nil
   571  	}
   572  
   573  	bytesToParse := [][]byte{
   574  		builtBlk1.Bytes(),
   575  		builtBlk2.Bytes(),
   576  		builtBlk3.Bytes(),
   577  	}
   578  	res, err := proRemoteVM.BatchedParseBlock(context.Background(), bytesToParse)
   579  	require.NoError(err)
   580  	require.Len(res, 3)
   581  	require.Equal(builtBlk1.ID(), res[0].ID())
   582  	require.Equal(builtBlk2.ID(), res[1].ID())
   583  	require.Equal(builtBlk3.ID(), res[2].ID())
   584  }
   585  
   586  func TestBatchedParseBlockPostForkOnly(t *testing.T) {
   587  	require := require.New(t)
   588  	var (
   589  		activationTime = time.Unix(0, 0)
   590  		durangoTime    = activationTime
   591  	)
   592  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, activationTime, durangoTime)
   593  	defer func() {
   594  		require.NoError(proRemoteVM.Shutdown(context.Background()))
   595  	}()
   596  
   597  	// Build some post-Fork blocks....
   598  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   599  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   600  		return coreBlk1, nil
   601  	}
   602  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
   603  	require.NoError(err)
   604  
   605  	// prepare build of next block
   606  	require.NoError(builtBlk1.Verify(context.Background()))
   607  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
   608  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk1, 0))
   609  
   610  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
   611  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   612  		return coreBlk2, nil
   613  	}
   614  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
   615  	require.NoError(err)
   616  
   617  	// prepare build of next block
   618  	require.NoError(builtBlk2.Verify(context.Background()))
   619  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
   620  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk2, builtBlk2.(*postForkBlock).PChainHeight()))
   621  
   622  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   623  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   624  		return coreBlk3, nil
   625  	}
   626  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   627  	require.NoError(err)
   628  
   629  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   630  		switch {
   631  		case bytes.Equal(b, coreBlk1.Bytes()):
   632  			return coreBlk1, nil
   633  		case bytes.Equal(b, coreBlk2.Bytes()):
   634  			return coreBlk2, nil
   635  		case bytes.Equal(b, coreBlk3.Bytes()):
   636  			return coreBlk3, nil
   637  		default:
   638  			return nil, errUnknownBlock
   639  		}
   640  	}
   641  
   642  	coreVM.BatchedParseBlockF = func(_ context.Context, blks [][]byte) ([]snowman.Block, error) {
   643  		res := make([]snowman.Block, 0, len(blks))
   644  		for _, blkBytes := range blks {
   645  			switch {
   646  			case bytes.Equal(blkBytes, coreBlk1.Bytes()):
   647  				res = append(res, coreBlk1)
   648  			case bytes.Equal(blkBytes, coreBlk2.Bytes()):
   649  				res = append(res, coreBlk2)
   650  			case bytes.Equal(blkBytes, coreBlk3.Bytes()):
   651  				res = append(res, coreBlk3)
   652  			default:
   653  				return nil, errUnknownBlock
   654  			}
   655  		}
   656  		return res, nil
   657  	}
   658  
   659  	bytesToParse := [][]byte{
   660  		builtBlk1.Bytes(),
   661  		builtBlk2.Bytes(),
   662  		builtBlk3.Bytes(),
   663  	}
   664  	res, err := proRemoteVM.BatchedParseBlock(context.Background(), bytesToParse)
   665  	require.NoError(err)
   666  	require.Len(res, 3)
   667  	require.Equal(builtBlk1.ID(), res[0].ID())
   668  	require.Equal(builtBlk2.ID(), res[1].ID())
   669  	require.Equal(builtBlk3.ID(), res[2].ID())
   670  }
   671  
   672  func TestBatchedParseBlockAtSnomanPlusPlusFork(t *testing.T) {
   673  	require := require.New(t)
   674  
   675  	var (
   676  		currentTime  = time.Now().Truncate(time.Second)
   677  		preForkTime  = currentTime.Add(5 * time.Minute)
   678  		forkTime     = currentTime.Add(10 * time.Minute)
   679  		postForkTime = currentTime.Add(15 * time.Minute)
   680  
   681  		durangoTime = forkTime
   682  	)
   683  
   684  	// enable ProBlks in next future
   685  	coreVM, proRemoteVM := initTestRemoteProposerVM(t, forkTime, durangoTime)
   686  	defer func() {
   687  		require.NoError(proRemoteVM.Shutdown(context.Background()))
   688  	}()
   689  
   690  	// Build some prefork blocks....
   691  	proRemoteVM.Set(preForkTime)
   692  	coreBlk1 := snowmantest.BuildChild(snowmantest.Genesis)
   693  	coreBlk1.TimestampV = preForkTime
   694  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   695  		return coreBlk1, nil
   696  	}
   697  	builtBlk1, err := proRemoteVM.BuildBlock(context.Background())
   698  	require.NoError(err)
   699  	require.IsType(&preForkBlock{}, builtBlk1)
   700  
   701  	// prepare build of next block
   702  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk1.ID()))
   703  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   704  		switch {
   705  		case blkID == coreBlk1.ID():
   706  			return coreBlk1, nil
   707  		default:
   708  			return nil, errUnknownBlock
   709  		}
   710  	}
   711  
   712  	coreBlk2 := snowmantest.BuildChild(coreBlk1)
   713  	coreBlk2.TimestampV = postForkTime
   714  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   715  		return coreBlk2, nil
   716  	}
   717  	builtBlk2, err := proRemoteVM.BuildBlock(context.Background())
   718  	require.NoError(err)
   719  	require.IsType(&preForkBlock{}, builtBlk2)
   720  
   721  	// prepare build of next block
   722  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk2.ID()))
   723  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   724  		switch {
   725  		case blkID == coreBlk2.ID():
   726  			return coreBlk2, nil
   727  		default:
   728  			return nil, errUnknownBlock
   729  		}
   730  	}
   731  
   732  	// .. and some post-fork
   733  	proRemoteVM.Set(postForkTime)
   734  	coreBlk3 := snowmantest.BuildChild(coreBlk2)
   735  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   736  		return coreBlk3, nil
   737  	}
   738  	builtBlk3, err := proRemoteVM.BuildBlock(context.Background())
   739  	require.NoError(err)
   740  	require.IsType(&postForkBlock{}, builtBlk3)
   741  
   742  	// prepare build of next block
   743  	require.NoError(builtBlk3.Verify(context.Background()))
   744  	require.NoError(proRemoteVM.SetPreference(context.Background(), builtBlk3.ID()))
   745  	require.NoError(waitForProposerWindow(proRemoteVM, builtBlk3, builtBlk3.(*postForkBlock).PChainHeight()))
   746  
   747  	coreBlk4 := snowmantest.BuildChild(coreBlk3)
   748  	coreVM.BuildBlockF = func(context.Context) (snowman.Block, error) {
   749  		return coreBlk4, nil
   750  	}
   751  	builtBlk4, err := proRemoteVM.BuildBlock(context.Background())
   752  	require.NoError(err)
   753  	require.IsType(&postForkBlock{}, builtBlk4)
   754  	require.NoError(builtBlk4.Verify(context.Background()))
   755  
   756  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   757  		switch {
   758  		case bytes.Equal(b, coreBlk1.Bytes()):
   759  			return coreBlk1, nil
   760  		case bytes.Equal(b, coreBlk2.Bytes()):
   761  			return coreBlk2, nil
   762  		case bytes.Equal(b, coreBlk3.Bytes()):
   763  			return coreBlk3, nil
   764  		case bytes.Equal(b, coreBlk4.Bytes()):
   765  			return coreBlk4, nil
   766  		default:
   767  			return nil, errUnknownBlock
   768  		}
   769  	}
   770  
   771  	coreVM.BatchedParseBlockF = func(_ context.Context, blks [][]byte) ([]snowman.Block, error) {
   772  		res := make([]snowman.Block, 0, len(blks))
   773  		for _, blkBytes := range blks {
   774  			switch {
   775  			case bytes.Equal(blkBytes, coreBlk1.Bytes()):
   776  				res = append(res, coreBlk1)
   777  			case bytes.Equal(blkBytes, coreBlk2.Bytes()):
   778  				res = append(res, coreBlk2)
   779  			case bytes.Equal(blkBytes, coreBlk3.Bytes()):
   780  				res = append(res, coreBlk3)
   781  			case bytes.Equal(blkBytes, coreBlk4.Bytes()):
   782  				res = append(res, coreBlk4)
   783  			default:
   784  				return nil, errUnknownBlock
   785  			}
   786  		}
   787  		return res, nil
   788  	}
   789  
   790  	bytesToParse := [][]byte{
   791  		builtBlk4.Bytes(),
   792  		builtBlk3.Bytes(),
   793  		builtBlk2.Bytes(),
   794  		builtBlk1.Bytes(),
   795  	}
   796  
   797  	res, err := proRemoteVM.BatchedParseBlock(context.Background(), bytesToParse)
   798  	require.NoError(err)
   799  	require.Len(res, 4)
   800  	require.Equal(builtBlk4.ID(), res[0].ID())
   801  	require.Equal(builtBlk3.ID(), res[1].ID())
   802  	require.Equal(builtBlk2.ID(), res[2].ID())
   803  	require.Equal(builtBlk1.ID(), res[3].ID())
   804  }
   805  
   806  type TestRemoteProposerVM struct {
   807  	*block.TestBatchedVM
   808  	*block.TestVM
   809  }
   810  
   811  func initTestRemoteProposerVM(
   812  	t *testing.T,
   813  	activationTime,
   814  	durangoTime time.Time,
   815  ) (
   816  	TestRemoteProposerVM,
   817  	*VM,
   818  ) {
   819  	require := require.New(t)
   820  
   821  	initialState := []byte("genesis state")
   822  	coreVM := TestRemoteProposerVM{
   823  		TestVM:        &block.TestVM{},
   824  		TestBatchedVM: &block.TestBatchedVM{},
   825  	}
   826  	coreVM.TestVM.T = t
   827  	coreVM.TestBatchedVM.T = t
   828  
   829  	coreVM.InitializeF = func(
   830  		context.Context,
   831  		*snow.Context,
   832  		database.Database,
   833  		[]byte,
   834  		[]byte,
   835  		[]byte,
   836  		chan<- common.Message,
   837  		[]*common.Fx,
   838  		common.AppSender,
   839  	) error {
   840  		return nil
   841  	}
   842  	coreVM.LastAcceptedF = func(context.Context) (ids.ID, error) {
   843  		return snowmantest.GenesisID, nil
   844  	}
   845  	coreVM.GetBlockF = func(_ context.Context, blkID ids.ID) (snowman.Block, error) {
   846  		switch blkID {
   847  		case snowmantest.GenesisID:
   848  			return snowmantest.Genesis, nil
   849  		default:
   850  			return nil, errUnknownBlock
   851  		}
   852  	}
   853  	coreVM.ParseBlockF = func(_ context.Context, b []byte) (snowman.Block, error) {
   854  		switch {
   855  		case bytes.Equal(b, snowmantest.GenesisBytes):
   856  			return snowmantest.Genesis, nil
   857  		default:
   858  			return nil, errUnknownBlock
   859  		}
   860  	}
   861  
   862  	proVM := New(
   863  		coreVM,
   864  		Config{
   865  			ActivationTime:      activationTime,
   866  			DurangoTime:         durangoTime,
   867  			MinimumPChainHeight: 0,
   868  			MinBlkDelay:         DefaultMinBlockDelay,
   869  			NumHistoricalBlocks: DefaultNumHistoricalBlocks,
   870  			StakingLeafSigner:   pTestSigner,
   871  			StakingCertLeaf:     pTestCert,
   872  			Registerer:          prometheus.NewRegistry(),
   873  		},
   874  	)
   875  
   876  	valState := &validators.TestState{
   877  		T: t,
   878  	}
   879  	valState.GetMinimumHeightF = func(context.Context) (uint64, error) {
   880  		return snowmantest.GenesisHeight, nil
   881  	}
   882  	valState.GetCurrentHeightF = func(context.Context) (uint64, error) {
   883  		return defaultPChainHeight, nil
   884  	}
   885  	valState.GetValidatorSetF = func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   886  		var (
   887  			thisNode = proVM.ctx.NodeID
   888  			nodeID1  = ids.BuildTestNodeID([]byte{1})
   889  			nodeID2  = ids.BuildTestNodeID([]byte{2})
   890  			nodeID3  = ids.BuildTestNodeID([]byte{3})
   891  		)
   892  		return map[ids.NodeID]*validators.GetValidatorOutput{
   893  			thisNode: {
   894  				NodeID: thisNode,
   895  				Weight: 10,
   896  			},
   897  			nodeID1: {
   898  				NodeID: nodeID1,
   899  				Weight: 5,
   900  			},
   901  			nodeID2: {
   902  				NodeID: nodeID2,
   903  				Weight: 6,
   904  			},
   905  			nodeID3: {
   906  				NodeID: nodeID3,
   907  				Weight: 7,
   908  			},
   909  		}, nil
   910  	}
   911  
   912  	ctx := snowtest.Context(t, snowtest.CChainID)
   913  	ctx.NodeID = ids.NodeIDFromCert(pTestCert)
   914  	ctx.ValidatorState = valState
   915  
   916  	require.NoError(proVM.Initialize(
   917  		context.Background(),
   918  		ctx,
   919  		prefixdb.New([]byte{}, memdb.New()), // make sure that DBs are compressed correctly
   920  		initialState,
   921  		nil,
   922  		nil,
   923  		nil,
   924  		nil,
   925  		nil,
   926  	))
   927  
   928  	// Initialize shouldn't be called again
   929  	coreVM.InitializeF = nil
   930  
   931  	require.NoError(proVM.SetState(context.Background(), snow.NormalOp))
   932  	require.NoError(proVM.SetPreference(context.Background(), snowmantest.GenesisID))
   933  	return coreVM, proVM
   934  }