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

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