github.com/MetalBlockchain/metalgo@v1.11.9/vms/platformvm/block/executor/verifier_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 executor
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/mock/gomock"
    13  
    14  	"github.com/MetalBlockchain/metalgo/chains/atomic"
    15  	"github.com/MetalBlockchain/metalgo/database"
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/snow"
    18  	"github.com/MetalBlockchain/metalgo/utils/logging"
    19  	"github.com/MetalBlockchain/metalgo/utils/set"
    20  	"github.com/MetalBlockchain/metalgo/utils/timer/mockable"
    21  	"github.com/MetalBlockchain/metalgo/vms/components/verify"
    22  	"github.com/MetalBlockchain/metalgo/vms/platformvm/block"
    23  	"github.com/MetalBlockchain/metalgo/vms/platformvm/config"
    24  	"github.com/MetalBlockchain/metalgo/vms/platformvm/state"
    25  	"github.com/MetalBlockchain/metalgo/vms/platformvm/status"
    26  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs"
    27  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/executor"
    28  	"github.com/MetalBlockchain/metalgo/vms/platformvm/txs/mempool"
    29  	"github.com/MetalBlockchain/metalgo/vms/platformvm/upgrade"
    30  )
    31  
    32  func TestVerifierVisitProposalBlock(t *testing.T) {
    33  	require := require.New(t)
    34  	ctrl := gomock.NewController(t)
    35  
    36  	s := state.NewMockState(ctrl)
    37  	mempool := mempool.NewMockMempool(ctrl)
    38  	parentID := ids.GenerateTestID()
    39  	parentStatelessBlk := block.NewMockBlock(ctrl)
    40  	parentOnAcceptState := state.NewMockDiff(ctrl)
    41  	timestamp := time.Now()
    42  	// One call for each of onCommitState and onAbortState.
    43  	parentOnAcceptState.EXPECT().GetTimestamp().Return(timestamp).Times(2)
    44  
    45  	backend := &backend{
    46  		lastAccepted: parentID,
    47  		blkIDToState: map[ids.ID]*blockState{
    48  			parentID: {
    49  				statelessBlock: parentStatelessBlk,
    50  				onAcceptState:  parentOnAcceptState,
    51  			},
    52  		},
    53  		Mempool: mempool,
    54  		state:   s,
    55  		ctx: &snow.Context{
    56  			Log: logging.NoLog{},
    57  		},
    58  	}
    59  	verifier := &verifier{
    60  		txExecutorBackend: &executor.Backend{
    61  			Config: &config.Config{
    62  				UpgradeConfig: upgrade.Config{
    63  					BanffTime: mockable.MaxTime, // banff is not activated
    64  				},
    65  			},
    66  			Clk: &mockable.Clock{},
    67  		},
    68  		backend: backend,
    69  	}
    70  	manager := &manager{
    71  		backend:  backend,
    72  		verifier: verifier,
    73  	}
    74  
    75  	blkTx := txs.NewMockUnsignedTx(ctrl)
    76  	blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.ProposalTxExecutor{})).Return(nil).Times(1)
    77  
    78  	// We can't serialize [blkTx] because it isn't
    79  	// registered with the blocks.Codec.
    80  	// Serialize this block with a dummy tx
    81  	// and replace it after creation with the mock tx.
    82  	// TODO allow serialization of mock txs.
    83  	apricotBlk, err := block.NewApricotProposalBlock(
    84  		parentID,
    85  		2,
    86  		&txs.Tx{
    87  			Unsigned: &txs.AdvanceTimeTx{},
    88  			Creds:    []verify.Verifiable{},
    89  		},
    90  	)
    91  	require.NoError(err)
    92  	apricotBlk.Tx.Unsigned = blkTx
    93  
    94  	// Set expectations for dependencies.
    95  	tx := apricotBlk.Txs()[0]
    96  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
    97  	mempool.EXPECT().Remove([]*txs.Tx{tx}).Times(1)
    98  
    99  	// Visit the block
   100  	blk := manager.NewBlock(apricotBlk)
   101  	require.NoError(blk.Verify(context.Background()))
   102  	require.Contains(verifier.backend.blkIDToState, apricotBlk.ID())
   103  	gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()]
   104  	require.Equal(apricotBlk, gotBlkState.statelessBlock)
   105  	require.Equal(timestamp, gotBlkState.timestamp)
   106  
   107  	// Assert that the expected tx statuses are set.
   108  	_, gotStatus, err := gotBlkState.onCommitState.GetTx(tx.ID())
   109  	require.NoError(err)
   110  	require.Equal(status.Committed, gotStatus)
   111  
   112  	_, gotStatus, err = gotBlkState.onAbortState.GetTx(tx.ID())
   113  	require.NoError(err)
   114  	require.Equal(status.Aborted, gotStatus)
   115  
   116  	// Visiting again should return nil without using dependencies.
   117  	require.NoError(blk.Verify(context.Background()))
   118  }
   119  
   120  func TestVerifierVisitAtomicBlock(t *testing.T) {
   121  	require := require.New(t)
   122  	ctrl := gomock.NewController(t)
   123  
   124  	// Create mocked dependencies.
   125  	s := state.NewMockState(ctrl)
   126  	mempool := mempool.NewMockMempool(ctrl)
   127  	parentID := ids.GenerateTestID()
   128  	parentStatelessBlk := block.NewMockBlock(ctrl)
   129  	grandparentID := ids.GenerateTestID()
   130  	parentState := state.NewMockDiff(ctrl)
   131  
   132  	backend := &backend{
   133  		blkIDToState: map[ids.ID]*blockState{
   134  			parentID: {
   135  				statelessBlock: parentStatelessBlk,
   136  				onAcceptState:  parentState,
   137  			},
   138  		},
   139  		Mempool: mempool,
   140  		state:   s,
   141  		ctx: &snow.Context{
   142  			Log: logging.NoLog{},
   143  		},
   144  	}
   145  	verifier := &verifier{
   146  		txExecutorBackend: &executor.Backend{
   147  			Config: &config.Config{
   148  				UpgradeConfig: upgrade.Config{
   149  					ApricotPhase5Time: time.Now().Add(time.Hour),
   150  					BanffTime:         mockable.MaxTime, // banff is not activated
   151  				},
   152  			},
   153  			Clk: &mockable.Clock{},
   154  		},
   155  		backend: backend,
   156  	}
   157  	manager := &manager{
   158  		backend:  backend,
   159  		verifier: verifier,
   160  	}
   161  
   162  	onAccept := state.NewMockDiff(ctrl)
   163  	blkTx := txs.NewMockUnsignedTx(ctrl)
   164  	inputs := set.Of(ids.GenerateTestID())
   165  	blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.AtomicTxExecutor{})).DoAndReturn(
   166  		func(e *executor.AtomicTxExecutor) error {
   167  			e.OnAccept = onAccept
   168  			e.Inputs = inputs
   169  			return nil
   170  		},
   171  	).Times(1)
   172  
   173  	// We can't serialize [blkTx] because it isn't registered with blocks.Codec.
   174  	// Serialize this block with a dummy tx and replace it after creation with
   175  	// the mock tx.
   176  	// TODO allow serialization of mock txs.
   177  	apricotBlk, err := block.NewApricotAtomicBlock(
   178  		parentID,
   179  		2,
   180  		&txs.Tx{
   181  			Unsigned: &txs.AdvanceTimeTx{},
   182  			Creds:    []verify.Verifiable{},
   183  		},
   184  	)
   185  	require.NoError(err)
   186  	apricotBlk.Tx.Unsigned = blkTx
   187  
   188  	// Set expectations for dependencies.
   189  	timestamp := time.Now()
   190  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   191  	parentStatelessBlk.EXPECT().Parent().Return(grandparentID).Times(1)
   192  	mempool.EXPECT().Remove([]*txs.Tx{apricotBlk.Tx}).Times(1)
   193  	onAccept.EXPECT().AddTx(apricotBlk.Tx, status.Committed).Times(1)
   194  	onAccept.EXPECT().GetTimestamp().Return(timestamp).Times(1)
   195  
   196  	blk := manager.NewBlock(apricotBlk)
   197  	require.NoError(blk.Verify(context.Background()))
   198  
   199  	require.Contains(verifier.backend.blkIDToState, apricotBlk.ID())
   200  	gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()]
   201  	require.Equal(apricotBlk, gotBlkState.statelessBlock)
   202  	require.Equal(onAccept, gotBlkState.onAcceptState)
   203  	require.Equal(inputs, gotBlkState.inputs)
   204  	require.Equal(timestamp, gotBlkState.timestamp)
   205  
   206  	// Visiting again should return nil without using dependencies.
   207  	require.NoError(blk.Verify(context.Background()))
   208  }
   209  
   210  func TestVerifierVisitStandardBlock(t *testing.T) {
   211  	require := require.New(t)
   212  	ctrl := gomock.NewController(t)
   213  
   214  	// Create mocked dependencies.
   215  	s := state.NewMockState(ctrl)
   216  	mempool := mempool.NewMockMempool(ctrl)
   217  	parentID := ids.GenerateTestID()
   218  	parentStatelessBlk := block.NewMockBlock(ctrl)
   219  	parentState := state.NewMockDiff(ctrl)
   220  
   221  	backend := &backend{
   222  		blkIDToState: map[ids.ID]*blockState{
   223  			parentID: {
   224  				statelessBlock: parentStatelessBlk,
   225  				onAcceptState:  parentState,
   226  			},
   227  		},
   228  		Mempool: mempool,
   229  		state:   s,
   230  		ctx: &snow.Context{
   231  			Log: logging.NoLog{},
   232  		},
   233  	}
   234  	verifier := &verifier{
   235  		txExecutorBackend: &executor.Backend{
   236  			Config: &config.Config{
   237  				UpgradeConfig: upgrade.Config{
   238  					ApricotPhase5Time: time.Now().Add(time.Hour),
   239  					BanffTime:         mockable.MaxTime, // banff is not activated
   240  				},
   241  			},
   242  			Clk: &mockable.Clock{},
   243  		},
   244  		backend: backend,
   245  	}
   246  	manager := &manager{
   247  		backend:  backend,
   248  		verifier: verifier,
   249  	}
   250  
   251  	blkTx := txs.NewMockUnsignedTx(ctrl)
   252  	atomicRequests := map[ids.ID]*atomic.Requests{
   253  		ids.GenerateTestID(): {
   254  			RemoveRequests: [][]byte{{1}, {2}},
   255  			PutRequests: []*atomic.Element{
   256  				{
   257  					Key:    []byte{3},
   258  					Value:  []byte{4},
   259  					Traits: [][]byte{{5}, {6}},
   260  				},
   261  			},
   262  		},
   263  	}
   264  	blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.StandardTxExecutor{})).DoAndReturn(
   265  		func(e *executor.StandardTxExecutor) error {
   266  			e.OnAccept = func() {}
   267  			e.Inputs = set.Set[ids.ID]{}
   268  			e.AtomicRequests = atomicRequests
   269  			return nil
   270  		},
   271  	).Times(1)
   272  
   273  	// We can't serialize [blkTx] because it isn't
   274  	// registered with the blocks.Codec.
   275  	// Serialize this block with a dummy tx
   276  	// and replace it after creation with the mock tx.
   277  	// TODO allow serialization of mock txs.
   278  	apricotBlk, err := block.NewApricotStandardBlock(
   279  		parentID,
   280  		2, /*height*/
   281  		[]*txs.Tx{
   282  			{
   283  				Unsigned: &txs.AdvanceTimeTx{},
   284  				Creds:    []verify.Verifiable{},
   285  			},
   286  		},
   287  	)
   288  	require.NoError(err)
   289  	apricotBlk.Transactions[0].Unsigned = blkTx
   290  
   291  	// Set expectations for dependencies.
   292  	timestamp := time.Now()
   293  	parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1)
   294  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   295  	mempool.EXPECT().Remove(apricotBlk.Txs()).Times(1)
   296  
   297  	blk := manager.NewBlock(apricotBlk)
   298  	require.NoError(blk.Verify(context.Background()))
   299  
   300  	// Assert expected state.
   301  	require.Contains(verifier.backend.blkIDToState, apricotBlk.ID())
   302  	gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()]
   303  	require.Equal(apricotBlk, gotBlkState.statelessBlock)
   304  	require.Equal(set.Set[ids.ID]{}, gotBlkState.inputs)
   305  	require.Equal(timestamp, gotBlkState.timestamp)
   306  
   307  	// Visiting again should return nil without using dependencies.
   308  	require.NoError(blk.Verify(context.Background()))
   309  }
   310  
   311  func TestVerifierVisitCommitBlock(t *testing.T) {
   312  	require := require.New(t)
   313  	ctrl := gomock.NewController(t)
   314  
   315  	// Create mocked dependencies.
   316  	s := state.NewMockState(ctrl)
   317  	mempool := mempool.NewMockMempool(ctrl)
   318  	parentID := ids.GenerateTestID()
   319  	parentStatelessBlk := block.NewMockBlock(ctrl)
   320  	parentOnDecisionState := state.NewMockDiff(ctrl)
   321  	parentOnCommitState := state.NewMockDiff(ctrl)
   322  	parentOnAbortState := state.NewMockDiff(ctrl)
   323  
   324  	backend := &backend{
   325  		blkIDToState: map[ids.ID]*blockState{
   326  			parentID: {
   327  				statelessBlock: parentStatelessBlk,
   328  				proposalBlockState: proposalBlockState{
   329  					onDecisionState: parentOnDecisionState,
   330  					onCommitState:   parentOnCommitState,
   331  					onAbortState:    parentOnAbortState,
   332  				},
   333  			},
   334  		},
   335  		Mempool: mempool,
   336  		state:   s,
   337  		ctx: &snow.Context{
   338  			Log: logging.NoLog{},
   339  		},
   340  	}
   341  	verifier := &verifier{
   342  		txExecutorBackend: &executor.Backend{
   343  			Config: &config.Config{
   344  				UpgradeConfig: upgrade.Config{
   345  					BanffTime: mockable.MaxTime, // banff is not activated
   346  				},
   347  			},
   348  			Clk: &mockable.Clock{},
   349  		},
   350  		backend: backend,
   351  	}
   352  	manager := &manager{
   353  		backend:  backend,
   354  		verifier: verifier,
   355  	}
   356  
   357  	apricotBlk, err := block.NewApricotCommitBlock(
   358  		parentID,
   359  		2,
   360  	)
   361  	require.NoError(err)
   362  
   363  	// Set expectations for dependencies.
   364  	timestamp := time.Now()
   365  	gomock.InOrder(
   366  		parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1),
   367  		parentOnCommitState.EXPECT().GetTimestamp().Return(timestamp).Times(1),
   368  	)
   369  
   370  	// Verify the block.
   371  	blk := manager.NewBlock(apricotBlk)
   372  	require.NoError(blk.Verify(context.Background()))
   373  
   374  	// Assert expected state.
   375  	require.Contains(verifier.backend.blkIDToState, apricotBlk.ID())
   376  	gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()]
   377  	require.Equal(parentOnAbortState, gotBlkState.onAcceptState)
   378  	require.Equal(timestamp, gotBlkState.timestamp)
   379  
   380  	// Visiting again should return nil without using dependencies.
   381  	require.NoError(blk.Verify(context.Background()))
   382  }
   383  
   384  func TestVerifierVisitAbortBlock(t *testing.T) {
   385  	require := require.New(t)
   386  	ctrl := gomock.NewController(t)
   387  
   388  	// Create mocked dependencies.
   389  	s := state.NewMockState(ctrl)
   390  	mempool := mempool.NewMockMempool(ctrl)
   391  	parentID := ids.GenerateTestID()
   392  	parentStatelessBlk := block.NewMockBlock(ctrl)
   393  	parentOnDecisionState := state.NewMockDiff(ctrl)
   394  	parentOnCommitState := state.NewMockDiff(ctrl)
   395  	parentOnAbortState := state.NewMockDiff(ctrl)
   396  
   397  	backend := &backend{
   398  		blkIDToState: map[ids.ID]*blockState{
   399  			parentID: {
   400  				statelessBlock: parentStatelessBlk,
   401  				proposalBlockState: proposalBlockState{
   402  					onDecisionState: parentOnDecisionState,
   403  					onCommitState:   parentOnCommitState,
   404  					onAbortState:    parentOnAbortState,
   405  				},
   406  			},
   407  		},
   408  		Mempool: mempool,
   409  		state:   s,
   410  		ctx: &snow.Context{
   411  			Log: logging.NoLog{},
   412  		},
   413  	}
   414  	verifier := &verifier{
   415  		txExecutorBackend: &executor.Backend{
   416  			Config: &config.Config{
   417  				UpgradeConfig: upgrade.Config{
   418  					BanffTime: mockable.MaxTime, // banff is not activated
   419  				},
   420  			},
   421  			Clk: &mockable.Clock{},
   422  		},
   423  		backend: backend,
   424  	}
   425  	manager := &manager{
   426  		backend:  backend,
   427  		verifier: verifier,
   428  	}
   429  
   430  	apricotBlk, err := block.NewApricotAbortBlock(
   431  		parentID,
   432  		2,
   433  	)
   434  	require.NoError(err)
   435  
   436  	// Set expectations for dependencies.
   437  	timestamp := time.Now()
   438  	gomock.InOrder(
   439  		parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1),
   440  		parentOnAbortState.EXPECT().GetTimestamp().Return(timestamp).Times(1),
   441  	)
   442  
   443  	// Verify the block.
   444  	blk := manager.NewBlock(apricotBlk)
   445  	require.NoError(blk.Verify(context.Background()))
   446  
   447  	// Assert expected state.
   448  	require.Contains(verifier.backend.blkIDToState, apricotBlk.ID())
   449  	gotBlkState := verifier.backend.blkIDToState[apricotBlk.ID()]
   450  	require.Equal(parentOnAbortState, gotBlkState.onAcceptState)
   451  	require.Equal(timestamp, gotBlkState.timestamp)
   452  
   453  	// Visiting again should return nil without using dependencies.
   454  	require.NoError(blk.Verify(context.Background()))
   455  }
   456  
   457  // Assert that a block with an unverified parent fails verification.
   458  func TestVerifyUnverifiedParent(t *testing.T) {
   459  	require := require.New(t)
   460  	ctrl := gomock.NewController(t)
   461  
   462  	// Create mocked dependencies.
   463  	s := state.NewMockState(ctrl)
   464  	mempool := mempool.NewMockMempool(ctrl)
   465  	parentID := ids.GenerateTestID()
   466  
   467  	backend := &backend{
   468  		blkIDToState: map[ids.ID]*blockState{},
   469  		Mempool:      mempool,
   470  		state:        s,
   471  		ctx: &snow.Context{
   472  			Log: logging.NoLog{},
   473  		},
   474  	}
   475  	verifier := &verifier{
   476  		txExecutorBackend: &executor.Backend{
   477  			Config: &config.Config{
   478  				UpgradeConfig: upgrade.Config{
   479  					BanffTime: mockable.MaxTime, // banff is not activated
   480  				},
   481  			},
   482  			Clk: &mockable.Clock{},
   483  		},
   484  		backend: backend,
   485  	}
   486  
   487  	blk, err := block.NewApricotAbortBlock(parentID /*not in memory or persisted state*/, 2 /*height*/)
   488  	require.NoError(err)
   489  
   490  	// Set expectations for dependencies.
   491  	s.EXPECT().GetTimestamp().Return(time.Now()).Times(1)
   492  	s.EXPECT().GetStatelessBlock(parentID).Return(nil, database.ErrNotFound).Times(1)
   493  
   494  	// Verify the block.
   495  	err = blk.Visit(verifier)
   496  	require.ErrorIs(err, database.ErrNotFound)
   497  }
   498  
   499  func TestBanffAbortBlockTimestampChecks(t *testing.T) {
   500  	ctrl := gomock.NewController(t)
   501  
   502  	now := defaultGenesisTime.Add(time.Hour)
   503  
   504  	tests := []struct {
   505  		description string
   506  		parentTime  time.Time
   507  		childTime   time.Time
   508  		result      error
   509  	}{
   510  		{
   511  			description: "abort block timestamp matching parent's one",
   512  			parentTime:  now,
   513  			childTime:   now,
   514  			result:      nil,
   515  		},
   516  		{
   517  			description: "abort block timestamp before parent's one",
   518  			childTime:   now.Add(-1 * time.Second),
   519  			parentTime:  now,
   520  			result:      errOptionBlockTimestampNotMatchingParent,
   521  		},
   522  		{
   523  			description: "abort block timestamp after parent's one",
   524  			parentTime:  now,
   525  			childTime:   now.Add(time.Second),
   526  			result:      errOptionBlockTimestampNotMatchingParent,
   527  		},
   528  	}
   529  
   530  	for _, test := range tests {
   531  		t.Run(test.description, func(t *testing.T) {
   532  			require := require.New(t)
   533  
   534  			// Create mocked dependencies.
   535  			s := state.NewMockState(ctrl)
   536  			mempool := mempool.NewMockMempool(ctrl)
   537  			parentID := ids.GenerateTestID()
   538  			parentStatelessBlk := block.NewMockBlock(ctrl)
   539  			parentHeight := uint64(1)
   540  
   541  			backend := &backend{
   542  				blkIDToState: make(map[ids.ID]*blockState),
   543  				Mempool:      mempool,
   544  				state:        s,
   545  				ctx: &snow.Context{
   546  					Log: logging.NoLog{},
   547  				},
   548  			}
   549  			verifier := &verifier{
   550  				txExecutorBackend: &executor.Backend{
   551  					Config: &config.Config{
   552  						UpgradeConfig: upgrade.Config{
   553  							BanffTime: time.Time{}, // banff is activated
   554  						},
   555  					},
   556  					Clk: &mockable.Clock{},
   557  				},
   558  				backend: backend,
   559  			}
   560  
   561  			// build and verify child block
   562  			childHeight := parentHeight + 1
   563  			statelessAbortBlk, err := block.NewBanffAbortBlock(test.childTime, parentID, childHeight)
   564  			require.NoError(err)
   565  
   566  			// setup parent state
   567  			parentTime := defaultGenesisTime
   568  			s.EXPECT().GetLastAccepted().Return(parentID).Times(3)
   569  			s.EXPECT().GetTimestamp().Return(parentTime).Times(3)
   570  
   571  			onDecisionState, err := state.NewDiff(parentID, backend)
   572  			require.NoError(err)
   573  			onCommitState, err := state.NewDiff(parentID, backend)
   574  			require.NoError(err)
   575  			onAbortState, err := state.NewDiff(parentID, backend)
   576  			require.NoError(err)
   577  			backend.blkIDToState[parentID] = &blockState{
   578  				timestamp:      test.parentTime,
   579  				statelessBlock: parentStatelessBlk,
   580  				proposalBlockState: proposalBlockState{
   581  					onDecisionState: onDecisionState,
   582  					onCommitState:   onCommitState,
   583  					onAbortState:    onAbortState,
   584  				},
   585  			}
   586  
   587  			// Set expectations for dependencies.
   588  			parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   589  
   590  			err = statelessAbortBlk.Visit(verifier)
   591  			require.ErrorIs(err, test.result)
   592  		})
   593  	}
   594  }
   595  
   596  // TODO combine with TestApricotCommitBlockTimestampChecks
   597  func TestBanffCommitBlockTimestampChecks(t *testing.T) {
   598  	ctrl := gomock.NewController(t)
   599  
   600  	now := defaultGenesisTime.Add(time.Hour)
   601  
   602  	tests := []struct {
   603  		description string
   604  		parentTime  time.Time
   605  		childTime   time.Time
   606  		result      error
   607  	}{
   608  		{
   609  			description: "commit block timestamp matching parent's one",
   610  			parentTime:  now,
   611  			childTime:   now,
   612  			result:      nil,
   613  		},
   614  		{
   615  			description: "commit block timestamp before parent's one",
   616  			childTime:   now.Add(-1 * time.Second),
   617  			parentTime:  now,
   618  			result:      errOptionBlockTimestampNotMatchingParent,
   619  		},
   620  		{
   621  			description: "commit block timestamp after parent's one",
   622  			parentTime:  now,
   623  			childTime:   now.Add(time.Second),
   624  			result:      errOptionBlockTimestampNotMatchingParent,
   625  		},
   626  	}
   627  
   628  	for _, test := range tests {
   629  		t.Run(test.description, func(t *testing.T) {
   630  			require := require.New(t)
   631  
   632  			// Create mocked dependencies.
   633  			s := state.NewMockState(ctrl)
   634  			mempool := mempool.NewMockMempool(ctrl)
   635  			parentID := ids.GenerateTestID()
   636  			parentStatelessBlk := block.NewMockBlock(ctrl)
   637  			parentHeight := uint64(1)
   638  
   639  			backend := &backend{
   640  				blkIDToState: make(map[ids.ID]*blockState),
   641  				Mempool:      mempool,
   642  				state:        s,
   643  				ctx: &snow.Context{
   644  					Log: logging.NoLog{},
   645  				},
   646  			}
   647  			verifier := &verifier{
   648  				txExecutorBackend: &executor.Backend{
   649  					Config: &config.Config{
   650  						UpgradeConfig: upgrade.Config{
   651  							BanffTime: time.Time{}, // banff is activated
   652  						},
   653  					},
   654  					Clk: &mockable.Clock{},
   655  				},
   656  				backend: backend,
   657  			}
   658  
   659  			// build and verify child block
   660  			childHeight := parentHeight + 1
   661  			statelessCommitBlk, err := block.NewBanffCommitBlock(test.childTime, parentID, childHeight)
   662  			require.NoError(err)
   663  
   664  			// setup parent state
   665  			parentTime := defaultGenesisTime
   666  			s.EXPECT().GetLastAccepted().Return(parentID).Times(3)
   667  			s.EXPECT().GetTimestamp().Return(parentTime).Times(3)
   668  
   669  			onDecisionState, err := state.NewDiff(parentID, backend)
   670  			require.NoError(err)
   671  			onCommitState, err := state.NewDiff(parentID, backend)
   672  			require.NoError(err)
   673  			onAbortState, err := state.NewDiff(parentID, backend)
   674  			require.NoError(err)
   675  			backend.blkIDToState[parentID] = &blockState{
   676  				timestamp:      test.parentTime,
   677  				statelessBlock: parentStatelessBlk,
   678  				proposalBlockState: proposalBlockState{
   679  					onDecisionState: onDecisionState,
   680  					onCommitState:   onCommitState,
   681  					onAbortState:    onAbortState,
   682  				},
   683  			}
   684  
   685  			// Set expectations for dependencies.
   686  			parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   687  
   688  			err = statelessCommitBlk.Visit(verifier)
   689  			require.ErrorIs(err, test.result)
   690  		})
   691  	}
   692  }
   693  
   694  func TestVerifierVisitStandardBlockWithDuplicateInputs(t *testing.T) {
   695  	require := require.New(t)
   696  	ctrl := gomock.NewController(t)
   697  
   698  	// Create mocked dependencies.
   699  	s := state.NewMockState(ctrl)
   700  	mempool := mempool.NewMockMempool(ctrl)
   701  
   702  	grandParentID := ids.GenerateTestID()
   703  	grandParentStatelessBlk := block.NewMockBlock(ctrl)
   704  	grandParentState := state.NewMockDiff(ctrl)
   705  	parentID := ids.GenerateTestID()
   706  	parentStatelessBlk := block.NewMockBlock(ctrl)
   707  	parentState := state.NewMockDiff(ctrl)
   708  	atomicInputs := set.Of(ids.GenerateTestID())
   709  
   710  	backend := &backend{
   711  		blkIDToState: map[ids.ID]*blockState{
   712  			grandParentID: {
   713  				statelessBlock: grandParentStatelessBlk,
   714  				onAcceptState:  grandParentState,
   715  				inputs:         atomicInputs,
   716  			},
   717  			parentID: {
   718  				statelessBlock: parentStatelessBlk,
   719  				onAcceptState:  parentState,
   720  			},
   721  		},
   722  		Mempool: mempool,
   723  		state:   s,
   724  		ctx: &snow.Context{
   725  			Log: logging.NoLog{},
   726  		},
   727  	}
   728  	verifier := &verifier{
   729  		txExecutorBackend: &executor.Backend{
   730  			Config: &config.Config{
   731  				UpgradeConfig: upgrade.Config{
   732  					ApricotPhase5Time: time.Now().Add(time.Hour),
   733  					BanffTime:         mockable.MaxTime, // banff is not activated
   734  				},
   735  			},
   736  			Clk: &mockable.Clock{},
   737  		},
   738  		backend: backend,
   739  	}
   740  
   741  	blkTx := txs.NewMockUnsignedTx(ctrl)
   742  	atomicRequests := map[ids.ID]*atomic.Requests{
   743  		ids.GenerateTestID(): {
   744  			RemoveRequests: [][]byte{{1}, {2}},
   745  			PutRequests: []*atomic.Element{
   746  				{
   747  					Key:    []byte{3},
   748  					Value:  []byte{4},
   749  					Traits: [][]byte{{5}, {6}},
   750  				},
   751  			},
   752  		},
   753  	}
   754  	blkTx.EXPECT().Visit(gomock.AssignableToTypeOf(&executor.StandardTxExecutor{})).DoAndReturn(
   755  		func(e *executor.StandardTxExecutor) error {
   756  			e.OnAccept = func() {}
   757  			e.Inputs = atomicInputs
   758  			e.AtomicRequests = atomicRequests
   759  			return nil
   760  		},
   761  	).Times(1)
   762  
   763  	// We can't serialize [blkTx] because it isn't
   764  	// registered with the blocks.Codec.
   765  	// Serialize this block with a dummy tx
   766  	// and replace it after creation with the mock tx.
   767  	// TODO allow serialization of mock txs.
   768  	blk, err := block.NewApricotStandardBlock(
   769  		parentID,
   770  		2,
   771  		[]*txs.Tx{
   772  			{
   773  				Unsigned: &txs.AdvanceTimeTx{},
   774  				Creds:    []verify.Verifiable{},
   775  			},
   776  		},
   777  	)
   778  	require.NoError(err)
   779  	blk.Transactions[0].Unsigned = blkTx
   780  
   781  	// Set expectations for dependencies.
   782  	timestamp := time.Now()
   783  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   784  	parentState.EXPECT().GetTimestamp().Return(timestamp).Times(1)
   785  	parentStatelessBlk.EXPECT().Parent().Return(grandParentID).Times(1)
   786  
   787  	err = verifier.ApricotStandardBlock(blk)
   788  	require.ErrorIs(err, errConflictingParentTxs)
   789  }
   790  
   791  func TestVerifierVisitApricotStandardBlockWithProposalBlockParent(t *testing.T) {
   792  	require := require.New(t)
   793  	ctrl := gomock.NewController(t)
   794  
   795  	// Create mocked dependencies.
   796  	s := state.NewMockState(ctrl)
   797  	mempool := mempool.NewMockMempool(ctrl)
   798  	parentID := ids.GenerateTestID()
   799  	parentStatelessBlk := block.NewMockBlock(ctrl)
   800  	parentOnCommitState := state.NewMockDiff(ctrl)
   801  	parentOnAbortState := state.NewMockDiff(ctrl)
   802  
   803  	backend := &backend{
   804  		blkIDToState: map[ids.ID]*blockState{
   805  			parentID: {
   806  				statelessBlock: parentStatelessBlk,
   807  				proposalBlockState: proposalBlockState{
   808  					onCommitState: parentOnCommitState,
   809  					onAbortState:  parentOnAbortState,
   810  				},
   811  			},
   812  		},
   813  		Mempool: mempool,
   814  		state:   s,
   815  		ctx: &snow.Context{
   816  			Log: logging.NoLog{},
   817  		},
   818  	}
   819  	verifier := &verifier{
   820  		txExecutorBackend: &executor.Backend{
   821  			Config: &config.Config{
   822  				UpgradeConfig: upgrade.Config{
   823  					BanffTime: mockable.MaxTime, // banff is not activated
   824  				},
   825  			},
   826  			Clk: &mockable.Clock{},
   827  		},
   828  		backend: backend,
   829  	}
   830  
   831  	blk, err := block.NewApricotStandardBlock(
   832  		parentID,
   833  		2,
   834  		[]*txs.Tx{
   835  			{
   836  				Unsigned: &txs.AdvanceTimeTx{},
   837  				Creds:    []verify.Verifiable{},
   838  			},
   839  		},
   840  	)
   841  	require.NoError(err)
   842  
   843  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   844  
   845  	err = verifier.ApricotStandardBlock(blk)
   846  	require.ErrorIs(err, state.ErrMissingParentState)
   847  }
   848  
   849  func TestVerifierVisitBanffStandardBlockWithProposalBlockParent(t *testing.T) {
   850  	require := require.New(t)
   851  	ctrl := gomock.NewController(t)
   852  
   853  	// Create mocked dependencies.
   854  	s := state.NewMockState(ctrl)
   855  	mempool := mempool.NewMockMempool(ctrl)
   856  	parentID := ids.GenerateTestID()
   857  	parentStatelessBlk := block.NewMockBlock(ctrl)
   858  	parentTime := time.Now()
   859  	parentOnCommitState := state.NewMockDiff(ctrl)
   860  	parentOnAbortState := state.NewMockDiff(ctrl)
   861  
   862  	backend := &backend{
   863  		blkIDToState: map[ids.ID]*blockState{
   864  			parentID: {
   865  				statelessBlock: parentStatelessBlk,
   866  				proposalBlockState: proposalBlockState{
   867  					onCommitState: parentOnCommitState,
   868  					onAbortState:  parentOnAbortState,
   869  				},
   870  			},
   871  		},
   872  		Mempool: mempool,
   873  		state:   s,
   874  		ctx: &snow.Context{
   875  			Log: logging.NoLog{},
   876  		},
   877  	}
   878  	verifier := &verifier{
   879  		txExecutorBackend: &executor.Backend{
   880  			Config: &config.Config{
   881  				UpgradeConfig: upgrade.Config{
   882  					BanffTime: time.Time{}, // banff is activated
   883  				},
   884  			},
   885  			Clk: &mockable.Clock{},
   886  		},
   887  		backend: backend,
   888  	}
   889  
   890  	blk, err := block.NewBanffStandardBlock(
   891  		parentTime.Add(time.Second),
   892  		parentID,
   893  		2,
   894  		[]*txs.Tx{
   895  			{
   896  				Unsigned: &txs.AdvanceTimeTx{},
   897  				Creds:    []verify.Verifiable{},
   898  			},
   899  		},
   900  	)
   901  	require.NoError(err)
   902  
   903  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   904  
   905  	err = verifier.BanffStandardBlock(blk)
   906  	require.ErrorIs(err, state.ErrMissingParentState)
   907  }
   908  
   909  func TestVerifierVisitApricotCommitBlockUnexpectedParentState(t *testing.T) {
   910  	require := require.New(t)
   911  	ctrl := gomock.NewController(t)
   912  
   913  	// Create mocked dependencies.
   914  	s := state.NewMockState(ctrl)
   915  	parentID := ids.GenerateTestID()
   916  	parentStatelessBlk := block.NewMockBlock(ctrl)
   917  	verifier := &verifier{
   918  		txExecutorBackend: &executor.Backend{
   919  			Config: &config.Config{
   920  				UpgradeConfig: upgrade.Config{
   921  					BanffTime: mockable.MaxTime, // banff is not activated
   922  				},
   923  			},
   924  			Clk: &mockable.Clock{},
   925  		},
   926  		backend: &backend{
   927  			blkIDToState: map[ids.ID]*blockState{
   928  				parentID: {
   929  					statelessBlock: parentStatelessBlk,
   930  				},
   931  			},
   932  			state: s,
   933  			ctx: &snow.Context{
   934  				Log: logging.NoLog{},
   935  			},
   936  		},
   937  	}
   938  
   939  	blk, err := block.NewApricotCommitBlock(
   940  		parentID,
   941  		2,
   942  	)
   943  	require.NoError(err)
   944  
   945  	// Set expectations for dependencies.
   946  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   947  
   948  	// Verify the block.
   949  	err = verifier.ApricotCommitBlock(blk)
   950  	require.ErrorIs(err, state.ErrMissingParentState)
   951  }
   952  
   953  func TestVerifierVisitBanffCommitBlockUnexpectedParentState(t *testing.T) {
   954  	require := require.New(t)
   955  	ctrl := gomock.NewController(t)
   956  
   957  	// Create mocked dependencies.
   958  	s := state.NewMockState(ctrl)
   959  	parentID := ids.GenerateTestID()
   960  	parentStatelessBlk := block.NewMockBlock(ctrl)
   961  	timestamp := time.Unix(12345, 0)
   962  	verifier := &verifier{
   963  		txExecutorBackend: &executor.Backend{
   964  			Config: &config.Config{
   965  				UpgradeConfig: upgrade.Config{
   966  					BanffTime: time.Time{}, // banff is activated
   967  				},
   968  			},
   969  			Clk: &mockable.Clock{},
   970  		},
   971  		backend: &backend{
   972  			blkIDToState: map[ids.ID]*blockState{
   973  				parentID: {
   974  					statelessBlock: parentStatelessBlk,
   975  					timestamp:      timestamp,
   976  				},
   977  			},
   978  			state: s,
   979  			ctx: &snow.Context{
   980  				Log: logging.NoLog{},
   981  			},
   982  		},
   983  	}
   984  
   985  	blk, err := block.NewBanffCommitBlock(
   986  		timestamp,
   987  		parentID,
   988  		2,
   989  	)
   990  	require.NoError(err)
   991  
   992  	// Set expectations for dependencies.
   993  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
   994  
   995  	// Verify the block.
   996  	err = verifier.BanffCommitBlock(blk)
   997  	require.ErrorIs(err, state.ErrMissingParentState)
   998  }
   999  
  1000  func TestVerifierVisitApricotAbortBlockUnexpectedParentState(t *testing.T) {
  1001  	require := require.New(t)
  1002  	ctrl := gomock.NewController(t)
  1003  
  1004  	// Create mocked dependencies.
  1005  	s := state.NewMockState(ctrl)
  1006  	parentID := ids.GenerateTestID()
  1007  	parentStatelessBlk := block.NewMockBlock(ctrl)
  1008  	verifier := &verifier{
  1009  		txExecutorBackend: &executor.Backend{
  1010  			Config: &config.Config{
  1011  				UpgradeConfig: upgrade.Config{
  1012  					BanffTime: mockable.MaxTime, // banff is not activated
  1013  				},
  1014  			},
  1015  			Clk: &mockable.Clock{},
  1016  		},
  1017  		backend: &backend{
  1018  			blkIDToState: map[ids.ID]*blockState{
  1019  				parentID: {
  1020  					statelessBlock: parentStatelessBlk,
  1021  				},
  1022  			},
  1023  			state: s,
  1024  			ctx: &snow.Context{
  1025  				Log: logging.NoLog{},
  1026  			},
  1027  		},
  1028  	}
  1029  
  1030  	blk, err := block.NewApricotAbortBlock(
  1031  		parentID,
  1032  		2,
  1033  	)
  1034  	require.NoError(err)
  1035  
  1036  	// Set expectations for dependencies.
  1037  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
  1038  
  1039  	// Verify the block.
  1040  	err = verifier.ApricotAbortBlock(blk)
  1041  	require.ErrorIs(err, state.ErrMissingParentState)
  1042  }
  1043  
  1044  func TestVerifierVisitBanffAbortBlockUnexpectedParentState(t *testing.T) {
  1045  	require := require.New(t)
  1046  	ctrl := gomock.NewController(t)
  1047  
  1048  	// Create mocked dependencies.
  1049  	s := state.NewMockState(ctrl)
  1050  	parentID := ids.GenerateTestID()
  1051  	parentStatelessBlk := block.NewMockBlock(ctrl)
  1052  	timestamp := time.Unix(12345, 0)
  1053  	verifier := &verifier{
  1054  		txExecutorBackend: &executor.Backend{
  1055  			Config: &config.Config{
  1056  				UpgradeConfig: upgrade.Config{
  1057  					BanffTime: time.Time{}, // banff is activated
  1058  				},
  1059  			},
  1060  			Clk: &mockable.Clock{},
  1061  		},
  1062  		backend: &backend{
  1063  			blkIDToState: map[ids.ID]*blockState{
  1064  				parentID: {
  1065  					statelessBlock: parentStatelessBlk,
  1066  					timestamp:      timestamp,
  1067  				},
  1068  			},
  1069  			state: s,
  1070  			ctx: &snow.Context{
  1071  				Log: logging.NoLog{},
  1072  			},
  1073  		},
  1074  	}
  1075  
  1076  	blk, err := block.NewBanffAbortBlock(
  1077  		timestamp,
  1078  		parentID,
  1079  		2,
  1080  	)
  1081  	require.NoError(err)
  1082  
  1083  	// Set expectations for dependencies.
  1084  	parentStatelessBlk.EXPECT().Height().Return(uint64(1)).Times(1)
  1085  
  1086  	// Verify the block.
  1087  	err = verifier.BanffAbortBlock(blk)
  1088  	require.ErrorIs(err, state.ErrMissingParentState)
  1089  }