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 }