github.com/iotexproject/iotex-core@v1.14.1-rc1/api/web3server_utils.go (about) 1 package api 2 3 import ( 4 "context" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "math/big" 9 "strconv" 10 "time" 11 12 "github.com/ethereum/go-ethereum/common" 13 "github.com/ethereum/go-ethereum/common/math" 14 "github.com/ethereum/go-ethereum/core/types" 15 "github.com/ethereum/go-ethereum/eth/tracers/logger" 16 "github.com/go-redis/redis/v8" 17 "github.com/iotexproject/go-pkgs/cache/ttl" 18 "github.com/iotexproject/go-pkgs/hash" 19 "github.com/iotexproject/go-pkgs/util" 20 "github.com/iotexproject/iotex-address/address" 21 "github.com/iotexproject/iotex-proto/golang/iotexapi" 22 "github.com/iotexproject/iotex-proto/golang/iotextypes" 23 "github.com/pkg/errors" 24 "github.com/tidwall/gjson" 25 "go.uber.org/zap" 26 27 "github.com/iotexproject/iotex-core/action" 28 logfilter "github.com/iotexproject/iotex-core/api/logfilter" 29 apitypes "github.com/iotexproject/iotex-core/api/types" 30 "github.com/iotexproject/iotex-core/blockchain/block" 31 "github.com/iotexproject/iotex-core/pkg/log" 32 "github.com/iotexproject/iotex-core/pkg/util/addrutil" 33 ) 34 35 func hexStringToNumber(hexStr string) (uint64, error) { 36 return strconv.ParseUint(util.Remove0xPrefix(hexStr), 16, 64) 37 } 38 39 func ethAddrToIoAddr(ethAddr string) (address.Address, error) { 40 if ok := common.IsHexAddress(ethAddr); !ok { 41 return nil, errors.Wrapf(errUnkownType, "ethAddr: %s", ethAddr) 42 } 43 return address.FromHex(ethAddr) 44 } 45 46 func ioAddrToEthAddr(ioAddr string) (string, error) { 47 if len(ioAddr) == 0 { 48 return "0x0000000000000000000000000000000000000000", nil 49 } 50 addr, err := addrutil.IoAddrToEvmAddr(ioAddr) 51 if err != nil { 52 return "", err 53 } 54 return addr.String(), nil 55 } 56 57 func uint64ToHex(val uint64) string { 58 return "0x" + strconv.FormatUint(val, 16) 59 } 60 61 func intStrToHex(str string) (string, error) { 62 amount, ok := new(big.Int).SetString(str, 10) 63 if !ok { 64 return "", errors.Wrapf(errUnkownType, "int: %s", str) 65 } 66 return "0x" + fmt.Sprintf("%x", amount), nil 67 } 68 69 func (svr *web3Handler) getBlockWithTransactions(blk *block.Block, receipts []*action.Receipt, isDetailed bool) (*getBlockResult, error) { 70 if blk == nil || receipts == nil { 71 return nil, errInvalidBlock 72 } 73 transactions := make([]interface{}, 0) 74 for i, selp := range blk.Actions { 75 if isDetailed { 76 tx, err := svr.assembleConfirmedTransaction(blk.HashBlock(), selp, receipts[i]) 77 if err != nil { 78 if errors.Cause(err) != errUnsupportedAction { 79 h, _ := selp.Hash() 80 log.Logger("api").Error("failed to get info from action", zap.Error(err), zap.String("actHash", hex.EncodeToString(h[:]))) 81 } 82 continue 83 } 84 transactions = append(transactions, tx) 85 } else { 86 actHash, err := selp.Hash() 87 if err != nil { 88 return nil, err 89 } 90 transactions = append(transactions, "0x"+hex.EncodeToString(actHash[:])) 91 } 92 } 93 return &getBlockResult{ 94 blk: blk, 95 transactions: transactions, 96 }, nil 97 } 98 99 func (svr *web3Handler) assembleConfirmedTransaction(blkHash hash.Hash256, selp *action.SealedEnvelope, receipt *action.Receipt) (*getTransactionResult, error) { 100 // sanity check 101 if receipt == nil { 102 return nil, errors.New("receipt is empty") 103 } 104 actHash, err := selp.Hash() 105 if err != nil || actHash != receipt.ActionHash { 106 return nil, errors.Errorf("the action %s of receipt doesn't match", hex.EncodeToString(actHash[:])) 107 } 108 return newGetTransactionResult(&blkHash, selp, receipt, svr.coreService.EVMNetworkID()) 109 } 110 111 func (svr *web3Handler) assemblePendingTransaction(selp *action.SealedEnvelope) (*getTransactionResult, error) { 112 return newGetTransactionResult(nil, selp, nil, svr.coreService.EVMNetworkID()) 113 } 114 115 func getRecipientAndContractAddrFromAction(selp *action.SealedEnvelope, receipt *action.Receipt) (*string, *string, error) { 116 // recipient is empty when contract is created 117 if exec, ok := selp.Action().(*action.Execution); ok && len(exec.Contract()) == 0 { 118 addr, err := ioAddrToEthAddr(receipt.ContractAddress) 119 if err != nil { 120 return nil, nil, err 121 } 122 return nil, &addr, nil 123 } 124 act, ok := selp.Action().(action.EthCompatibleAction) 125 if !ok { 126 actHash, _ := selp.Hash() 127 return nil, nil, errors.Wrapf(errUnsupportedAction, "actHash: %s", hex.EncodeToString(actHash[:])) 128 } 129 ethTx, err := act.ToEthTx(0) 130 if err != nil { 131 return nil, nil, err 132 } 133 toTmp := ethTx.To().String() 134 return &toTmp, nil, nil 135 } 136 137 func (svr *web3Handler) parseBlockNumber(str string) (uint64, error) { 138 switch str { 139 case _earliestBlockNumber: 140 return 1, nil 141 case "", _pendingBlockNumber, _latestBlockNumber: 142 return svr.coreService.TipHeight(), nil 143 default: 144 return hexStringToNumber(str) 145 } 146 } 147 148 func (svr *web3Handler) parseBlockRange(fromStr string, toStr string) (from uint64, to uint64, err error) { 149 from, err = svr.parseBlockNumber(fromStr) 150 if err != nil { 151 return 152 } 153 to, err = svr.parseBlockNumber(toStr) 154 return 155 } 156 157 func (svr *web3Handler) ethTxToEnvelope(tx *types.Transaction) (action.Envelope, error) { 158 to := "" 159 if tx.To() != nil { 160 ioAddr, _ := address.FromBytes(tx.To().Bytes()) 161 to = ioAddr.String() 162 } 163 elpBuilder := (&action.EnvelopeBuilder{}).SetChainID(svr.coreService.ChainID()) 164 if to == address.StakingProtocolAddr { 165 return elpBuilder.BuildStakingAction(tx) 166 } 167 if to == address.RewardingProtocol { 168 return elpBuilder.BuildRewardingAction(tx) 169 } 170 isContract, err := svr.checkContractAddr(to) 171 if err != nil { 172 return nil, err 173 } 174 if isContract { 175 return elpBuilder.BuildExecution(tx) 176 } 177 return elpBuilder.BuildTransfer(tx) 178 } 179 180 func (svr *web3Handler) checkContractAddr(to string) (bool, error) { 181 if to == "" { 182 return true, nil 183 } 184 ioAddr, err := address.FromString(to) 185 if err != nil { 186 return false, err 187 } 188 accountMeta, _, err := svr.coreService.Account(ioAddr) 189 return accountMeta.IsContract, err 190 } 191 192 func (svr *web3Handler) getLogsWithFilter(from uint64, to uint64, addrs []string, topics [][]string) ([]*getLogsResult, error) { 193 filter, err := newLogFilterFrom(addrs, topics) 194 if err != nil { 195 return nil, err 196 } 197 logs, hashes, err := svr.coreService.LogsInRange(filter, from, to, 0) 198 if err != nil { 199 return nil, err 200 } 201 ret := make([]*getLogsResult, 0, len(logs)) 202 for i := range logs { 203 ret = append(ret, &getLogsResult{hashes[i], logs[i]}) 204 } 205 return ret, nil 206 } 207 208 // construct filter topics and addresses 209 func newLogFilterFrom(addrs []string, topics [][]string) (*logfilter.LogFilter, error) { 210 filter := iotexapi.LogsFilter{} 211 for _, ethAddr := range addrs { 212 ioAddr, err := ethAddrToIoAddr(ethAddr) 213 if err != nil { 214 return nil, err 215 } 216 filter.Address = append(filter.Address, ioAddr.String()) 217 } 218 for _, tp := range topics { 219 var topic [][]byte 220 for _, str := range tp { 221 b, err := hexToBytes(str) 222 if err != nil { 223 return nil, err 224 } 225 topic = append(topic, b) 226 } 227 filter.Topics = append(filter.Topics, &iotexapi.Topics{Topic: topic}) 228 } 229 return logfilter.NewLogFilter(&filter), nil 230 } 231 232 func byteToHex(b []byte) string { 233 return "0x" + hex.EncodeToString(b) 234 } 235 236 func hexToBytes(str string) ([]byte, error) { 237 str = util.Remove0xPrefix(str) 238 if len(str)%2 == 1 { 239 str = "0" + str 240 } 241 return hex.DecodeString(str) 242 } 243 244 func parseLogRequest(in gjson.Result) (*filterObject, error) { 245 if !in.Exists() { 246 return nil, errInvalidFormat 247 } 248 var logReq filterObject 249 if len(in.Array()) > 0 { 250 req := in.Array()[0] 251 logReq.FromBlock = req.Get("fromBlock").String() 252 logReq.ToBlock = req.Get("toBlock").String() 253 for _, addr := range req.Get("address").Array() { 254 logReq.Address = append(logReq.Address, addr.String()) 255 } 256 for _, topics := range req.Get("topics").Array() { 257 if topics.IsArray() { 258 var topicArr []string 259 for _, topic := range topics.Array() { 260 topicArr = append(topicArr, util.Remove0xPrefix(topic.String())) 261 } 262 logReq.Topics = append(logReq.Topics, topicArr) 263 } else { 264 logReq.Topics = append(logReq.Topics, []string{util.Remove0xPrefix(topics.String())}) 265 } 266 } 267 } 268 return &logReq, nil 269 } 270 271 func parseCallObject(in *gjson.Result) (address.Address, string, uint64, *big.Int, *big.Int, []byte, error) { 272 var ( 273 from address.Address 274 to string 275 gasLimit uint64 276 gasPrice *big.Int = big.NewInt(0) 277 value *big.Int = big.NewInt(0) 278 data []byte 279 err error 280 ) 281 fromStr := in.Get("params.0.from").String() 282 if fromStr == "" { 283 fromStr = "0x0000000000000000000000000000000000000000" 284 } 285 if from, err = ethAddrToIoAddr(fromStr); err != nil { 286 return nil, "", 0, nil, nil, nil, err 287 } 288 289 toStr := in.Get("params.0.to").String() 290 if toStr != "" { 291 ioAddr, err := ethAddrToIoAddr(toStr) 292 if err != nil { 293 return nil, "", 0, nil, nil, nil, err 294 } 295 to = ioAddr.String() 296 } 297 298 gasStr := in.Get("params.0.gas").String() 299 if gasStr != "" { 300 if gasLimit, err = hexStringToNumber(gasStr); err != nil { 301 return nil, "", 0, nil, nil, nil, err 302 } 303 } 304 305 gasPriceStr := in.Get("params.0.gasPrice").String() 306 if gasPriceStr != "" { 307 var ok bool 308 if gasPrice, ok = new(big.Int).SetString(util.Remove0xPrefix(gasPriceStr), 16); !ok { 309 return nil, "", 0, nil, nil, nil, errors.Wrapf(errUnkownType, "gasPrice: %s", gasPriceStr) 310 } 311 } 312 313 valStr := in.Get("params.0.value").String() 314 if valStr != "" { 315 var ok bool 316 if value, ok = new(big.Int).SetString(util.Remove0xPrefix(valStr), 16); !ok { 317 return nil, "", 0, nil, nil, nil, errors.Wrapf(errUnkownType, "value: %s", valStr) 318 } 319 } 320 321 input := in.Get("params.0.input") 322 if input.Exists() { 323 data = common.FromHex(input.String()) 324 } else { 325 data = common.FromHex(in.Get("params.0.data").String()) 326 } 327 return from, to, gasLimit, gasPrice, value, data, nil 328 } 329 330 func (svr *web3Handler) getLogQueryRange(fromStr, toStr string, logHeight uint64) (from uint64, to uint64, hasNewLogs bool, err error) { 331 if from, to, err = svr.parseBlockRange(fromStr, toStr); err != nil { 332 return 333 } 334 switch { 335 case logHeight < from: 336 hasNewLogs = true 337 return 338 case logHeight > to: 339 hasNewLogs = false 340 return 341 default: 342 from = logHeight 343 hasNewLogs = true 344 return 345 } 346 } 347 348 func loadFilterFromCache(c apiCache, filterID string) (filterObject, error) { 349 dataStr, isFound := c.Get(filterID) 350 if !isFound { 351 return filterObject{}, errInvalidFilterID 352 } 353 var filterObj filterObject 354 if err := json.Unmarshal([]byte(dataStr), &filterObj); err != nil { 355 return filterObject{}, err 356 } 357 return filterObj, nil 358 } 359 360 func newAPICache(expireTime time.Duration, remoteURL string) apiCache { 361 redisClient := redis.NewClient(&redis.Options{ 362 Addr: remoteURL, 363 Password: "", // no password set 364 DB: 0, // use default DB 365 }) 366 if redisClient.Ping(context.Background()).Err() != nil { 367 log.L().Info("local cache is used as API cache") 368 filterCache, _ := ttl.NewCache(ttl.AutoExpireOption(expireTime)) 369 return &localCache{ 370 ttlCache: filterCache, 371 } 372 } 373 log.L().Info("remote cache is used as API cache") 374 return &remoteCache{ 375 redisCache: redisClient, 376 expireTime: expireTime, 377 } 378 } 379 380 type apiCache interface { 381 Set(key string, data []byte) error 382 Del(key string) bool 383 Get(key string) ([]byte, bool) 384 } 385 386 type localCache struct { 387 ttlCache *ttl.Cache 388 } 389 390 func (c *localCache) Set(key string, data []byte) error { 391 if c.ttlCache == nil { 392 return errNullPointer 393 } 394 c.ttlCache.Set(key, data) 395 return nil 396 } 397 398 func (c *localCache) Del(key string) bool { 399 if c.ttlCache == nil { 400 return false 401 } 402 return c.ttlCache.Delete(key) 403 } 404 405 func (c *localCache) Get(key string) ([]byte, bool) { 406 if c.ttlCache == nil { 407 return nil, false 408 } 409 val, exist := c.ttlCache.Get(key) 410 if !exist { 411 return nil, false 412 } 413 ret, ok := val.([]byte) 414 return ret, ok 415 } 416 417 type remoteCache struct { 418 redisCache *redis.Client 419 expireTime time.Duration 420 } 421 422 func (c *remoteCache) Set(key string, data []byte) error { 423 if c.redisCache == nil { 424 return errNullPointer 425 } 426 return c.redisCache.Set(context.Background(), key, data, c.expireTime).Err() 427 } 428 429 func (c *remoteCache) Del(key string) bool { 430 if c.redisCache == nil { 431 return false 432 } 433 err := c.redisCache.Unlink(context.Background(), key).Err() 434 return err == nil 435 } 436 437 func (c *remoteCache) Get(key string) ([]byte, bool) { 438 if c.redisCache == nil { 439 return nil, false 440 } 441 ret, err := c.redisCache.Get(context.Background(), key).Bytes() 442 if err == redis.Nil { 443 return nil, false 444 } else if err != nil { 445 return nil, false 446 } 447 c.redisCache.Expire(context.Background(), key, c.expireTime) 448 return ret, true 449 } 450 451 // fromLoggerStructLogs converts logger.StructLog to apitypes.StructLog 452 func fromLoggerStructLogs(logs []logger.StructLog) []apitypes.StructLog { 453 ret := make([]apitypes.StructLog, len(logs)) 454 for index, log := range logs { 455 ret[index] = apitypes.StructLog{ 456 Pc: log.Pc, 457 Op: log.Op, 458 Gas: math.HexOrDecimal64(log.Gas), 459 GasCost: math.HexOrDecimal64(log.GasCost), 460 Memory: log.Memory, 461 MemorySize: log.MemorySize, 462 Stack: log.Stack, 463 ReturnData: log.ReturnData, 464 Storage: log.Storage, 465 Depth: log.Depth, 466 RefundCounter: log.RefundCounter, 467 OpName: log.OpName(), 468 ErrorString: log.ErrorString(), 469 } 470 } 471 return ret 472 } 473 474 func newGetTransactionResult( 475 blkHash *hash.Hash256, 476 selp *action.SealedEnvelope, 477 receipt *action.Receipt, 478 evmChainID uint32, 479 ) (*getTransactionResult, error) { 480 act, ok := selp.Action().(action.EthCompatibleAction) 481 if !ok { 482 actHash, _ := selp.Hash() 483 return nil, errors.Wrapf(errUnsupportedAction, "actHash: %s", hex.EncodeToString(actHash[:])) 484 } 485 ethTx, err := act.ToEthTx(0) 486 if err != nil { 487 return nil, err 488 } 489 var to *string 490 if ethTx.To() != nil { 491 tmp := ethTx.To().String() 492 to = &tmp 493 } 494 495 signer, err := action.NewEthSigner(iotextypes.Encoding(selp.Encoding()), evmChainID) 496 if err != nil { 497 return nil, err 498 } 499 tx, err := action.RawTxToSignedTx(ethTx, signer, selp.Signature()) 500 if err != nil { 501 return nil, err 502 } 503 return &getTransactionResult{ 504 blockHash: blkHash, 505 to: to, 506 ethTx: tx, 507 receipt: receipt, 508 pubkey: selp.SrcPubkey(), 509 }, nil 510 }