github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/block/executor/manager_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  	"errors"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/mock/gomock"
    13  
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/utils/set"
    16  	"github.com/MetalBlockchain/metalgo/vms/avm/block"
    17  	"github.com/MetalBlockchain/metalgo/vms/avm/state"
    18  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    19  )
    20  
    21  var (
    22  	errTest                    = errors.New("test error")
    23  	errTestSyntacticVerifyFail = errors.New("test error")
    24  	errTestSemanticVerifyFail  = errors.New("test error")
    25  	errTestExecutionFail       = errors.New("test error")
    26  )
    27  
    28  func TestManagerGetStatelessBlock(t *testing.T) {
    29  	require := require.New(t)
    30  	ctrl := gomock.NewController(t)
    31  
    32  	state := state.NewMockState(ctrl)
    33  	m := &manager{
    34  		state:        state,
    35  		blkIDToState: map[ids.ID]*blockState{},
    36  	}
    37  
    38  	// Case: block is in memory
    39  	{
    40  		statelessBlk := block.NewMockBlock(ctrl)
    41  		blkID := ids.GenerateTestID()
    42  		blk := &blockState{
    43  			statelessBlock: statelessBlk,
    44  		}
    45  		m.blkIDToState[blkID] = blk
    46  		gotBlk, err := m.GetStatelessBlock(blkID)
    47  		require.NoError(err)
    48  		require.Equal(statelessBlk, gotBlk)
    49  	}
    50  
    51  	// Case: block isn't in memory
    52  	{
    53  		blkID := ids.GenerateTestID()
    54  		blk := block.NewMockBlock(ctrl)
    55  		state.EXPECT().GetBlock(blkID).Return(blk, nil)
    56  		gotBlk, err := m.GetStatelessBlock(blkID)
    57  		require.NoError(err)
    58  		require.Equal(blk, gotBlk)
    59  	}
    60  
    61  	// Case: error while getting block from state
    62  	{
    63  		blkID := ids.GenerateTestID()
    64  		state.EXPECT().GetBlock(blkID).Return(nil, errTest)
    65  		_, err := m.GetStatelessBlock(blkID)
    66  		require.ErrorIs(err, errTest)
    67  	}
    68  }
    69  
    70  func TestManagerGetState(t *testing.T) {
    71  	require := require.New(t)
    72  	ctrl := gomock.NewController(t)
    73  
    74  	s := state.NewMockState(ctrl)
    75  	m := &manager{
    76  		state:        s,
    77  		blkIDToState: map[ids.ID]*blockState{},
    78  		lastAccepted: ids.GenerateTestID(),
    79  	}
    80  
    81  	// Case: Block is in memory
    82  	{
    83  		diff := state.NewMockDiff(ctrl)
    84  		blkID := ids.GenerateTestID()
    85  		m.blkIDToState[blkID] = &blockState{
    86  			onAcceptState: diff,
    87  		}
    88  		gotState, ok := m.GetState(blkID)
    89  		require.True(ok)
    90  		require.Equal(diff, gotState)
    91  	}
    92  
    93  	// Case: Block isn't in memory; block isn't last accepted
    94  	{
    95  		blkID := ids.GenerateTestID()
    96  		gotState, ok := m.GetState(blkID)
    97  		require.False(ok)
    98  		require.Equal(s, gotState)
    99  	}
   100  
   101  	// Case: Block isn't in memory; block is last accepted
   102  	{
   103  		gotState, ok := m.GetState(m.lastAccepted)
   104  		require.True(ok)
   105  		require.Equal(s, gotState)
   106  	}
   107  }
   108  
   109  func TestManagerVerifyTx(t *testing.T) {
   110  	type test struct {
   111  		name        string
   112  		txF         func(*gomock.Controller) *txs.Tx
   113  		managerF    func(*gomock.Controller) *manager
   114  		expectedErr error
   115  	}
   116  
   117  	tests := []test{
   118  		{
   119  			name: "not bootstrapped",
   120  			txF: func(*gomock.Controller) *txs.Tx {
   121  				return &txs.Tx{}
   122  			},
   123  			managerF: func(*gomock.Controller) *manager {
   124  				return &manager{
   125  					backend: defaultTestBackend(false, nil),
   126  				}
   127  			},
   128  			expectedErr: ErrChainNotSynced,
   129  		},
   130  		{
   131  			name: "fails syntactic verification",
   132  			txF: func(ctrl *gomock.Controller) *txs.Tx {
   133  				unsigned := txs.NewMockUnsignedTx(ctrl)
   134  				unsigned.EXPECT().Visit(gomock.Any()).Return(errTestSyntacticVerifyFail)
   135  				return &txs.Tx{
   136  					Unsigned: unsigned,
   137  				}
   138  			},
   139  			managerF: func(*gomock.Controller) *manager {
   140  				return &manager{
   141  					backend: defaultTestBackend(true, nil),
   142  				}
   143  			},
   144  			expectedErr: errTestSyntacticVerifyFail,
   145  		},
   146  		{
   147  			name: "fails semantic verification",
   148  			txF: func(ctrl *gomock.Controller) *txs.Tx {
   149  				unsigned := txs.NewMockUnsignedTx(ctrl)
   150  				// Syntactic verification passes
   151  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   152  				// Semantic verification fails
   153  				unsigned.EXPECT().Visit(gomock.Any()).Return(errTestSemanticVerifyFail)
   154  				return &txs.Tx{
   155  					Unsigned: unsigned,
   156  				}
   157  			},
   158  			managerF: func(ctrl *gomock.Controller) *manager {
   159  				lastAcceptedID := ids.GenerateTestID()
   160  
   161  				// These values don't matter for this test
   162  				state := state.NewMockState(ctrl)
   163  				state.EXPECT().GetLastAccepted().Return(lastAcceptedID)
   164  				state.EXPECT().GetTimestamp().Return(time.Time{})
   165  
   166  				return &manager{
   167  					backend:      defaultTestBackend(true, nil),
   168  					state:        state,
   169  					lastAccepted: lastAcceptedID,
   170  				}
   171  			},
   172  			expectedErr: errTestSemanticVerifyFail,
   173  		},
   174  		{
   175  			name: "fails execution",
   176  			txF: func(ctrl *gomock.Controller) *txs.Tx {
   177  				unsigned := txs.NewMockUnsignedTx(ctrl)
   178  				// Syntactic verification passes
   179  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   180  				// Semantic verification passes
   181  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   182  				// Execution fails
   183  				unsigned.EXPECT().Visit(gomock.Any()).Return(errTestExecutionFail)
   184  				return &txs.Tx{
   185  					Unsigned: unsigned,
   186  				}
   187  			},
   188  			managerF: func(ctrl *gomock.Controller) *manager {
   189  				lastAcceptedID := ids.GenerateTestID()
   190  
   191  				// These values don't matter for this test
   192  				state := state.NewMockState(ctrl)
   193  				state.EXPECT().GetLastAccepted().Return(lastAcceptedID)
   194  				state.EXPECT().GetTimestamp().Return(time.Time{})
   195  
   196  				return &manager{
   197  					backend:      defaultTestBackend(true, nil),
   198  					state:        state,
   199  					lastAccepted: lastAcceptedID,
   200  				}
   201  			},
   202  			expectedErr: errTestExecutionFail,
   203  		},
   204  		{
   205  			name: "happy path",
   206  			txF: func(ctrl *gomock.Controller) *txs.Tx {
   207  				unsigned := txs.NewMockUnsignedTx(ctrl)
   208  				// Syntactic verification passes
   209  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   210  				// Semantic verification passes
   211  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   212  				// Execution passes
   213  				unsigned.EXPECT().Visit(gomock.Any()).Return(nil)
   214  				return &txs.Tx{
   215  					Unsigned: unsigned,
   216  				}
   217  			},
   218  			managerF: func(ctrl *gomock.Controller) *manager {
   219  				lastAcceptedID := ids.GenerateTestID()
   220  
   221  				// These values don't matter for this test
   222  				state := state.NewMockState(ctrl)
   223  				state.EXPECT().GetLastAccepted().Return(lastAcceptedID)
   224  				state.EXPECT().GetTimestamp().Return(time.Time{})
   225  
   226  				return &manager{
   227  					backend:      defaultTestBackend(true, nil),
   228  					state:        state,
   229  					lastAccepted: lastAcceptedID,
   230  				}
   231  			},
   232  			expectedErr: nil,
   233  		},
   234  	}
   235  
   236  	for _, test := range tests {
   237  		t.Run(test.name, func(t *testing.T) {
   238  			require := require.New(t)
   239  			ctrl := gomock.NewController(t)
   240  
   241  			m := test.managerF(ctrl)
   242  			tx := test.txF(ctrl)
   243  			err := m.VerifyTx(tx)
   244  			require.ErrorIs(err, test.expectedErr)
   245  		})
   246  	}
   247  }
   248  
   249  func TestVerifyUniqueInputs(t *testing.T) {
   250  	require := require.New(t)
   251  	ctrl := gomock.NewController(t)
   252  
   253  	// Case: No inputs
   254  	{
   255  		m := &manager{}
   256  		require.NoError(m.VerifyUniqueInputs(ids.GenerateTestID(), set.Set[ids.ID]{}))
   257  	}
   258  
   259  	// blk0 is blk1's parent
   260  	blk0ID, blk1ID := ids.GenerateTestID(), ids.GenerateTestID()
   261  	blk0, blk1 := block.NewMockBlock(ctrl), block.NewMockBlock(ctrl)
   262  	blk1.EXPECT().Parent().Return(blk0ID).AnyTimes()
   263  	blk0.EXPECT().Parent().Return(ids.Empty).AnyTimes() // blk0's parent is accepted
   264  
   265  	inputID := ids.GenerateTestID()
   266  	m := &manager{
   267  		blkIDToState: map[ids.ID]*blockState{
   268  			blk0ID: {
   269  				statelessBlock: blk0,
   270  				importedInputs: set.Of(inputID),
   271  			},
   272  			blk1ID: {
   273  				statelessBlock: blk1,
   274  				importedInputs: set.Of(ids.GenerateTestID()),
   275  			},
   276  		},
   277  	}
   278  	// [blk1]'s parent, [blk0], has [inputID] as an input
   279  	err := m.VerifyUniqueInputs(blk1ID, set.Of(inputID))
   280  	require.ErrorIs(err, ErrConflictingParentTxs)
   281  
   282  	require.NoError(m.VerifyUniqueInputs(blk1ID, set.Of(ids.GenerateTestID())))
   283  }