github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/network/network_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 network
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/require"
    14  	"go.uber.org/mock/gomock"
    15  
    16  	"github.com/MetalBlockchain/metalgo/ids"
    17  	"github.com/MetalBlockchain/metalgo/snow/engine/common"
    18  	"github.com/MetalBlockchain/metalgo/snow/validators"
    19  	"github.com/MetalBlockchain/metalgo/utils/logging"
    20  	"github.com/MetalBlockchain/metalgo/vms/avm/block/executor"
    21  	"github.com/MetalBlockchain/metalgo/vms/avm/fxs"
    22  	"github.com/MetalBlockchain/metalgo/vms/avm/txs"
    23  	"github.com/MetalBlockchain/metalgo/vms/nftfx"
    24  	"github.com/MetalBlockchain/metalgo/vms/propertyfx"
    25  	"github.com/MetalBlockchain/metalgo/vms/secp256k1fx"
    26  	"github.com/MetalBlockchain/metalgo/vms/txs/mempool"
    27  
    28  	xmempool "github.com/MetalBlockchain/metalgo/vms/avm/txs/mempool"
    29  )
    30  
    31  var (
    32  	testConfig = Config{
    33  		MaxValidatorSetStaleness:                    time.Second,
    34  		TargetGossipSize:                            1,
    35  		PushGossipNumValidators:                     1,
    36  		PushGossipNumPeers:                          0,
    37  		PushRegossipNumValidators:                   1,
    38  		PushRegossipNumPeers:                        0,
    39  		PushGossipDiscardedCacheSize:                1,
    40  		PushGossipMaxRegossipFrequency:              time.Second,
    41  		PushGossipFrequency:                         time.Second,
    42  		PullGossipPollSize:                          1,
    43  		PullGossipFrequency:                         time.Second,
    44  		PullGossipThrottlingPeriod:                  time.Second,
    45  		PullGossipThrottlingLimit:                   1,
    46  		ExpectedBloomFilterElements:                 10,
    47  		ExpectedBloomFilterFalsePositiveProbability: .1,
    48  		MaxBloomFilterFalsePositiveProbability:      .5,
    49  	}
    50  
    51  	errTest = errors.New("test error")
    52  )
    53  
    54  func TestNetworkIssueTxFromRPC(t *testing.T) {
    55  	type test struct {
    56  		name           string
    57  		mempoolFunc    func(*gomock.Controller) xmempool.Mempool
    58  		txVerifierFunc func(*gomock.Controller) TxVerifier
    59  		appSenderFunc  func(*gomock.Controller) common.AppSender
    60  		expectedErr    error
    61  	}
    62  
    63  	tests := []test{
    64  		{
    65  			name: "mempool has transaction",
    66  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
    67  				mempool := xmempool.NewMockMempool(ctrl)
    68  				mempool.EXPECT().Get(gomock.Any()).Return(nil, true)
    69  				return mempool
    70  			},
    71  			expectedErr: mempool.ErrDuplicateTx,
    72  		},
    73  		{
    74  			name: "transaction marked as dropped in mempool",
    75  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
    76  				mempool := xmempool.NewMockMempool(ctrl)
    77  				mempool.EXPECT().Get(gomock.Any()).Return(nil, false)
    78  				mempool.EXPECT().GetDropReason(gomock.Any()).Return(errTest)
    79  				return mempool
    80  			},
    81  			expectedErr: errTest,
    82  		},
    83  		{
    84  			name: "transaction invalid",
    85  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
    86  				mempool := xmempool.NewMockMempool(ctrl)
    87  				mempool.EXPECT().Get(gomock.Any()).Return(nil, false)
    88  				mempool.EXPECT().GetDropReason(gomock.Any()).Return(nil)
    89  				mempool.EXPECT().MarkDropped(gomock.Any(), gomock.Any())
    90  				return mempool
    91  			},
    92  			txVerifierFunc: func(ctrl *gomock.Controller) TxVerifier {
    93  				txVerifier := executor.NewMockManager(ctrl)
    94  				txVerifier.EXPECT().VerifyTx(gomock.Any()).Return(errTest)
    95  				return txVerifier
    96  			},
    97  			expectedErr: errTest,
    98  		},
    99  		{
   100  			name: "can't add transaction to mempool",
   101  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
   102  				mempool := xmempool.NewMockMempool(ctrl)
   103  				mempool.EXPECT().Get(gomock.Any()).Return(nil, false)
   104  				mempool.EXPECT().GetDropReason(gomock.Any()).Return(nil)
   105  				mempool.EXPECT().Add(gomock.Any()).Return(errTest)
   106  				mempool.EXPECT().MarkDropped(gomock.Any(), gomock.Any())
   107  				return mempool
   108  			},
   109  			txVerifierFunc: func(ctrl *gomock.Controller) TxVerifier {
   110  				txVerifier := executor.NewMockManager(ctrl)
   111  				txVerifier.EXPECT().VerifyTx(gomock.Any()).Return(nil)
   112  				return txVerifier
   113  			},
   114  			expectedErr: errTest,
   115  		},
   116  		{
   117  			name: "happy path",
   118  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
   119  				mempool := xmempool.NewMockMempool(ctrl)
   120  				mempool.EXPECT().Get(gomock.Any()).Return(nil, false)
   121  				mempool.EXPECT().GetDropReason(gomock.Any()).Return(nil)
   122  				mempool.EXPECT().Add(gomock.Any()).Return(nil)
   123  				mempool.EXPECT().Len().Return(0)
   124  				mempool.EXPECT().RequestBuildBlock()
   125  				mempool.EXPECT().Get(gomock.Any()).Return(nil, true).Times(2)
   126  				return mempool
   127  			},
   128  			txVerifierFunc: func(ctrl *gomock.Controller) TxVerifier {
   129  				txVerifier := executor.NewMockManager(ctrl)
   130  				txVerifier.EXPECT().VerifyTx(gomock.Any()).Return(nil)
   131  				return txVerifier
   132  			},
   133  			appSenderFunc: func(ctrl *gomock.Controller) common.AppSender {
   134  				appSender := common.NewMockSender(ctrl)
   135  				appSender.EXPECT().SendAppGossip(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
   136  				return appSender
   137  			},
   138  			expectedErr: nil,
   139  		},
   140  	}
   141  
   142  	for _, tt := range tests {
   143  		t.Run(tt.name, func(t *testing.T) {
   144  			require := require.New(t)
   145  			ctrl := gomock.NewController(t)
   146  
   147  			parser, err := txs.NewParser(
   148  				[]fxs.Fx{
   149  					&secp256k1fx.Fx{},
   150  					&nftfx.Fx{},
   151  					&propertyfx.Fx{},
   152  				},
   153  			)
   154  			require.NoError(err)
   155  
   156  			mempoolFunc := func(ctrl *gomock.Controller) xmempool.Mempool {
   157  				return xmempool.NewMockMempool(ctrl)
   158  			}
   159  			if tt.mempoolFunc != nil {
   160  				mempoolFunc = tt.mempoolFunc
   161  			}
   162  
   163  			txVerifierFunc := func(ctrl *gomock.Controller) TxVerifier {
   164  				return executor.NewMockManager(ctrl)
   165  			}
   166  			if tt.txVerifierFunc != nil {
   167  				txVerifierFunc = tt.txVerifierFunc
   168  			}
   169  
   170  			appSenderFunc := func(ctrl *gomock.Controller) common.AppSender {
   171  				return common.NewMockSender(ctrl)
   172  			}
   173  			if tt.appSenderFunc != nil {
   174  				appSenderFunc = tt.appSenderFunc
   175  			}
   176  
   177  			n, err := New(
   178  				logging.NoLog{},
   179  				ids.EmptyNodeID,
   180  				ids.Empty,
   181  				&validators.TestState{
   182  					GetCurrentHeightF: func(context.Context) (uint64, error) {
   183  						return 0, nil
   184  					},
   185  					GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   186  						return nil, nil
   187  					},
   188  				},
   189  				parser,
   190  				txVerifierFunc(ctrl),
   191  				mempoolFunc(ctrl),
   192  				appSenderFunc(ctrl),
   193  				prometheus.NewRegistry(),
   194  				testConfig,
   195  			)
   196  			require.NoError(err)
   197  			err = n.IssueTxFromRPC(&txs.Tx{})
   198  			require.ErrorIs(err, tt.expectedErr)
   199  
   200  			require.NoError(n.txPushGossiper.Gossip(context.Background()))
   201  		})
   202  	}
   203  }
   204  
   205  func TestNetworkIssueTxFromRPCWithoutVerification(t *testing.T) {
   206  	type test struct {
   207  		name          string
   208  		mempoolFunc   func(*gomock.Controller) xmempool.Mempool
   209  		appSenderFunc func(*gomock.Controller) common.AppSender
   210  		expectedErr   error
   211  	}
   212  
   213  	tests := []test{
   214  		{
   215  			name: "can't add transaction to mempool",
   216  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
   217  				mempool := xmempool.NewMockMempool(ctrl)
   218  				mempool.EXPECT().Add(gomock.Any()).Return(errTest)
   219  				mempool.EXPECT().MarkDropped(gomock.Any(), gomock.Any())
   220  				return mempool
   221  			},
   222  			expectedErr: errTest,
   223  		},
   224  		{
   225  			name: "happy path",
   226  			mempoolFunc: func(ctrl *gomock.Controller) xmempool.Mempool {
   227  				mempool := xmempool.NewMockMempool(ctrl)
   228  				mempool.EXPECT().Get(gomock.Any()).Return(nil, true).Times(2)
   229  				mempool.EXPECT().Add(gomock.Any()).Return(nil)
   230  				mempool.EXPECT().Len().Return(0)
   231  				mempool.EXPECT().RequestBuildBlock()
   232  				return mempool
   233  			},
   234  			appSenderFunc: func(ctrl *gomock.Controller) common.AppSender {
   235  				appSender := common.NewMockSender(ctrl)
   236  				appSender.EXPECT().SendAppGossip(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
   237  				return appSender
   238  			},
   239  			expectedErr: nil,
   240  		},
   241  	}
   242  
   243  	for _, tt := range tests {
   244  		t.Run(tt.name, func(t *testing.T) {
   245  			require := require.New(t)
   246  			ctrl := gomock.NewController(t)
   247  
   248  			parser, err := txs.NewParser(
   249  				[]fxs.Fx{
   250  					&secp256k1fx.Fx{},
   251  					&nftfx.Fx{},
   252  					&propertyfx.Fx{},
   253  				},
   254  			)
   255  			require.NoError(err)
   256  
   257  			mempoolFunc := func(ctrl *gomock.Controller) xmempool.Mempool {
   258  				return xmempool.NewMockMempool(ctrl)
   259  			}
   260  			if tt.mempoolFunc != nil {
   261  				mempoolFunc = tt.mempoolFunc
   262  			}
   263  
   264  			appSenderFunc := func(ctrl *gomock.Controller) common.AppSender {
   265  				return common.NewMockSender(ctrl)
   266  			}
   267  			if tt.appSenderFunc != nil {
   268  				appSenderFunc = tt.appSenderFunc
   269  			}
   270  
   271  			n, err := New(
   272  				logging.NoLog{},
   273  				ids.EmptyNodeID,
   274  				ids.Empty,
   275  				&validators.TestState{
   276  					GetCurrentHeightF: func(context.Context) (uint64, error) {
   277  						return 0, nil
   278  					},
   279  					GetValidatorSetF: func(context.Context, uint64, ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
   280  						return nil, nil
   281  					},
   282  				},
   283  				parser,
   284  				executor.NewMockManager(ctrl), // Should never verify a tx
   285  				mempoolFunc(ctrl),
   286  				appSenderFunc(ctrl),
   287  				prometheus.NewRegistry(),
   288  				testConfig,
   289  			)
   290  			require.NoError(err)
   291  			err = n.IssueTxFromRPCWithoutVerification(&txs.Tx{})
   292  			require.ErrorIs(err, tt.expectedErr)
   293  
   294  			require.NoError(n.txPushGossiper.Gossip(context.Background()))
   295  		})
   296  	}
   297  }