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 }