github.com/klaytn/klaytn@v1.12.1/api/api_public_transaction_pool_test.go (about)

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  	"os"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/golang/mock/gomock"
    11  	"github.com/klaytn/klaytn/accounts"
    12  	"github.com/klaytn/klaytn/accounts/keystore"
    13  	mock_accounts "github.com/klaytn/klaytn/accounts/mocks"
    14  	mock_api "github.com/klaytn/klaytn/api/mocks"
    15  	"github.com/klaytn/klaytn/blockchain/types"
    16  	"github.com/klaytn/klaytn/common"
    17  	"github.com/klaytn/klaytn/common/hexutil"
    18  	"github.com/klaytn/klaytn/crypto"
    19  	"github.com/klaytn/klaytn/params"
    20  	"github.com/stretchr/testify/assert"
    21  )
    22  
    23  // test tx types and internal data to be supported by APIs in PublicTransactionPoolAPI.
    24  var internalDataTypes = map[types.TxType]interface{}{
    25  	types.TxTypeLegacyTransaction:                           types.TxInternalDataLegacy{},
    26  	types.TxTypeValueTransfer:                               types.TxInternalDataValueTransfer{},
    27  	types.TxTypeFeeDelegatedValueTransfer:                   types.TxInternalDataFeeDelegatedValueTransfer{},
    28  	types.TxTypeFeeDelegatedValueTransferWithRatio:          types.TxInternalDataFeeDelegatedValueTransferWithRatio{},
    29  	types.TxTypeValueTransferMemo:                           types.TxInternalDataValueTransferMemo{},
    30  	types.TxTypeFeeDelegatedValueTransferMemo:               types.TxInternalDataFeeDelegatedValueTransferMemo{},
    31  	types.TxTypeFeeDelegatedValueTransferMemoWithRatio:      types.TxInternalDataFeeDelegatedValueTransferMemoWithRatio{},
    32  	types.TxTypeAccountUpdate:                               types.TxInternalDataAccountUpdate{},
    33  	types.TxTypeFeeDelegatedAccountUpdate:                   types.TxInternalDataFeeDelegatedAccountUpdate{},
    34  	types.TxTypeFeeDelegatedAccountUpdateWithRatio:          types.TxInternalDataFeeDelegatedAccountUpdateWithRatio{},
    35  	types.TxTypeSmartContractDeploy:                         types.TxInternalDataSmartContractDeploy{},
    36  	types.TxTypeFeeDelegatedSmartContractDeploy:             types.TxInternalDataFeeDelegatedSmartContractDeploy{},
    37  	types.TxTypeFeeDelegatedSmartContractDeployWithRatio:    types.TxInternalDataFeeDelegatedSmartContractDeployWithRatio{},
    38  	types.TxTypeSmartContractExecution:                      types.TxInternalDataSmartContractExecution{},
    39  	types.TxTypeFeeDelegatedSmartContractExecution:          types.TxInternalDataFeeDelegatedSmartContractExecution{},
    40  	types.TxTypeFeeDelegatedSmartContractExecutionWithRatio: types.TxInternalDataFeeDelegatedSmartContractExecutionWithRatio{},
    41  	types.TxTypeCancel:                                      types.TxInternalDataCancel{},
    42  	types.TxTypeFeeDelegatedCancel:                          types.TxInternalDataFeeDelegatedCancel{},
    43  	types.TxTypeFeeDelegatedCancelWithRatio:                 types.TxInternalDataFeeDelegatedCancelWithRatio{},
    44  	types.TxTypeChainDataAnchoring:                          types.TxInternalDataChainDataAnchoring{},
    45  	types.TxTypeFeeDelegatedChainDataAnchoring:              types.TxInternalDataFeeDelegatedChainDataAnchoring{},
    46  	types.TxTypeFeeDelegatedChainDataAnchoringWithRatio:     types.TxInternalDataFeeDelegatedChainDataAnchoringWithRatio{},
    47  }
    48  
    49  // test values of tx field.
    50  var (
    51  	testNonce         = hexutil.Uint64(0)
    52  	testGas           = hexutil.Uint64(900000)
    53  	testGasPrice      = (*hexutil.Big)(big.NewInt(25 * params.Ston))
    54  	testValue         = (*hexutil.Big)(big.NewInt(1))
    55  	testTo            = common.StringToAddress("1234")
    56  	testFeePayer      = common.HexToAddress("0x819104a190255e0cedbdd9d5f59a557633d79db1")
    57  	testFeeRatio      = types.FeeRatio(30)
    58  	testData          = hexutil.Bytes{0x11, 0x99}
    59  	testCodeFormat    = params.CodeFormatEVM
    60  	testHumanReadable = false
    61  	testAccountKey    = hexutil.Bytes{0x01, 0xc0}
    62  	testFrom          = common.HexToAddress("0xa7Eb6992c5FD55F43305B24Ee67150Bf4910d329")
    63  	testSig           = types.TxSignatures{&types.TxSignature{V: big.NewInt(1), R: big.NewInt(2), S: big.NewInt(3)}}.ToJSON()
    64  	senderPrvKey, _   = crypto.HexToECDSA("95a21e86efa290d6665a9dbce06ae56319335540d13540fb1b01e28a5b2c8460")
    65  	feePayerPrvKey, _ = crypto.HexToECDSA("aebb680a5e596c1d1a01bac78a3985b62c685c5e995d780c176138cb2679ba3e")
    66  )
    67  
    68  // TestTxTypeSupport tests tx type support of APIs in PublicTransactionPoolAPI.
    69  func TestTxTypeSupport(t *testing.T) {
    70  	var ctx context.Context
    71  	chainConf := params.ChainConfig{ChainID: big.NewInt(1)}
    72  
    73  	// generate a keystore and active accounts
    74  	dir, err := os.MkdirTemp("", "klay-keystore-test")
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	defer os.RemoveAll(dir)
    79  	ks := keystore.NewKeyStore(dir, 2, 1)
    80  	password := ""
    81  	acc, err := ks.ImportECDSA(senderPrvKey, password)
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  	if err := ks.Unlock(acc, password); err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	accFeePayer, err := ks.ImportECDSA(feePayerPrvKey, password)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	if err := ks.Unlock(accFeePayer, password); err != nil {
    93  		t.Fatal(err)
    94  	}
    95  
    96  	// mock Backend and AccountManager for easy test
    97  	mockCtrl := gomock.NewController(t)
    98  	mockBackend := mock_api.NewMockBackend(mockCtrl)
    99  	mockAccountManager := mock_accounts.NewMockAccountManager(mockCtrl)
   100  
   101  	mockBackend.EXPECT().AccountManager().Return(mockAccountManager).AnyTimes()
   102  	mockBackend.EXPECT().CurrentBlock().Return(
   103  		types.NewBlockWithHeader(&types.Header{Number: new(big.Int).SetUint64(0)}),
   104  	).AnyTimes()
   105  	mockBackend.EXPECT().SuggestPrice(ctx).Return((*big.Int)(testGasPrice), nil).AnyTimes()
   106  	mockBackend.EXPECT().GetPoolNonce(ctx, gomock.Any()).Return(uint64(testNonce)).AnyTimes()
   107  	mockBackend.EXPECT().SendTx(ctx, gomock.Any()).Return(nil).AnyTimes()
   108  	mockBackend.EXPECT().ChainConfig().Return(&chainConf).AnyTimes()
   109  	mockAccountManager.EXPECT().Find(accounts.Account{Address: acc.Address}).Return(ks.Wallets()[0], nil).AnyTimes()
   110  	mockAccountManager.EXPECT().Find(accounts.Account{Address: accFeePayer.Address}).Return(ks.Wallets()[1], nil).AnyTimes()
   111  
   112  	// APIs in PublicTransactionPoolAPI will be tested
   113  	api := PublicTransactionPoolAPI{
   114  		b:         mockBackend,
   115  		nonceLock: new(AddrLocker),
   116  	}
   117  
   118  	// test for all possible tx types
   119  	for txType, internalData := range internalDataTypes {
   120  		// args contains values of tx fields
   121  		args := SendTxArgs{
   122  			TypeInt: &txType,
   123  			From:    testFrom,
   124  		}
   125  
   126  		// set required fields of each typed tx
   127  		internalType := reflect.TypeOf(internalData)
   128  		for i := 0; i < internalType.NumField(); i++ {
   129  			switch internalType.Field(i).Name {
   130  			case "AccountNonce":
   131  				args.AccountNonce = &testNonce
   132  			case "Amount":
   133  				args.Amount = testValue
   134  			case "Recipient":
   135  				args.Recipient = &testTo
   136  			case "FeePayer":
   137  				args.FeePayer = &testFeePayer
   138  			case "FeeRatio":
   139  				args.FeeRatio = &testFeeRatio
   140  			case "GasLimit":
   141  				args.GasLimit = &testGas
   142  			case "Price":
   143  				args.Price = testGasPrice
   144  			case "Payload":
   145  				args.Payload = &testData
   146  			case "CodeFormat":
   147  				args.CodeFormat = &testCodeFormat
   148  			case "HumanReadable":
   149  				args.HumanReadable = &testHumanReadable
   150  			case "Key":
   151  				args.Key = &testAccountKey
   152  			}
   153  		}
   154  		if txType.IsFeeDelegatedTransaction() {
   155  			args.TxSignatures = testSig
   156  		}
   157  
   158  		testTxTypeSupport_normalCase(t, api, ctx, args)
   159  		testTxTypeSupport_setDefault(t, api, ctx, args)
   160  		testTxTypeSupport_noFieldValues(t, api, ctx, args)
   161  		testTxTypeSupport_unnecessaryFieldValues(t, api, ctx, args)
   162  	}
   163  }
   164  
   165  // testTxTypeSupport_normalCase tests APIs with proper SendTxArgs values.
   166  func testTxTypeSupport_normalCase(t *testing.T, api PublicTransactionPoolAPI, ctx context.Context, args SendTxArgs) {
   167  	var err error
   168  
   169  	// test APIs for non-fee-delegation txs
   170  	if !args.TypeInt.IsFeeDelegatedTransaction() {
   171  		_, err = api.SendTransaction(ctx, args)
   172  		assert.Equal(t, nil, err)
   173  
   174  		// test APIs for fee delegation txs
   175  	} else {
   176  		_, err := api.SignTransactionAsFeePayer(ctx, args)
   177  		assert.Equal(t, nil, err)
   178  
   179  		_, err = api.SendTransactionAsFeePayer(ctx, args)
   180  		assert.Equal(t, nil, err)
   181  	}
   182  
   183  	// test for all txs
   184  	_, err = api.SignTransaction(ctx, args)
   185  	assert.Equal(t, nil, err)
   186  }
   187  
   188  // testTxTypeSupport_setDefault tests the setDefault function which auto-assign some values of tx.
   189  func testTxTypeSupport_setDefault(t *testing.T, api PublicTransactionPoolAPI, ctx context.Context, args SendTxArgs) {
   190  	args.AccountNonce = nil
   191  	args.GasLimit = nil
   192  	args.Price = nil
   193  
   194  	_, err := api.SignTransaction(ctx, args)
   195  	assert.Equal(t, nil, err)
   196  }
   197  
   198  // testTxTypeSupport_noFieldValues tests error handling for not assigned field values.
   199  func testTxTypeSupport_noFieldValues(t *testing.T, api PublicTransactionPoolAPI, ctx context.Context, oriArgs SendTxArgs) {
   200  	// fields of legacy tx will not be checked in the checkArgs function
   201  	if *oriArgs.TypeInt == types.TxTypeLegacyTransaction {
   202  		return
   203  	}
   204  
   205  	args := oriArgs
   206  	if args.Recipient != nil && !(*args.TypeInt).IsContractDeploy() {
   207  		args.Recipient = nil
   208  
   209  		_, err := api.SignTransaction(ctx, args)
   210  		assert.Equal(t, "json:\"to\" is required for "+(*args.TypeInt).String(), err.Error())
   211  	}
   212  
   213  	args = oriArgs
   214  	if args.Payload != nil {
   215  		args.Payload = nil
   216  
   217  		_, err := api.SignTransaction(ctx, args)
   218  		assert.Equal(t, "json:\"input\" is required for "+(*args.TypeInt).String(), err.Error())
   219  	}
   220  
   221  	args = oriArgs
   222  	if args.Amount != nil {
   223  		args.Amount = nil
   224  
   225  		_, err := api.SignTransaction(ctx, args)
   226  		assert.Equal(t, "json:\"value\" is required for "+(*args.TypeInt).String(), err.Error())
   227  	}
   228  
   229  	args = oriArgs
   230  	if args.CodeFormat != nil {
   231  		args.CodeFormat = nil
   232  
   233  		_, err := api.SignTransaction(ctx, args)
   234  		assert.Equal(t, "json:\"codeFormat\" is required for "+(*args.TypeInt).String(), err.Error())
   235  	}
   236  
   237  	args = oriArgs
   238  	if args.HumanReadable != nil {
   239  		args.HumanReadable = nil
   240  
   241  		_, err := api.SignTransaction(ctx, args)
   242  		assert.Equal(t, "json:\"humanReadable\" is required for "+(*args.TypeInt).String(), err.Error())
   243  	}
   244  
   245  	args = oriArgs
   246  	if args.Key != nil {
   247  		args.Key = nil
   248  
   249  		_, err := api.SignTransaction(ctx, args)
   250  		assert.Equal(t, "json:\"key\" is required for "+(*args.TypeInt).String(), err.Error())
   251  	}
   252  
   253  	args = oriArgs
   254  	if args.FeePayer != nil {
   255  		args.FeePayer = nil
   256  
   257  		_, err := api.SignTransaction(ctx, args)
   258  		assert.Equal(t, "json:\"feePayer\" is required for "+(*args.TypeInt).String(), err.Error())
   259  	}
   260  
   261  	args = oriArgs
   262  	if args.FeeRatio != nil {
   263  		args.FeeRatio = nil
   264  
   265  		_, err := api.SignTransaction(ctx, args)
   266  		assert.Equal(t, "json:\"feeRatio\" is required for "+(*args.TypeInt).String(), err.Error())
   267  	}
   268  }
   269  
   270  // testTxTypeSupport_unnecessaryFieldValues tests error handling for not assigned field values.
   271  func testTxTypeSupport_unnecessaryFieldValues(t *testing.T, api PublicTransactionPoolAPI, ctx context.Context, oriArgs SendTxArgs) {
   272  	// fields of legacy tx will not be checked in the checkArgs function
   273  	if *oriArgs.TypeInt == types.TxTypeLegacyTransaction {
   274  		return
   275  	}
   276  
   277  	args := oriArgs
   278  	if args.Recipient == nil {
   279  		args.Recipient = &testTo
   280  
   281  		_, err := api.SignTransaction(ctx, args)
   282  		assert.Equal(t, "json:\"to\" is not a field of "+(*args.TypeInt).String(), err.Error())
   283  	}
   284  
   285  	args = oriArgs
   286  	if args.Payload == nil {
   287  		args.Payload = &testData
   288  
   289  		_, err := api.SignTransaction(ctx, args)
   290  		assert.Equal(t, "json:\"input\" is not a field of "+(*args.TypeInt).String(), err.Error())
   291  	}
   292  
   293  	args = oriArgs
   294  	if args.Amount == nil {
   295  		args.Amount = testValue
   296  
   297  		_, err := api.SignTransaction(ctx, args)
   298  		assert.Equal(t, "json:\"value\" is not a field of "+(*args.TypeInt).String(), err.Error())
   299  	}
   300  
   301  	args = oriArgs
   302  	if args.CodeFormat == nil {
   303  		args.CodeFormat = &testCodeFormat
   304  
   305  		_, err := api.SignTransaction(ctx, args)
   306  		assert.Equal(t, "json:\"codeFormat\" is not a field of "+(*args.TypeInt).String(), err.Error())
   307  	}
   308  
   309  	args = oriArgs
   310  	if args.HumanReadable == nil {
   311  		args.HumanReadable = &testHumanReadable
   312  
   313  		_, err := api.SignTransaction(ctx, args)
   314  		assert.Equal(t, "json:\"humanReadable\" is not a field of "+(*args.TypeInt).String(), err.Error())
   315  	}
   316  
   317  	args = oriArgs
   318  	if args.Key == nil {
   319  		args.Key = &testAccountKey
   320  
   321  		_, err := api.SignTransaction(ctx, args)
   322  		assert.Equal(t, "json:\"key\" is not a field of "+(*args.TypeInt).String(), err.Error())
   323  	}
   324  
   325  	args = oriArgs
   326  	if args.FeePayer == nil {
   327  		args.FeePayer = &testFeePayer
   328  
   329  		_, err := api.SignTransaction(ctx, args)
   330  		assert.Equal(t, "json:\"feePayer\" is not a field of "+(*args.TypeInt).String(), err.Error())
   331  	}
   332  
   333  	args = oriArgs
   334  	if args.FeeRatio == nil {
   335  		args.FeeRatio = &testFeeRatio
   336  
   337  		_, err := api.SignTransaction(ctx, args)
   338  		assert.Equal(t, "json:\"feeRatio\" is not a field of "+(*args.TypeInt).String(), err.Error())
   339  	}
   340  }