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