github.com/klaytn/klaytn@v1.10.2/api/tx_args.go (about) 1 // Modifications Copyright 2019 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from internal/ethapi/api.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package api 22 23 import ( 24 "bytes" 25 "context" 26 "errors" 27 "fmt" 28 "math/big" 29 "reflect" 30 31 "github.com/klaytn/klaytn/accounts/abi" 32 "github.com/klaytn/klaytn/blockchain/types" 33 "github.com/klaytn/klaytn/blockchain/types/accountkey" 34 "github.com/klaytn/klaytn/blockchain/vm" 35 "github.com/klaytn/klaytn/common" 36 "github.com/klaytn/klaytn/common/hexutil" 37 "github.com/klaytn/klaytn/params" 38 "github.com/klaytn/klaytn/rlp" 39 ) 40 41 var ( 42 errTxArgInvalidInputData = errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`) 43 errTxArgInvalidFeePayer = errors.New("invalid fee payer is set") 44 errTxArgNilTxType = errors.New("tx should have a type value") 45 errTxArgNilContractData = errors.New(`contract creation without any data provided`) 46 errTxArgNilSenderSig = errors.New("sender signature is not set") 47 errTxArgNilNonce = errors.New("nonce of the sender is not set") 48 errTxArgNilGas = errors.New("gas limit is not set") 49 errTxArgNilGasPrice = errors.New("gas price is not set") 50 errNotForFeeDelegationTx = errors.New("fee-delegation type transactions are not allowed to use this API") 51 ) 52 53 // isTxField checks whether the string is a field name of the specific txType. 54 // isTxField[txType][txFieldName] has true/false. 55 var isTxField = func() map[types.TxType]map[string]bool { 56 mapOfFieldMap := map[types.TxType]map[string]bool{} 57 internalDataTypes := map[types.TxType]interface{}{ 58 // since legacy tx has optional fields, some fields can be omitted 59 // types.TxTypeLegacyTransaction: types.TxInternalDataLegacy{}, 60 types.TxTypeValueTransfer: types.TxInternalDataValueTransfer{}, 61 types.TxTypeFeeDelegatedValueTransfer: types.TxInternalDataFeeDelegatedValueTransfer{}, 62 types.TxTypeFeeDelegatedValueTransferWithRatio: types.TxInternalDataFeeDelegatedValueTransferWithRatio{}, 63 types.TxTypeValueTransferMemo: types.TxInternalDataValueTransferMemo{}, 64 types.TxTypeFeeDelegatedValueTransferMemo: types.TxInternalDataFeeDelegatedValueTransferMemo{}, 65 types.TxTypeFeeDelegatedValueTransferMemoWithRatio: types.TxInternalDataFeeDelegatedValueTransferMemoWithRatio{}, 66 types.TxTypeAccountUpdate: types.TxInternalDataAccountUpdate{}, 67 types.TxTypeFeeDelegatedAccountUpdate: types.TxInternalDataFeeDelegatedAccountUpdate{}, 68 types.TxTypeFeeDelegatedAccountUpdateWithRatio: types.TxInternalDataFeeDelegatedAccountUpdateWithRatio{}, 69 types.TxTypeSmartContractDeploy: types.TxInternalDataSmartContractDeploy{}, 70 types.TxTypeFeeDelegatedSmartContractDeploy: types.TxInternalDataFeeDelegatedSmartContractDeploy{}, 71 types.TxTypeFeeDelegatedSmartContractDeployWithRatio: types.TxInternalDataFeeDelegatedSmartContractDeployWithRatio{}, 72 types.TxTypeSmartContractExecution: types.TxInternalDataSmartContractExecution{}, 73 types.TxTypeFeeDelegatedSmartContractExecution: types.TxInternalDataFeeDelegatedSmartContractExecution{}, 74 types.TxTypeFeeDelegatedSmartContractExecutionWithRatio: types.TxInternalDataFeeDelegatedSmartContractExecutionWithRatio{}, 75 types.TxTypeCancel: types.TxInternalDataCancel{}, 76 types.TxTypeFeeDelegatedCancel: types.TxInternalDataFeeDelegatedCancel{}, 77 types.TxTypeFeeDelegatedCancelWithRatio: types.TxInternalDataFeeDelegatedCancelWithRatio{}, 78 types.TxTypeChainDataAnchoring: types.TxInternalDataChainDataAnchoring{}, 79 types.TxTypeFeeDelegatedChainDataAnchoring: types.TxInternalDataFeeDelegatedChainDataAnchoring{}, 80 types.TxTypeFeeDelegatedChainDataAnchoringWithRatio: types.TxInternalDataFeeDelegatedChainDataAnchoringWithRatio{}, 81 } 82 83 // generate field maps for each tx type 84 for txType, internalData := range internalDataTypes { 85 fieldMap := map[string]bool{} 86 internalDataType := reflect.TypeOf(internalData) 87 88 // key of filedMap is tx field name and value of fieldMap means the existence of field name 89 for i := 0; i < internalDataType.NumField(); i++ { 90 fieldMap[internalDataType.Field(i).Name] = true 91 } 92 93 // additional field of SendTxArgs to support various tx types 94 fieldMap["TypeInt"] = true 95 // additional field of SendTxArgs to support a legacy tx field (skip checking) 96 fieldMap["Data"] = false 97 98 mapOfFieldMap[txType] = fieldMap 99 } 100 return mapOfFieldMap 101 }() 102 103 type NewTxArgs interface { 104 setDefaults(context.Context, Backend) error 105 toTransaction() (*types.Transaction, error) 106 from() common.Address 107 } 108 109 // SendTxArgs represents the arguments to submit a new transaction into the transaction pool. 110 type SendTxArgs struct { 111 TypeInt *types.TxType `json:"typeInt"` 112 From common.Address `json:"from"` 113 Recipient *common.Address `json:"to"` 114 GasLimit *hexutil.Uint64 `json:"gas"` 115 Price *hexutil.Big `json:"gasPrice"` 116 MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` 117 MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` 118 Amount *hexutil.Big `json:"value"` 119 AccountNonce *hexutil.Uint64 `json:"nonce"` 120 // We accept "data" and "input" for backwards-compatibility reasons. "input" is the 121 // newer name and should be preferred by clients. 122 Data *hexutil.Bytes `json:"data"` 123 Payload *hexutil.Bytes `json:"input"` 124 125 CodeFormat *params.CodeFormat `json:"codeFormat"` 126 HumanReadable *bool `json:"humanReadable"` 127 128 Key *hexutil.Bytes `json:"key"` 129 130 AccessList *types.AccessList `json:"accessList,omitempty"` 131 ChainID *hexutil.Big `json:"chainId,omitempty"` 132 133 FeePayer *common.Address `json:"feePayer"` 134 FeeRatio *types.FeeRatio `json:"feeRatio"` 135 136 TxSignatures types.TxSignaturesJSON `json:"signatures"` 137 } 138 139 // setDefaults is a helper function that fills in default values for unspecified common tx fields. 140 func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { 141 isMagma := b.ChainConfig().IsMagmaForkEnabled(new(big.Int).Add(b.CurrentBlock().Number(), big.NewInt(1))) 142 143 if args.TypeInt == nil { 144 args.TypeInt = new(types.TxType) 145 *args.TypeInt = types.TxTypeLegacyTransaction 146 } 147 if args.GasLimit == nil { 148 args.GasLimit = new(hexutil.Uint64) 149 *args.GasLimit = hexutil.Uint64(90000) 150 } 151 // Eth typed transactions requires chainId. 152 if args.TypeInt.IsEthTypedTransaction() { 153 if args.ChainID == nil { 154 args.ChainID = (*hexutil.Big)(b.ChainConfig().ChainID) 155 } 156 } 157 // For the transaction that do not use the gasPrice field, the default value of gasPrice is not set. 158 if args.Price == nil && *args.TypeInt != types.TxTypeEthereumDynamicFee { 159 // b.SuggestPrice = unitPrice, for before Magma 160 // = baseFee * 2, for after Magma 161 price, err := b.SuggestPrice(ctx) 162 if err != nil { 163 return err 164 } 165 args.Price = (*hexutil.Big)(price) 166 } 167 168 if *args.TypeInt == types.TxTypeEthereumDynamicFee { 169 gasPrice, err := b.SuggestPrice(ctx) 170 if err != nil { 171 return err 172 } 173 if args.MaxPriorityFeePerGas == nil { 174 args.MaxPriorityFeePerGas = (*hexutil.Big)(gasPrice) 175 } 176 if args.MaxFeePerGas == nil { 177 // Before Magma hard fork, `gasFeeCap` was set to `baseFee*2 + maxPriorityFeePerGas` by default. 178 gasFeeCap := new(big.Int).Add( 179 (*big.Int)(args.MaxPriorityFeePerGas), 180 new(big.Int).Mul(new(big.Int).SetUint64(params.ZeroBaseFee), big.NewInt(2)), 181 ) 182 if isMagma { 183 // After Magma hard fork, `gasFeeCap` was set to `baseFee*2` by default. 184 gasFeeCap = gasPrice 185 } 186 args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap) 187 } 188 if isMagma { 189 if args.MaxFeePerGas.ToInt().Cmp(new(big.Int).Div(gasPrice, common.Big2)) < 0 { 190 return fmt.Errorf("maxFeePerGas (%v) < BaseFee (%v)", args.MaxFeePerGas, gasPrice) 191 } 192 } else if args.MaxPriorityFeePerGas.ToInt().Cmp(gasPrice) != 0 || args.MaxFeePerGas.ToInt().Cmp(gasPrice) != 0 { 193 return fmt.Errorf("only %s is allowed to be used as maxFeePerGas and maxPriorityPerGas", gasPrice.Text(16)) 194 } 195 if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 { 196 return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", args.MaxFeePerGas, args.MaxPriorityFeePerGas) 197 } 198 } 199 if args.AccountNonce == nil { 200 nonce := b.GetPoolNonce(ctx, args.From) 201 args.AccountNonce = (*hexutil.Uint64)(&nonce) 202 } 203 204 return nil 205 } 206 207 // checkArgs checks the validity of SendTxArgs values. 208 // The each tx types has its own validation logic to give detailed errors to users. 209 func (args *SendTxArgs) checkArgs() error { 210 if args.TypeInt == nil { 211 return errTxArgNilTxType 212 } 213 // Skip ethereum transaction type since it has optional fields 214 if args.TypeInt.IsEthereumTransaction() { 215 return nil 216 } 217 218 argsType := reflect.TypeOf(*args) 219 argsValue := reflect.ValueOf(*args) 220 221 for i := 0; i < argsType.NumField(); i++ { 222 // Skip From since it is an essential field and a non-pointer value 223 // Skip TxSignatures since the value is not considered by all APIs 224 if argsType.Field(i).Name == "From" || argsType.Field(i).Name == "TxSignatures" { 225 continue 226 } 227 228 // An args field doesn't have a value but the field name exist on the tx type 229 if argsValue.Field(i).IsNil() && isTxField[*args.TypeInt][argsType.Field(i).Name] { 230 // Allow only contract deploying txs to set the recipient as nil 231 if (*args.TypeInt).IsContractDeploy() && argsType.Field(i).Name == "Recipient" { 232 continue 233 } 234 return errors.New((string)(argsType.Field(i).Tag) + " is required for " + (*args.TypeInt).String()) 235 } 236 237 // An args field has a value but the field name doesn't exist on the tx type 238 if !argsValue.Field(i).IsNil() && !isTxField[*args.TypeInt][argsType.Field(i).Name] { 239 return errors.New((string)(argsType.Field(i).Tag) + " is not a field of " + (*args.TypeInt).String()) 240 } 241 } 242 243 return nil 244 } 245 246 // genTxValuesMap generates a value map used used in "NewTransactionWithMap" function. 247 // This function assigned all non-nil values regardless of the tx type. 248 // Invalid values in the map will be validated in "NewTransactionWithMap" function. 249 func (args *SendTxArgs) genTxValuesMap() map[types.TxValueKeyType]interface{} { 250 values := make(map[types.TxValueKeyType]interface{}) 251 252 // common tx fields. They should have values after executing "setDefaults" function. 253 if args.TypeInt == nil || args.AccountNonce == nil || args.GasLimit == nil { 254 return values 255 } 256 // GasPrice can be an optional tx filed for TxTypeEthereumDynamicFee 257 if args.Price == nil && *args.TypeInt != types.TxTypeEthereumDynamicFee { 258 return values 259 } 260 261 if !args.TypeInt.IsEthereumTransaction() { 262 values[types.TxValueKeyFrom] = args.From 263 } 264 values[types.TxValueKeyNonce] = uint64(*args.AccountNonce) 265 values[types.TxValueKeyGasLimit] = uint64(*args.GasLimit) 266 267 // optional tx fields 268 if args.Price != nil { 269 values[types.TxValueKeyGasPrice] = (*big.Int)(args.Price) 270 } 271 if args.TypeInt.IsContractDeploy() || args.TypeInt.IsEthereumTransaction() { 272 // contract deploy type and ethereum tx types allow nil as TxValueKeyTo value 273 values[types.TxValueKeyTo] = (*common.Address)(args.Recipient) 274 } else if args.Recipient != nil { 275 values[types.TxValueKeyTo] = *args.Recipient 276 } 277 if args.FeePayer != nil { 278 values[types.TxValueKeyFeePayer] = *args.FeePayer 279 } 280 if args.FeeRatio != nil { 281 values[types.TxValueKeyFeeRatioOfFeePayer] = *args.FeeRatio 282 } 283 if args.Amount != nil { 284 values[types.TxValueKeyAmount] = (*big.Int)(args.Amount) 285 } else if args.TypeInt.IsEthereumTransaction() { 286 values[types.TxValueKeyAmount] = common.Big0 287 } 288 if args.Payload != nil { 289 // chain data anchoring type uses the TxValueKeyAnchoredData field 290 if args.TypeInt.IsChainDataAnchoring() { 291 values[types.TxValueKeyAnchoredData] = ([]byte)(*args.Payload) 292 } else { 293 values[types.TxValueKeyData] = ([]byte)(*args.Payload) 294 } 295 } else if args.TypeInt.IsEthereumTransaction() { 296 // For Ethereum transactions, Payload is an optional field. 297 values[types.TxValueKeyData] = []byte{} 298 } 299 if args.CodeFormat != nil { 300 values[types.TxValueKeyCodeFormat] = *args.CodeFormat 301 } 302 if args.HumanReadable != nil { 303 values[types.TxValueKeyHumanReadable] = *args.HumanReadable 304 } 305 if args.Key != nil { 306 serializer := accountkey.NewAccountKeySerializer() 307 if err := rlp.DecodeBytes(*args.Key, &serializer); err == nil { 308 values[types.TxValueKeyAccountKey] = serializer.GetKey() 309 } 310 } 311 if args.ChainID != nil { 312 values[types.TxValueKeyChainID] = (*big.Int)(args.ChainID) 313 } 314 if args.AccessList != nil { 315 values[types.TxValueKeyAccessList] = *args.AccessList 316 } 317 if args.MaxPriorityFeePerGas != nil { 318 values[types.TxValueKeyGasTipCap] = (*big.Int)(args.MaxPriorityFeePerGas) 319 } 320 if args.MaxFeePerGas != nil { 321 values[types.TxValueKeyGasFeeCap] = (*big.Int)(args.MaxFeePerGas) 322 } 323 324 return values 325 } 326 327 // toTransaction returns an unsigned transaction filled with values in SendTxArgs. 328 func (args *SendTxArgs) toTransaction() (*types.Transaction, error) { 329 var input []byte 330 331 // provide detailed error messages to users (optional) 332 if err := args.checkArgs(); err != nil { 333 return nil, err 334 } 335 336 // for TxTypeLegacyTransaction 337 if *args.TypeInt == types.TxTypeLegacyTransaction { 338 if args.Data != nil && args.Payload != nil && !bytes.Equal(*args.Data, *args.Payload) { 339 return nil, errTxArgInvalidInputData 340 } 341 342 if args.Data != nil { 343 input = *args.Data 344 } else if args.Payload != nil { 345 input = *args.Payload 346 } 347 348 if args.Recipient == nil { 349 if len(input) == 0 { 350 return nil, errTxArgNilContractData 351 } 352 return types.NewContractCreation(uint64(*args.AccountNonce), (*big.Int)(args.Amount), uint64(*args.GasLimit), (*big.Int)(args.Price), input), nil 353 } 354 return types.NewTransaction(uint64(*args.AccountNonce), *args.Recipient, (*big.Int)(args.Amount), uint64(*args.GasLimit), (*big.Int)(args.Price), input), nil 355 } 356 357 // for other tx types except TxTypeLegacyTransaction 358 values := args.genTxValuesMap() 359 return types.NewTransactionWithMap(*args.TypeInt, values) 360 } 361 362 type ValueTransferTxArgs struct { 363 From common.Address `json:"from"` 364 Gas *hexutil.Uint64 `json:"gas"` 365 GasPrice *hexutil.Big `json:"gasPrice"` 366 Nonce *hexutil.Uint64 `json:"nonce"` 367 To common.Address `json:"to"` 368 Value *hexutil.Big `json:"value"` 369 } 370 371 func (args *ValueTransferTxArgs) from() common.Address { 372 return args.From 373 } 374 375 // setDefaults is a helper function that fills in default values for unspecified tx fields. 376 func (args *ValueTransferTxArgs) setDefaults(ctx context.Context, b Backend) error { 377 if args.Gas == nil { 378 args.Gas = new(hexutil.Uint64) 379 *(*uint64)(args.Gas) = 90000 380 } 381 if args.GasPrice == nil { 382 price, err := b.SuggestPrice(ctx) 383 if err != nil { 384 return err 385 } 386 args.GasPrice = (*hexutil.Big)(price) 387 } 388 if args.Nonce == nil { 389 nonce := b.GetPoolNonce(ctx, args.From) 390 args.Nonce = (*hexutil.Uint64)(&nonce) 391 } 392 return nil 393 } 394 395 func (args *ValueTransferTxArgs) toTransaction() (*types.Transaction, error) { 396 tx, err := types.NewTransactionWithMap(types.TxTypeValueTransfer, map[types.TxValueKeyType]interface{}{ 397 types.TxValueKeyNonce: (uint64)(*args.Nonce), 398 types.TxValueKeyGasLimit: (uint64)(*args.Gas), 399 types.TxValueKeyGasPrice: (*big.Int)(args.GasPrice), 400 types.TxValueKeyFrom: args.From, 401 types.TxValueKeyTo: args.To, 402 types.TxValueKeyAmount: (*big.Int)(args.Value), 403 }) 404 if err != nil { 405 return nil, err 406 } 407 408 return tx, nil 409 } 410 411 type AccountUpdateTxArgs struct { 412 From common.Address `json:"from"` 413 Gas *hexutil.Uint64 `json:"gas"` 414 GasPrice *hexutil.Big `json:"gasPrice"` 415 Nonce *hexutil.Uint64 `json:"nonce"` 416 Key *hexutil.Bytes `json:"key"` 417 } 418 419 func (args *AccountUpdateTxArgs) from() common.Address { 420 return args.From 421 } 422 423 // setDefaults is a helper function that fills in default values for unspecified tx fields. 424 func (args *AccountUpdateTxArgs) setDefaults(ctx context.Context, b Backend) error { 425 if args.Gas == nil { 426 args.Gas = new(hexutil.Uint64) 427 *(*uint64)(args.Gas) = 90000 428 } 429 if args.GasPrice == nil { 430 price, err := b.SuggestPrice(ctx) 431 if err != nil { 432 return err 433 } 434 args.GasPrice = (*hexutil.Big)(price) 435 } 436 if args.Nonce == nil { 437 nonce := b.GetPoolNonce(ctx, args.From) 438 args.Nonce = (*hexutil.Uint64)(&nonce) 439 } 440 return nil 441 } 442 443 func (args *AccountUpdateTxArgs) toTransaction() (*types.Transaction, error) { 444 serializer := accountkey.NewAccountKeySerializer() 445 446 if err := rlp.DecodeBytes(*args.Key, &serializer); err != nil { 447 return nil, err 448 } 449 tx, err := types.NewTransactionWithMap(types.TxTypeAccountUpdate, map[types.TxValueKeyType]interface{}{ 450 types.TxValueKeyNonce: (uint64)(*args.Nonce), 451 types.TxValueKeyGasLimit: (uint64)(*args.Gas), 452 types.TxValueKeyGasPrice: (*big.Int)(args.GasPrice), 453 types.TxValueKeyFrom: args.From, 454 types.TxValueKeyAccountKey: serializer.GetKey(), 455 }) 456 if err != nil { 457 return nil, err 458 } 459 460 return tx, nil 461 } 462 463 // isReverted checks given error is vm.ErrExecutionReverted 464 func isReverted(err error) bool { 465 if errors.Is(err, vm.ErrExecutionReverted) { 466 return true 467 } 468 return false 469 } 470 471 // newRevertError wraps data returned when EVM execution was reverted. 472 // Make sure that data is returned when execution reverted situation. 473 func newRevertError(data []byte) *revertError { 474 reason, errUnpack := abi.UnpackRevert(data) 475 err := errors.New("execution reverted") 476 if errUnpack == nil { 477 err = fmt.Errorf("execution reverted: %v", reason) 478 } 479 return &revertError{ 480 error: err, 481 reason: hexutil.Encode(data), 482 } 483 } 484 485 // revertError is an API error that encompassas an EVM revertal with JSON error 486 // code and a binary data blob. 487 type revertError struct { 488 error 489 reason string // revert reason hex encoded 490 } 491 492 // ErrorCode returns the JSON error code for a revertal. 493 // See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal 494 func (e *revertError) ErrorCode() int { 495 return 3 496 } 497 498 // ErrorData returns the hex encoded revert reason. 499 func (e *revertError) ErrorData() interface{} { 500 return e.reason 501 }