github.com/iotexproject/iotex-core@v1.14.1-rc1/api/coreservice.go (about) 1 // Copyright (c) 2022 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package api 7 8 import ( 9 "bytes" 10 "context" 11 "encoding/hex" 12 "fmt" 13 "math" 14 "math/big" 15 "strconv" 16 "time" 17 18 "github.com/ethereum/go-ethereum/core/vm" 19 "github.com/ethereum/go-ethereum/eth/tracers" 20 21 // Force-load the tracer engines to trigger registration 22 _ "github.com/ethereum/go-ethereum/eth/tracers/js" 23 _ "github.com/ethereum/go-ethereum/eth/tracers/native" 24 25 "github.com/ethereum/go-ethereum/eth/tracers/logger" 26 "github.com/pkg/errors" 27 "go.uber.org/zap" 28 "golang.org/x/sync/errgroup" 29 "google.golang.org/genproto/googleapis/rpc/errdetails" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/status" 32 "google.golang.org/protobuf/proto" 33 "google.golang.org/protobuf/types/known/durationpb" 34 "google.golang.org/protobuf/types/known/timestamppb" 35 36 "github.com/iotexproject/go-pkgs/hash" 37 "github.com/iotexproject/go-pkgs/util" 38 "github.com/iotexproject/iotex-address/address" 39 "github.com/iotexproject/iotex-election/committee" 40 "github.com/iotexproject/iotex-proto/golang/iotexapi" 41 "github.com/iotexproject/iotex-proto/golang/iotextypes" 42 43 "github.com/iotexproject/iotex-core/action" 44 "github.com/iotexproject/iotex-core/action/protocol" 45 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 46 "github.com/iotexproject/iotex-core/action/protocol/execution/evm" 47 "github.com/iotexproject/iotex-core/action/protocol/poll" 48 "github.com/iotexproject/iotex-core/action/protocol/rewarding" 49 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 50 "github.com/iotexproject/iotex-core/actpool" 51 logfilter "github.com/iotexproject/iotex-core/api/logfilter" 52 apitypes "github.com/iotexproject/iotex-core/api/types" 53 "github.com/iotexproject/iotex-core/blockchain" 54 "github.com/iotexproject/iotex-core/blockchain/block" 55 "github.com/iotexproject/iotex-core/blockchain/blockdao" 56 "github.com/iotexproject/iotex-core/blockchain/filedao" 57 "github.com/iotexproject/iotex-core/blockchain/genesis" 58 "github.com/iotexproject/iotex-core/blockindex" 59 "github.com/iotexproject/iotex-core/blocksync" 60 "github.com/iotexproject/iotex-core/db" 61 "github.com/iotexproject/iotex-core/gasstation" 62 "github.com/iotexproject/iotex-core/pkg/log" 63 batch "github.com/iotexproject/iotex-core/pkg/messagebatcher" 64 "github.com/iotexproject/iotex-core/pkg/tracer" 65 "github.com/iotexproject/iotex-core/pkg/version" 66 "github.com/iotexproject/iotex-core/server/itx/nodestats" 67 "github.com/iotexproject/iotex-core/state" 68 "github.com/iotexproject/iotex-core/state/factory" 69 ) 70 71 const _workerNumbers int = 5 72 const ( 73 // defaultTraceTimeout is the amount of time a single transaction can execute 74 // by default before being forcefully aborted. 75 defaultTraceTimeout = 5 * time.Second 76 ) 77 78 type ( 79 // CoreService provides api interface for user to interact with blockchain data 80 CoreService interface { 81 // Account returns the metadata of an account 82 Account(addr address.Address) (*iotextypes.AccountMeta, *iotextypes.BlockIdentifier, error) 83 // ChainMeta returns blockchain metadata 84 ChainMeta() (*iotextypes.ChainMeta, string, error) 85 // ServerMeta gets the server metadata 86 ServerMeta() (packageVersion string, packageCommitID string, gitStatus string, goVersion string, buildTime string) 87 // SendAction is the API to send an action to blockchain. 88 SendAction(ctx context.Context, in *iotextypes.Action) (string, error) 89 // ReadContract reads the state in a contract address specified by the slot 90 ReadContract(ctx context.Context, callerAddr address.Address, sc *action.Execution) (string, *iotextypes.Receipt, error) 91 // ReadState reads state on blockchain 92 ReadState(protocolID string, height string, methodName []byte, arguments [][]byte) (*iotexapi.ReadStateResponse, error) 93 // SuggestGasPrice suggests gas price 94 SuggestGasPrice() (uint64, error) 95 // EstimateGasForAction estimates gas for action 96 EstimateGasForAction(ctx context.Context, in *iotextypes.Action) (uint64, error) 97 // EpochMeta gets epoch metadata 98 EpochMeta(epochNum uint64) (*iotextypes.EpochData, uint64, []*iotexapi.BlockProducerInfo, error) 99 // RawBlocks gets raw block data 100 RawBlocks(startHeight uint64, count uint64, withReceipts bool, withTransactionLogs bool) ([]*iotexapi.BlockInfo, error) 101 // ElectionBuckets returns the native election buckets. 102 ElectionBuckets(epochNum uint64) ([]*iotextypes.ElectionBucket, error) 103 // ReceiptByActionHash returns receipt by action hash 104 ReceiptByActionHash(h hash.Hash256) (*action.Receipt, error) 105 // TransactionLogByActionHash returns transaction log by action hash 106 TransactionLogByActionHash(actHash string) (*iotextypes.TransactionLog, error) 107 // TransactionLogByBlockHeight returns transaction log by block height 108 TransactionLogByBlockHeight(blockHeight uint64) (*iotextypes.BlockIdentifier, *iotextypes.TransactionLogs, error) 109 110 // Start starts the API server 111 Start(ctx context.Context) error 112 // Stop stops the API server 113 Stop(ctx context.Context) error 114 // Actions returns actions within the range 115 Actions(start uint64, count uint64) ([]*iotexapi.ActionInfo, error) 116 // TODO: unify the three get action by hash methods: Action, ActionByActionHash, PendingActionByActionHash 117 // Action returns action by action hash 118 Action(actionHash string, checkPending bool) (*iotexapi.ActionInfo, error) 119 // ActionsByAddress returns all actions associated with an address 120 ActionsByAddress(addr address.Address, start uint64, count uint64) ([]*iotexapi.ActionInfo, error) 121 // ActionByActionHash returns action by action hash 122 ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, *block.Block, uint32, error) 123 // PendingActionByActionHash returns action by action hash 124 PendingActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, error) 125 // ActPoolActions returns the all Transaction Identifiers in the actpool 126 ActionsInActPool(actHashes []string) ([]*action.SealedEnvelope, error) 127 // BlockByHeightRange returns blocks within the height range 128 BlockByHeightRange(uint64, uint64) ([]*apitypes.BlockWithReceipts, error) 129 // BlockByHeight returns the block and its receipt from block height 130 BlockByHeight(uint64) (*apitypes.BlockWithReceipts, error) 131 // BlockByHash returns the block and its receipt 132 BlockByHash(string) (*apitypes.BlockWithReceipts, error) 133 // UnconfirmedActionsByAddress returns all unconfirmed actions in actpool associated with an address 134 UnconfirmedActionsByAddress(address string, start uint64, count uint64) ([]*iotexapi.ActionInfo, error) 135 // EstimateGasForNonExecution estimates action gas except execution 136 EstimateGasForNonExecution(action.Action) (uint64, error) 137 // EstimateExecutionGasConsumption estimate gas consumption for execution action 138 EstimateExecutionGasConsumption(ctx context.Context, sc *action.Execution, callerAddr address.Address) (uint64, error) 139 // LogsInBlockByHash filter logs in the block by hash 140 LogsInBlockByHash(filter *logfilter.LogFilter, blockHash hash.Hash256) ([]*action.Log, error) 141 // LogsInRange filter logs among [start, end] blocks 142 LogsInRange(filter *logfilter.LogFilter, start, end, paginationSize uint64) ([]*action.Log, []hash.Hash256, error) 143 // Genesis returns the genesis of the chain 144 Genesis() genesis.Genesis 145 // EVMNetworkID returns the network id of evm 146 EVMNetworkID() uint32 147 // ChainID returns the chain id of evm 148 ChainID() uint32 149 // ReadContractStorage reads contract's storage 150 ReadContractStorage(ctx context.Context, addr address.Address, key []byte) ([]byte, error) 151 // ChainListener returns the instance of Listener 152 ChainListener() apitypes.Listener 153 // SimulateExecution simulates execution 154 SimulateExecution(context.Context, address.Address, *action.Execution) ([]byte, *action.Receipt, error) 155 // SyncingProgress returns the syncing status of node 156 SyncingProgress() (uint64, uint64, uint64) 157 // TipHeight returns the tip of the chain 158 TipHeight() uint64 159 // PendingNonce returns the pending nonce of an account 160 PendingNonce(address.Address) (uint64, error) 161 // ReceiveBlock broadcasts the block to api subscribers 162 ReceiveBlock(blk *block.Block) error 163 // BlockHashByBlockHeight returns block hash by block height 164 BlockHashByBlockHeight(blkHeight uint64) (hash.Hash256, error) 165 // TraceTransaction returns the trace result of a transaction 166 TraceTransaction(ctx context.Context, actHash string, config *tracers.TraceConfig) ([]byte, *action.Receipt, any, error) 167 // TraceCall returns the trace result of a call 168 TraceCall(ctx context.Context, 169 callerAddr address.Address, 170 blkNumOrHash any, 171 contractAddress string, 172 nonce uint64, 173 amount *big.Int, 174 gasLimit uint64, 175 data []byte, 176 config *tracers.TraceConfig) ([]byte, *action.Receipt, any, error) 177 178 // Track tracks the api call 179 Track(ctx context.Context, start time.Time, method string, size int64, success bool) 180 } 181 182 // coreService implements the CoreService interface 183 coreService struct { 184 bc blockchain.Blockchain 185 bs blocksync.BlockSync 186 sf factory.Factory 187 dao blockdao.BlockDAO 188 indexer blockindex.Indexer 189 bfIndexer blockindex.BloomFilterIndexer 190 ap actpool.ActPool 191 gs *gasstation.GasStation 192 broadcastHandler BroadcastOutbound 193 cfg Config 194 registry *protocol.Registry 195 chainListener apitypes.Listener 196 electionCommittee committee.Committee 197 readCache *ReadCache 198 messageBatcher *batch.Manager 199 apiStats *nodestats.APILocalStats 200 sgdIndexer blockindex.SGDRegistry 201 getBlockTime evm.GetBlockTime 202 } 203 204 // jobDesc provides a struct to get and store logs in core.LogsInRange 205 jobDesc struct { 206 idx int 207 blkNum uint64 208 } 209 ) 210 211 // Option is the option to override the api config 212 type Option func(cfg *coreService) 213 214 // BroadcastOutbound sends a broadcast message to the whole network 215 type BroadcastOutbound func(ctx context.Context, chainID uint32, msg proto.Message) error 216 217 // WithBroadcastOutbound is the option to broadcast msg outbound 218 func WithBroadcastOutbound(broadcastHandler BroadcastOutbound) Option { 219 return func(svr *coreService) { 220 svr.broadcastHandler = broadcastHandler 221 } 222 } 223 224 // WithNativeElection is the option to return native election data through API. 225 func WithNativeElection(committee committee.Committee) Option { 226 return func(svr *coreService) { 227 svr.electionCommittee = committee 228 } 229 } 230 231 // WithAPIStats is the option to return RPC stats through API. 232 func WithAPIStats(stats *nodestats.APILocalStats) Option { 233 return func(svr *coreService) { 234 svr.apiStats = stats 235 } 236 } 237 238 // WithSGDIndexer is the option to return SGD Indexer through API. 239 func WithSGDIndexer(sgdIndexer blockindex.SGDRegistry) Option { 240 return func(svr *coreService) { 241 svr.sgdIndexer = sgdIndexer 242 } 243 } 244 245 type intrinsicGasCalculator interface { 246 IntrinsicGas() (uint64, error) 247 } 248 249 var ( 250 // ErrNotFound indicates the record isn't found 251 ErrNotFound = errors.New("not found") 252 ) 253 254 // newcoreService creates a api server that contains major blockchain components 255 func newCoreService( 256 cfg Config, 257 chain blockchain.Blockchain, 258 bs blocksync.BlockSync, 259 sf factory.Factory, 260 dao blockdao.BlockDAO, 261 indexer blockindex.Indexer, 262 bfIndexer blockindex.BloomFilterIndexer, 263 actPool actpool.ActPool, 264 registry *protocol.Registry, 265 getBlockTime evm.GetBlockTime, 266 opts ...Option, 267 ) (CoreService, error) { 268 if cfg == (Config{}) { 269 log.L().Warn("API server is not configured.") 270 cfg = DefaultConfig 271 } 272 273 if cfg.RangeQueryLimit < uint64(cfg.TpsWindow) { 274 return nil, errors.New("range query upper limit cannot be less than tps window") 275 } 276 277 core := coreService{ 278 bc: chain, 279 bs: bs, 280 sf: sf, 281 dao: dao, 282 indexer: indexer, 283 bfIndexer: bfIndexer, 284 ap: actPool, 285 cfg: cfg, 286 registry: registry, 287 chainListener: NewChainListener(500), 288 gs: gasstation.NewGasStation(chain, dao, cfg.GasStation), 289 readCache: NewReadCache(), 290 getBlockTime: getBlockTime, 291 } 292 293 for _, opt := range opts { 294 opt(&core) 295 } 296 297 if core.broadcastHandler != nil { 298 core.messageBatcher = batch.NewManager(func(msg *batch.Message) error { 299 return core.broadcastHandler(context.Background(), core.bc.ChainID(), msg.Data) 300 }) 301 } 302 303 return &core, nil 304 } 305 306 // Account returns the metadata of an account 307 func (core *coreService) Account(addr address.Address) (*iotextypes.AccountMeta, *iotextypes.BlockIdentifier, error) { 308 ctx, span := tracer.NewSpan(context.Background(), "coreService.Account") 309 defer span.End() 310 addrStr := addr.String() 311 if addrStr == address.RewardingPoolAddr || addrStr == address.StakingBucketPoolAddr { 312 return core.getProtocolAccount(ctx, addrStr) 313 } 314 span.AddEvent("accountutil.AccountStateWithHeight") 315 ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) 316 state, tipHeight, err := accountutil.AccountStateWithHeight(ctx, core.sf, addr) 317 if err != nil { 318 return nil, nil, status.Error(codes.NotFound, err.Error()) 319 } 320 span.AddEvent("ap.GetPendingNonce") 321 pendingNonce, err := core.ap.GetPendingNonce(addrStr) 322 if err != nil { 323 return nil, nil, status.Error(codes.Internal, err.Error()) 324 } 325 if core.indexer == nil { 326 return nil, nil, status.Error(codes.NotFound, blockindex.ErrActionIndexNA.Error()) 327 } 328 span.AddEvent("indexer.GetActionCount") 329 numActions, err := core.indexer.GetActionCountByAddress(hash.BytesToHash160(addr.Bytes())) 330 if err != nil { 331 return nil, nil, status.Error(codes.NotFound, err.Error()) 332 } 333 // TODO: deprecate nonce field in account meta 334 accountMeta := &iotextypes.AccountMeta{ 335 Address: addrStr, 336 Balance: state.Balance.String(), 337 PendingNonce: pendingNonce, 338 NumActions: numActions, 339 IsContract: state.IsContract(), 340 } 341 if state.IsContract() { 342 var code protocol.SerializableBytes 343 _, err = core.sf.State(&code, protocol.NamespaceOption(evm.CodeKVNameSpace), protocol.KeyOption(state.CodeHash)) 344 if err != nil { 345 return nil, nil, status.Error(codes.NotFound, err.Error()) 346 } 347 accountMeta.ContractByteCode = code 348 } 349 span.AddEvent("bc.BlockHeaderByHeight") 350 header, err := core.bc.BlockHeaderByHeight(tipHeight) 351 if err != nil { 352 return nil, nil, status.Error(codes.NotFound, err.Error()) 353 } 354 hash := header.HashBlock() 355 span.AddEvent("coreService.Account.End") 356 return accountMeta, &iotextypes.BlockIdentifier{ 357 Hash: hex.EncodeToString(hash[:]), 358 Height: tipHeight, 359 }, nil 360 } 361 362 // ChainMeta returns blockchain metadata 363 func (core *coreService) ChainMeta() (*iotextypes.ChainMeta, string, error) { 364 tipHeight := core.bc.TipHeight() 365 if tipHeight == 0 { 366 return &iotextypes.ChainMeta{ 367 Epoch: &iotextypes.EpochData{}, 368 ChainID: core.bc.ChainID(), 369 }, "", nil 370 } 371 syncStatus := "" 372 if core.bs != nil { 373 _, _, _, syncStatus = core.bs.SyncStatus() 374 } 375 chainMeta := &iotextypes.ChainMeta{ 376 Height: tipHeight, 377 ChainID: core.bc.ChainID(), 378 } 379 if core.indexer == nil { 380 return chainMeta, syncStatus, nil 381 } 382 totalActions, err := core.indexer.GetTotalActions() 383 if err != nil { 384 return nil, "", status.Error(codes.Internal, err.Error()) 385 } 386 blockLimit := int64(core.cfg.TpsWindow) 387 if blockLimit <= 0 { 388 return nil, "", status.Errorf(codes.Internal, "block limit is %d", blockLimit) 389 } 390 391 // avoid genesis block 392 if int64(tipHeight) < blockLimit { 393 blockLimit = int64(tipHeight) 394 } 395 blkStores, err := core.BlockByHeightRange(tipHeight-uint64(blockLimit)+1, uint64(blockLimit)) 396 if err != nil { 397 return nil, "", status.Error(codes.NotFound, err.Error()) 398 } 399 if len(blkStores) == 0 { 400 return nil, "", status.Error(codes.NotFound, "get 0 blocks! not able to calculate aps") 401 } 402 403 var numActions uint64 404 for _, blkStore := range blkStores { 405 numActions += uint64(len(blkStore.Block.Actions)) 406 } 407 408 t1 := blkStores[0].Block.Timestamp() 409 t2 := blkStores[len(blkStores)-1].Block.Timestamp() 410 // duration of time difference in milli-seconds 411 // TODO: use config.Genesis.BlockInterval after PR1289 merges 412 timeDiff := (t2.Sub(t1) + 10*time.Second) / time.Millisecond 413 tps := float32(numActions*1000) / float32(timeDiff) 414 415 chainMeta.NumActions = int64(totalActions) 416 chainMeta.Tps = int64(math.Ceil(float64(tps))) 417 chainMeta.TpsFloat = tps 418 419 rp := rolldpos.FindProtocol(core.registry) 420 if rp != nil { 421 epochNum := rp.GetEpochNum(tipHeight) 422 epochHeight := rp.GetEpochHeight(epochNum) 423 gravityChainStartHeight, err := core.getGravityChainStartHeight(epochHeight) 424 if err != nil { 425 return nil, "", status.Error(codes.NotFound, err.Error()) 426 } 427 chainMeta.Epoch = &iotextypes.EpochData{ 428 Num: epochNum, 429 Height: epochHeight, 430 GravityChainStartHeight: gravityChainStartHeight, 431 } 432 } 433 return chainMeta, syncStatus, nil 434 } 435 436 // ServerMeta gets the server metadata 437 func (core *coreService) ServerMeta() (packageVersion string, packageCommitID string, gitStatus string, goVersion string, buildTime string) { 438 packageVersion = version.PackageVersion 439 packageCommitID = version.PackageCommitID 440 gitStatus = version.GitStatus 441 goVersion = version.GoVersion 442 buildTime = version.BuildTime 443 return 444 } 445 446 // SendAction is the API to send an action to blockchain. 447 func (core *coreService) SendAction(ctx context.Context, in *iotextypes.Action) (string, error) { 448 log.Logger("api").Debug("receive send action request") 449 selp, err := (&action.Deserializer{}).SetEvmNetworkID(core.EVMNetworkID()).ActionToSealedEnvelope(in) 450 if err != nil { 451 return "", status.Error(codes.InvalidArgument, err.Error()) 452 } 453 454 // reject action if chainID is not matched at KamchatkaHeight 455 if err := core.validateChainID(in.GetCore().GetChainID()); err != nil { 456 return "", err 457 } 458 // reject action if a replay tx is not whitelisted 459 var ( 460 g = core.Genesis() 461 deployer = selp.SenderAddress() 462 ) 463 if selp.Encoding() == uint32(iotextypes.Encoding_ETHEREUM_UNPROTECTED) && !g.IsDeployerWhitelisted(deployer) { 464 return "", status.Errorf(codes.InvalidArgument, "replay deployer %v not whitelisted", deployer.Hex()) 465 } 466 467 // Add to local actpool 468 ctx = protocol.WithRegistry(ctx, core.registry) 469 hash, err := selp.Hash() 470 if err != nil { 471 return "", err 472 } 473 l := log.Logger("api").With(zap.String("actionHash", hex.EncodeToString(hash[:]))) 474 if err = core.ap.Add(ctx, selp); err != nil { 475 txBytes, serErr := proto.Marshal(in) 476 if serErr != nil { 477 l.Error("Data corruption", zap.Error(serErr)) 478 } else { 479 l.With(zap.String("txBytes", hex.EncodeToString(txBytes))).Debug("Failed to accept action", zap.Error(err)) 480 } 481 st := status.New(codes.Internal, err.Error()) 482 br := &errdetails.BadRequest{ 483 FieldViolations: []*errdetails.BadRequest_FieldViolation{ 484 { 485 Field: "Action rejected", 486 Description: action.LoadErrorDescription(err), 487 }, 488 }, 489 } 490 st, err := st.WithDetails(br) 491 if err != nil { 492 log.Logger("api").Panic("Unexpected error attaching metadata", zap.Error(err)) 493 } 494 return "", st.Err() 495 } 496 // If there is no error putting into local actpool, broadcast it to the network 497 if core.messageBatcher != nil { 498 err = core.messageBatcher.Put(&batch.Message{ 499 ChainID: core.bc.ChainID(), 500 Target: nil, 501 Data: in, 502 }) 503 } else { 504 err = core.broadcastHandler(ctx, core.bc.ChainID(), in) 505 } 506 if err != nil { 507 l.Warn("Failed to broadcast SendAction request.", zap.Error(err)) 508 } 509 return hex.EncodeToString(hash[:]), nil 510 } 511 512 func (core *coreService) PendingNonce(addr address.Address) (uint64, error) { 513 return core.ap.GetPendingNonce(addr.String()) 514 } 515 516 func (core *coreService) validateChainID(chainID uint32) error { 517 ge := core.bc.Genesis() 518 if ge.IsQuebec(core.bc.TipHeight()) && chainID != core.bc.ChainID() { 519 return status.Errorf(codes.InvalidArgument, "ChainID does not match, expecting %d, got %d", core.bc.ChainID(), chainID) 520 } 521 if ge.IsMidway(core.bc.TipHeight()) && chainID != core.bc.ChainID() && chainID != 0 { 522 return status.Errorf(codes.InvalidArgument, "ChainID does not match, expecting %d, got %d", core.bc.ChainID(), chainID) 523 } 524 return nil 525 } 526 527 // ReadContract reads the state in a contract address specified by the slot 528 func (core *coreService) ReadContract(ctx context.Context, callerAddr address.Address, sc *action.Execution) (string, *iotextypes.Receipt, error) { 529 log.Logger("api").Debug("receive read smart contract request") 530 key := hash.Hash160b(append([]byte(sc.Contract()), sc.Data()...)) 531 // TODO: either moving readcache into the upper layer or change the storage format 532 if d, ok := core.readCache.Get(key); ok { 533 res := iotexapi.ReadContractResponse{} 534 if err := proto.Unmarshal(d, &res); err == nil { 535 return res.Data, res.Receipt, nil 536 } 537 } 538 ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) 539 state, err := accountutil.AccountState(ctx, core.sf, callerAddr) 540 if err != nil { 541 return "", nil, status.Error(codes.InvalidArgument, err.Error()) 542 } 543 if ctx, err = core.bc.Context(ctx); err != nil { 544 return "", nil, err 545 } 546 ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 547 BlockHeight: core.bc.TipHeight(), 548 })) 549 var pendingNonce uint64 550 if protocol.MustGetFeatureCtx(ctx).RefactorFreshAccountConversion { 551 pendingNonce = state.PendingNonceConsideringFreshAccount() 552 } else { 553 pendingNonce = state.PendingNonce() 554 } 555 sc.SetNonce(pendingNonce) 556 var ( 557 g = core.bc.Genesis() 558 blockGasLimit = g.BlockGasLimitByHeight(core.bc.TipHeight()) 559 ) 560 if sc.GasLimit() == 0 || blockGasLimit < sc.GasLimit() { 561 sc.SetGasLimit(blockGasLimit) 562 } 563 sc.SetGasPrice(big.NewInt(0)) // ReadContract() is read-only, use 0 to prevent insufficient gas 564 565 retval, receipt, err := core.simulateExecution(ctx, callerAddr, sc, core.dao.GetBlockHash, core.getBlockTime) 566 if err != nil { 567 return "", nil, status.Error(codes.Internal, err.Error()) 568 } 569 // ReadContract() is read-only, if no error returned, we consider it a success 570 receipt.Status = uint64(iotextypes.ReceiptStatus_Success) 571 res := iotexapi.ReadContractResponse{ 572 Data: hex.EncodeToString(retval), 573 Receipt: receipt.ConvertToReceiptPb(), 574 } 575 if d, err := proto.Marshal(&res); err == nil { 576 core.readCache.Put(key, d) 577 } 578 return res.Data, res.Receipt, nil 579 } 580 581 // ReadState reads state on blockchain 582 func (core *coreService) ReadState(protocolID string, height string, methodName []byte, arguments [][]byte) (*iotexapi.ReadStateResponse, error) { 583 p, ok := core.registry.Find(protocolID) 584 if !ok { 585 return nil, status.Errorf(codes.Internal, "protocol %s isn't registered", protocolID) 586 } 587 data, readStateHeight, err := core.readState(context.Background(), p, height, methodName, arguments...) 588 if err != nil { 589 return nil, status.Error(codes.NotFound, err.Error()) 590 } 591 blkHash, err := core.dao.GetBlockHash(readStateHeight) 592 if err != nil { 593 if errors.Cause(err) == db.ErrNotExist { 594 return nil, status.Error(codes.NotFound, err.Error()) 595 } 596 return nil, status.Error(codes.Internal, err.Error()) 597 } 598 return &iotexapi.ReadStateResponse{ 599 Data: data, 600 BlockIdentifier: &iotextypes.BlockIdentifier{ 601 Height: readStateHeight, 602 Hash: hex.EncodeToString(blkHash[:]), 603 }, 604 }, nil 605 } 606 607 // SuggestGasPrice suggests gas price 608 func (core *coreService) SuggestGasPrice() (uint64, error) { 609 return core.gs.SuggestGasPrice() 610 } 611 612 // EstimateGasForAction estimates gas for action 613 func (core *coreService) EstimateGasForAction(ctx context.Context, in *iotextypes.Action) (uint64, error) { 614 selp, err := (&action.Deserializer{}).SetEvmNetworkID(core.EVMNetworkID()).ActionToSealedEnvelope(in) 615 if err != nil { 616 return 0, status.Error(codes.Internal, err.Error()) 617 } 618 sc, ok := selp.Action().(*action.Execution) 619 if !ok { 620 gas, err := selp.IntrinsicGas() 621 if err != nil { 622 return 0, status.Error(codes.Internal, err.Error()) 623 } 624 return gas, nil 625 } 626 callerAddr := selp.SenderAddress() 627 if callerAddr == nil { 628 return 0, status.Error(codes.Internal, "failed to get address") 629 } 630 _, receipt, err := core.SimulateExecution(ctx, callerAddr, sc) 631 if err != nil { 632 return 0, status.Error(codes.Internal, err.Error()) 633 } 634 return receipt.GasConsumed, nil 635 } 636 637 // EpochMeta gets epoch metadata 638 func (core *coreService) EpochMeta(epochNum uint64) (*iotextypes.EpochData, uint64, []*iotexapi.BlockProducerInfo, error) { 639 rp := rolldpos.FindProtocol(core.registry) 640 if rp == nil { 641 return nil, 0, nil, nil 642 } 643 if epochNum < 1 { 644 return nil, 0, nil, status.Error(codes.InvalidArgument, "epoch number cannot be less than one") 645 } 646 epochHeight := rp.GetEpochHeight(epochNum) 647 gravityChainStartHeight, err := core.getGravityChainStartHeight(epochHeight) 648 if err != nil { 649 return nil, 0, nil, status.Error(codes.NotFound, err.Error()) 650 } 651 epochData := &iotextypes.EpochData{ 652 Num: epochNum, 653 Height: epochHeight, 654 GravityChainStartHeight: gravityChainStartHeight, 655 } 656 657 pp := poll.FindProtocol(core.registry) 658 if pp == nil { 659 return nil, 0, nil, status.Error(codes.Internal, "poll protocol is not registered") 660 } 661 662 methodName := []byte("ActiveBlockProducersByEpoch") 663 arguments := [][]byte{[]byte(strconv.FormatUint(epochNum, 10))} 664 height := strconv.FormatUint(epochHeight, 10) 665 data, _, err := core.readState(context.Background(), pp, height, methodName, arguments...) 666 if err != nil { 667 return nil, 0, nil, status.Error(codes.NotFound, err.Error()) 668 } 669 670 var activeConsensusBlockProducers state.CandidateList 671 if err := activeConsensusBlockProducers.Deserialize(data); err != nil { 672 return nil, 0, nil, status.Error(codes.Internal, err.Error()) 673 } 674 675 numBlks, produce, err := core.getProductivityByEpoch(rp, epochNum, core.bc.TipHeight(), activeConsensusBlockProducers) 676 if err != nil { 677 return nil, 0, nil, status.Error(codes.NotFound, err.Error()) 678 } 679 680 methodName = []byte("BlockProducersByEpoch") 681 data, _, err = core.readState(context.Background(), pp, height, methodName, arguments...) 682 if err != nil { 683 return nil, 0, nil, status.Error(codes.NotFound, err.Error()) 684 } 685 686 var BlockProducers state.CandidateList 687 if err := BlockProducers.Deserialize(data); err != nil { 688 return nil, 0, nil, status.Error(codes.Internal, err.Error()) 689 } 690 691 var blockProducersInfo []*iotexapi.BlockProducerInfo 692 for _, bp := range BlockProducers { 693 var active bool 694 var blockProduction uint64 695 if production, ok := produce[bp.Address]; ok { 696 active = true 697 blockProduction = production 698 } 699 blockProducersInfo = append(blockProducersInfo, &iotexapi.BlockProducerInfo{ 700 Address: bp.Address, 701 Votes: bp.Votes.String(), 702 Active: active, 703 Production: blockProduction, 704 }) 705 } 706 return epochData, numBlks, blockProducersInfo, nil 707 } 708 709 // RawBlocks gets raw block data 710 func (core *coreService) RawBlocks(startHeight uint64, count uint64, withReceipts bool, withTransactionLogs bool) ([]*iotexapi.BlockInfo, error) { 711 if count == 0 || count > core.cfg.RangeQueryLimit { 712 return nil, status.Error(codes.InvalidArgument, "range exceeds the limit") 713 } 714 715 tipHeight := core.bc.TipHeight() 716 if startHeight > tipHeight { 717 return nil, status.Error(codes.InvalidArgument, "start height should not exceed tip height") 718 } 719 endHeight := startHeight + count - 1 720 if endHeight > tipHeight { 721 endHeight = tipHeight 722 } 723 var res []*iotexapi.BlockInfo 724 for height := startHeight; height <= endHeight; height++ { 725 blk, err := core.dao.GetBlockByHeight(height) 726 if err != nil { 727 return nil, status.Error(codes.NotFound, err.Error()) 728 } 729 var receiptsPb []*iotextypes.Receipt 730 if withReceipts && height > 0 { 731 receipts, err := core.dao.GetReceipts(height) 732 if err != nil { 733 return nil, status.Error(codes.NotFound, err.Error()) 734 } 735 for _, receipt := range receipts { 736 receiptsPb = append(receiptsPb, receipt.ConvertToReceiptPb()) 737 } 738 } 739 var transactionLogs *iotextypes.TransactionLogs 740 if withTransactionLogs { 741 if transactionLogs, err = core.dao.TransactionLogs(height); err != nil { 742 return nil, status.Error(codes.NotFound, err.Error()) 743 } 744 } 745 res = append(res, &iotexapi.BlockInfo{ 746 Block: blk.ConvertToBlockPb(), 747 Receipts: receiptsPb, 748 TransactionLogs: transactionLogs, 749 }) 750 } 751 return res, nil 752 } 753 754 // ChainListener returns the instance of Listener 755 func (core *coreService) ChainListener() apitypes.Listener { 756 return core.chainListener 757 } 758 759 // ElectionBuckets returns the native election buckets. 760 func (core *coreService) ElectionBuckets(epochNum uint64) ([]*iotextypes.ElectionBucket, error) { 761 if core.electionCommittee == nil { 762 return nil, status.Error(codes.Unavailable, "Native election no supported") 763 } 764 buckets, err := core.electionCommittee.NativeBucketsByEpoch(epochNum) 765 if err != nil { 766 return nil, status.Error(codes.Internal, err.Error()) 767 } 768 re := make([]*iotextypes.ElectionBucket, len(buckets)) 769 for i, b := range buckets { 770 startTime := timestamppb.New(b.StartTime()) 771 re[i] = &iotextypes.ElectionBucket{ 772 Voter: b.Voter(), 773 Candidate: b.Candidate(), 774 Amount: b.Amount().Bytes(), 775 StartTime: startTime, 776 Duration: durationpb.New(b.Duration()), 777 Decay: b.Decay(), 778 } 779 } 780 return re, nil 781 } 782 783 // ReceiptByActionHash returns receipt by action hash 784 func (core *coreService) ReceiptByActionHash(h hash.Hash256) (*action.Receipt, error) { 785 if core.indexer == nil { 786 return nil, status.Error(codes.NotFound, blockindex.ErrActionIndexNA.Error()) 787 } 788 789 actIndex, err := core.indexer.GetActionIndex(h[:]) 790 if err != nil { 791 return nil, errors.Wrap(ErrNotFound, err.Error()) 792 } 793 794 receipts, err := core.dao.GetReceipts(actIndex.BlockHeight()) 795 if err != nil { 796 return nil, err 797 } 798 if receipt := filterReceipts(receipts, h); receipt != nil { 799 return receipt, nil 800 } 801 return nil, errors.Wrapf(ErrNotFound, "failed to find receipt for action %x", h) 802 } 803 804 // TransactionLogByActionHash returns transaction log by action hash 805 func (core *coreService) TransactionLogByActionHash(actHash string) (*iotextypes.TransactionLog, error) { 806 if core.indexer == nil { 807 return nil, status.Error(codes.Unimplemented, blockindex.ErrActionIndexNA.Error()) 808 } 809 if !core.dao.ContainsTransactionLog() { 810 return nil, status.Error(codes.Unimplemented, filedao.ErrNotSupported.Error()) 811 } 812 813 h, err := hex.DecodeString(actHash) 814 if err != nil { 815 return nil, status.Error(codes.InvalidArgument, err.Error()) 816 } 817 818 actIndex, err := core.indexer.GetActionIndex(h) 819 if err != nil { 820 if errors.Cause(err) == db.ErrNotExist { 821 return nil, status.Error(codes.NotFound, err.Error()) 822 } 823 return nil, status.Error(codes.Internal, err.Error()) 824 } 825 826 sysLog, err := core.dao.TransactionLogs(actIndex.BlockHeight()) 827 if err != nil { 828 if errors.Cause(err) == db.ErrNotExist { 829 return nil, status.Error(codes.NotFound, err.Error()) 830 } 831 return nil, status.Error(codes.Internal, err.Error()) 832 } 833 834 for _, log := range sysLog.Logs { 835 if bytes.Equal(h, log.ActionHash) { 836 return log, nil 837 } 838 } 839 return nil, status.Errorf(codes.NotFound, "transaction log not found for action %s", actHash) 840 } 841 842 // TransactionLogByBlockHeight returns transaction log by block height 843 func (core *coreService) TransactionLogByBlockHeight(blockHeight uint64) (*iotextypes.BlockIdentifier, *iotextypes.TransactionLogs, error) { 844 if !core.dao.ContainsTransactionLog() { 845 return nil, nil, status.Error(codes.Unimplemented, filedao.ErrNotSupported.Error()) 846 } 847 848 tip, err := core.dao.Height() 849 if err != nil { 850 return nil, nil, status.Error(codes.Internal, err.Error()) 851 } 852 if blockHeight < 1 || blockHeight > tip { 853 return nil, nil, status.Errorf(codes.InvalidArgument, "invalid block height = %d", blockHeight) 854 } 855 856 h, err := core.dao.GetBlockHash(blockHeight) 857 if err != nil { 858 if errors.Cause(err) == db.ErrNotExist { 859 return nil, nil, status.Error(codes.NotFound, err.Error()) 860 } 861 return nil, nil, status.Error(codes.Internal, err.Error()) 862 } 863 864 blockIdentifier := &iotextypes.BlockIdentifier{ 865 Hash: hex.EncodeToString(h[:]), 866 Height: blockHeight, 867 } 868 sysLog, err := core.dao.TransactionLogs(blockHeight) 869 if err != nil { 870 if errors.Cause(err) == db.ErrNotExist { 871 // should return empty, no transaction happened in block 872 return blockIdentifier, nil, nil 873 } 874 return nil, nil, status.Error(codes.Internal, err.Error()) 875 } 876 return blockIdentifier, sysLog, nil 877 } 878 879 func (core *coreService) TipHeight() uint64 { 880 return core.bc.TipHeight() 881 } 882 883 // Start starts the API server 884 func (core *coreService) Start(_ context.Context) error { 885 if err := core.chainListener.Start(); err != nil { 886 return errors.Wrap(err, "failed to start blockchain listener") 887 } 888 if core.messageBatcher != nil { 889 if err := core.messageBatcher.Start(); err != nil { 890 return errors.Wrap(err, "failed to start message batcher") 891 } 892 } 893 return nil 894 } 895 896 // Stop stops the API server 897 func (core *coreService) Stop(_ context.Context) error { 898 if core.messageBatcher != nil { 899 if err := core.messageBatcher.Stop(); err != nil { 900 return errors.Wrap(err, "failed to stop message batcher") 901 } 902 } 903 return core.chainListener.Stop() 904 } 905 906 func (core *coreService) readState(ctx context.Context, p protocol.Protocol, height string, methodName []byte, arguments ...[]byte) ([]byte, uint64, error) { 907 key := ReadKey{ 908 Name: p.Name(), 909 Height: height, 910 Method: methodName, 911 Args: arguments, 912 } 913 if d, ok := core.readCache.Get(key.Hash()); ok { 914 var h uint64 915 if height != "" { 916 h, _ = strconv.ParseUint(height, 0, 64) 917 } 918 return d, h, nil 919 } 920 921 // TODO: need to complete the context 922 tipHeight := core.bc.TipHeight() 923 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 924 BlockHeight: tipHeight, 925 }) 926 ctx = genesis.WithGenesisContext( 927 protocol.WithRegistry(ctx, core.registry), 928 core.bc.Genesis(), 929 ) 930 ctx = protocol.WithFeatureCtx(protocol.WithFeatureWithHeightCtx(ctx)) 931 932 rp := rolldpos.FindProtocol(core.registry) 933 if rp == nil { 934 return nil, uint64(0), errors.New("rolldpos is not registered") 935 } 936 937 tipEpochNum := rp.GetEpochNum(tipHeight) 938 if height != "" { 939 inputHeight, err := strconv.ParseUint(height, 0, 64) 940 if err != nil { 941 return nil, uint64(0), err 942 } 943 inputEpochNum := rp.GetEpochNum(inputHeight) 944 if inputEpochNum < tipEpochNum { 945 // old data, wrap to history state reader 946 d, h, err := p.ReadState(ctx, factory.NewHistoryStateReader(core.sf, rp.GetEpochHeight(inputEpochNum)), methodName, arguments...) 947 if err == nil { 948 core.readCache.Put(key.Hash(), d) 949 } 950 return d, h, err 951 } 952 } 953 954 // TODO: need to distinguish user error and system error 955 d, h, err := p.ReadState(ctx, core.sf, methodName, arguments...) 956 if err == nil { 957 core.readCache.Put(key.Hash(), d) 958 } 959 return d, h, err 960 } 961 962 func (core *coreService) getActionsFromIndex(totalActions, start, count uint64) ([]*iotexapi.ActionInfo, error) { 963 hashes, err := core.indexer.GetActionHashFromIndex(start, count) 964 if err != nil { 965 return nil, status.Error(codes.Unavailable, err.Error()) 966 } 967 var actionInfo []*iotexapi.ActionInfo 968 for i := range hashes { 969 act, err := core.getAction(hash.BytesToHash256(hashes[i]), false) 970 if err != nil { 971 return nil, status.Error(codes.Unavailable, err.Error()) 972 } 973 actionInfo = append(actionInfo, act) 974 } 975 return actionInfo, nil 976 } 977 978 // Actions returns actions within the range 979 func (core *coreService) Actions(start uint64, count uint64) ([]*iotexapi.ActionInfo, error) { 980 if err := core.checkActionIndex(); err != nil { 981 return nil, err 982 } 983 if count == 0 { 984 return nil, status.Error(codes.InvalidArgument, "count must be greater than zero") 985 } 986 if count > core.cfg.RangeQueryLimit { 987 return nil, status.Error(codes.InvalidArgument, "range exceeds the limit") 988 } 989 990 totalActions, err := core.indexer.GetTotalActions() 991 if err != nil { 992 return nil, status.Error(codes.Internal, err.Error()) 993 } 994 if start >= totalActions { 995 return nil, status.Error(codes.InvalidArgument, "start exceeds the total actions in the block") 996 } 997 if totalActions == uint64(0) || count == 0 { 998 return []*iotexapi.ActionInfo{}, nil 999 } 1000 if start+count > totalActions { 1001 count = totalActions - start 1002 } 1003 if core.indexer != nil { 1004 return core.getActionsFromIndex(totalActions, start, count) 1005 } 1006 // Finding actions in reverse order saves time for querying most recent actions 1007 reverseStart := totalActions - (start + count) 1008 if totalActions < start+count { 1009 reverseStart = uint64(0) 1010 count = totalActions - start 1011 } 1012 1013 var res []*iotexapi.ActionInfo 1014 var actsList [][]*iotexapi.ActionInfo 1015 var hit bool 1016 for height := core.bc.TipHeight(); height >= 1 && count > 0; height-- { 1017 blk, err := core.dao.GetBlockByHeight(height) 1018 if err != nil { 1019 return nil, status.Error(codes.NotFound, err.Error()) 1020 } 1021 if !hit && reverseStart >= uint64(len(blk.Actions)) { 1022 reverseStart -= uint64(len(blk.Actions)) 1023 continue 1024 } 1025 // now reverseStart < len(blk.Actions), we are going to fetch actions from this block 1026 hit = true 1027 acts := core.reverseActionsInBlock(blk, reverseStart, count) 1028 actsList = append(actsList, acts) 1029 count -= uint64(len(acts)) 1030 reverseStart = 0 1031 } 1032 for i := len(actsList) - 1; i >= 0; i-- { 1033 res = append(res, actsList[i]...) 1034 } 1035 return res, nil 1036 } 1037 1038 // Action returns action by action hash 1039 func (core *coreService) Action(actionHash string, checkPending bool) (*iotexapi.ActionInfo, error) { 1040 if err := core.checkActionIndex(); err != nil { 1041 return nil, err 1042 } 1043 actHash, err := hash.HexStringToHash256(actionHash) 1044 if err != nil { 1045 return nil, status.Error(codes.InvalidArgument, err.Error()) 1046 } 1047 act, err := core.getAction(actHash, checkPending) 1048 if err != nil { 1049 return nil, status.Error(codes.Unavailable, err.Error()) 1050 } 1051 return act, nil 1052 } 1053 1054 // ActionsByAddress returns all actions associated with an address 1055 func (core *coreService) ActionsByAddress(addr address.Address, start uint64, count uint64) ([]*iotexapi.ActionInfo, error) { 1056 if err := core.checkActionIndex(); err != nil { 1057 return nil, err 1058 } 1059 if count == 0 { 1060 return nil, status.Error(codes.InvalidArgument, "count must be greater than zero") 1061 } 1062 if count > core.cfg.RangeQueryLimit { 1063 return nil, status.Error(codes.InvalidArgument, "range exceeds the limit") 1064 } 1065 1066 actions, err := core.indexer.GetActionsByAddress(hash.BytesToHash160(addr.Bytes()), start, count) 1067 if err != nil { 1068 if errors.Cause(err) == db.ErrBucketNotExist || errors.Cause(err) == db.ErrNotExist { 1069 // no actions associated with address, return nil 1070 return nil, nil 1071 } 1072 return nil, status.Error(codes.NotFound, err.Error()) 1073 } 1074 1075 var res []*iotexapi.ActionInfo 1076 for i := range actions { 1077 act, err := core.getAction(hash.BytesToHash256(actions[i]), false) 1078 if err != nil { 1079 continue 1080 } 1081 res = append(res, act) 1082 } 1083 return res, nil 1084 } 1085 1086 // BlockHashByBlockHeight returns block hash by block height 1087 func (core *coreService) BlockHashByBlockHeight(blkHeight uint64) (hash.Hash256, error) { 1088 return core.dao.GetBlockHash(blkHeight) 1089 } 1090 1091 // ActionByActionHash returns action by action hash 1092 func (core *coreService) ActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, *block.Block, uint32, error) { 1093 if err := core.checkActionIndex(); err != nil { 1094 return nil, nil, 0, status.Error(codes.NotFound, blockindex.ErrActionIndexNA.Error()) 1095 } 1096 1097 actIndex, err := core.indexer.GetActionIndex(h[:]) 1098 if err != nil { 1099 return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) 1100 } 1101 blk, err := core.dao.GetBlockByHeight(actIndex.BlockHeight()) 1102 if err != nil { 1103 return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) 1104 } 1105 selp, index, err := blk.ActionByHash(h) 1106 if err != nil { 1107 return nil, nil, 0, errors.Wrap(ErrNotFound, err.Error()) 1108 } 1109 return selp, blk, index, nil 1110 } 1111 1112 // ActionByActionHash returns action by action hash 1113 func (core *coreService) PendingActionByActionHash(h hash.Hash256) (*action.SealedEnvelope, error) { 1114 selp, err := core.ap.GetActionByHash(h) 1115 if err != nil { 1116 return nil, errors.Wrap(ErrNotFound, err.Error()) 1117 } 1118 return selp, nil 1119 } 1120 1121 // UnconfirmedActionsByAddress returns all unconfirmed actions in actpool associated with an address 1122 func (core *coreService) UnconfirmedActionsByAddress(address string, start uint64, count uint64) ([]*iotexapi.ActionInfo, error) { 1123 if count == 0 { 1124 return nil, status.Error(codes.InvalidArgument, "count must be greater than zero") 1125 } 1126 if count > core.cfg.RangeQueryLimit { 1127 return nil, status.Error(codes.InvalidArgument, "range exceeds the limit") 1128 } 1129 1130 selps := core.ap.GetUnconfirmedActs(address) 1131 if len(selps) == 0 { 1132 return []*iotexapi.ActionInfo{}, nil 1133 } 1134 if start >= uint64(len(selps)) { 1135 return nil, status.Error(codes.InvalidArgument, "start exceeds the limit") 1136 } 1137 1138 var res []*iotexapi.ActionInfo 1139 for i := start; i < uint64(len(selps)) && i < start+count; i++ { 1140 if act, err := core.pendingAction(selps[i]); err == nil { 1141 res = append(res, act) 1142 } 1143 } 1144 return res, nil 1145 } 1146 1147 // BlockByHash returns the block and its receipt from block hash 1148 func (core *coreService) BlockByHash(blkHash string) (*apitypes.BlockWithReceipts, error) { 1149 if err := core.checkActionIndex(); err != nil { 1150 return nil, err 1151 } 1152 hash, err := hash.HexStringToHash256(blkHash) 1153 if err != nil { 1154 return nil, err 1155 } 1156 blk, err := core.dao.GetBlock(hash) 1157 if err != nil { 1158 return nil, errors.Wrap(ErrNotFound, err.Error()) 1159 } 1160 receipts, err := core.dao.GetReceipts(blk.Height()) 1161 if err != nil { 1162 return nil, errors.Wrap(ErrNotFound, err.Error()) 1163 } 1164 return &apitypes.BlockWithReceipts{ 1165 Block: blk, 1166 Receipts: receipts, 1167 }, nil 1168 } 1169 1170 // BlockByHeightRange returns blocks within the height range 1171 func (core *coreService) BlockByHeightRange(start uint64, count uint64) ([]*apitypes.BlockWithReceipts, error) { 1172 if count == 0 { 1173 return nil, errors.Wrap(errInvalidFormat, "count must be greater than zero") 1174 } 1175 if count > core.cfg.RangeQueryLimit { 1176 return nil, errors.Wrap(errInvalidFormat, "range exceeds the limit") 1177 } 1178 1179 var ( 1180 tipHeight = core.bc.TipHeight() 1181 res = make([]*apitypes.BlockWithReceipts, 0) 1182 ) 1183 if start > tipHeight { 1184 return nil, errors.Wrap(errInvalidFormat, "start height should not exceed tip height") 1185 } 1186 for height := start; height <= tipHeight && count > 0; height++ { 1187 blkStore, err := core.getBlockByHeight(height) 1188 if err != nil { 1189 return nil, err 1190 } 1191 res = append(res, blkStore) 1192 count-- 1193 } 1194 return res, nil 1195 } 1196 1197 // BlockByHeight returns the block and its receipt from block height 1198 func (core *coreService) BlockByHeight(height uint64) (*apitypes.BlockWithReceipts, error) { 1199 return core.getBlockByHeight(height) 1200 } 1201 1202 func (core *coreService) getBlockByHeight(height uint64) (*apitypes.BlockWithReceipts, error) { 1203 if height > core.bc.TipHeight() { 1204 return nil, ErrNotFound 1205 } 1206 blk, err := core.dao.GetBlockByHeight(height) 1207 if err != nil { 1208 return nil, errors.Wrap(ErrNotFound, err.Error()) 1209 } 1210 receipts := []*action.Receipt{} 1211 if blk.Height() > 0 { 1212 var err error 1213 receipts, err = core.dao.GetReceipts(height) 1214 if err != nil { 1215 return nil, errors.Wrap(ErrNotFound, err.Error()) 1216 } 1217 } 1218 return &apitypes.BlockWithReceipts{ 1219 Block: blk, 1220 Receipts: receipts, 1221 }, nil 1222 } 1223 1224 func (core *coreService) getGravityChainStartHeight(epochHeight uint64) (uint64, error) { 1225 gravityChainStartHeight := epochHeight 1226 if pp := poll.FindProtocol(core.registry); pp != nil { 1227 methodName := []byte("GetGravityChainStartHeight") 1228 arguments := [][]byte{[]byte(strconv.FormatUint(epochHeight, 10))} 1229 data, _, err := core.readState(context.Background(), pp, "", methodName, arguments...) 1230 if err != nil { 1231 return 0, err 1232 } 1233 if len(data) == 0 { 1234 return 0, nil 1235 } 1236 if gravityChainStartHeight, err = strconv.ParseUint(string(data), 10, 64); err != nil { 1237 return 0, err 1238 } 1239 } 1240 return gravityChainStartHeight, nil 1241 } 1242 1243 func (core *coreService) committedAction(selp *action.SealedEnvelope, blkHash hash.Hash256, blkHeight uint64) (*iotexapi.ActionInfo, error) { 1244 actHash, err := selp.Hash() 1245 if err != nil { 1246 return nil, err 1247 } 1248 header, err := core.dao.Header(blkHash) 1249 if err != nil { 1250 return nil, err 1251 } 1252 sender := selp.SenderAddress() 1253 receipts, err := core.dao.GetReceipts(blkHeight) 1254 if err != nil { 1255 return nil, err 1256 } 1257 receipt := filterReceipts(receipts, actHash) 1258 if receipt == nil { 1259 return nil, errors.Wrapf(ErrNotFound, "failed to find receipt for action %x", actHash) 1260 } 1261 1262 gas := new(big.Int) 1263 gas = gas.Mul(selp.GasPrice(), big.NewInt(int64(receipt.GasConsumed))) 1264 return &iotexapi.ActionInfo{ 1265 Action: selp.Proto(), 1266 ActHash: hex.EncodeToString(actHash[:]), 1267 BlkHash: hex.EncodeToString(blkHash[:]), 1268 BlkHeight: header.Height(), 1269 Sender: sender.String(), 1270 GasFee: gas.String(), 1271 Timestamp: header.BlockHeaderCoreProto().Timestamp, 1272 }, nil 1273 } 1274 1275 func (core *coreService) pendingAction(selp *action.SealedEnvelope) (*iotexapi.ActionInfo, error) { 1276 actHash, err := selp.Hash() 1277 if err != nil { 1278 return nil, err 1279 } 1280 sender := selp.SenderAddress() 1281 return &iotexapi.ActionInfo{ 1282 Action: selp.Proto(), 1283 ActHash: hex.EncodeToString(actHash[:]), 1284 BlkHash: hex.EncodeToString(hash.ZeroHash256[:]), 1285 BlkHeight: 0, 1286 Sender: sender.String(), 1287 Timestamp: nil, 1288 Index: 0, 1289 }, nil 1290 } 1291 1292 func (core *coreService) getAction(actHash hash.Hash256, checkPending bool) (*iotexapi.ActionInfo, error) { 1293 selp, blk, actIndex, err := core.ActionByActionHash(actHash) 1294 if err == nil { 1295 act, err := core.committedAction(selp, blk.HashBlock(), blk.Height()) 1296 if err != nil { 1297 return nil, err 1298 } 1299 act.Index = actIndex 1300 return act, nil 1301 } 1302 // Try to fetch pending action from actpool 1303 if checkPending { 1304 selp, err = core.ap.GetActionByHash(actHash) 1305 } 1306 if err != nil { 1307 return nil, err 1308 } 1309 return core.pendingAction(selp) 1310 } 1311 1312 func (core *coreService) reverseActionsInBlock(blk *block.Block, reverseStart, count uint64) []*iotexapi.ActionInfo { 1313 h := blk.HashBlock() 1314 blkHash := hex.EncodeToString(h[:]) 1315 blkHeight := blk.Height() 1316 1317 size := uint64(len(blk.Actions)) 1318 if reverseStart > size || count == 0 { 1319 return nil 1320 } 1321 // TODO (saito): fix overflow 1322 start := size - (reverseStart + count) 1323 if start < 0 { 1324 start = 0 1325 } 1326 end := size - 1 - reverseStart 1327 res := make([]*iotexapi.ActionInfo, 0, start-end+1) 1328 receipts, err := core.dao.GetReceipts(blkHeight) 1329 if err != nil { 1330 log.Logger("api").Debug("Skipping action due to failing to get receipt", zap.Error(err)) 1331 return nil 1332 } 1333 receiptMap := make(map[hash.Hash256]*action.Receipt, len(receipts)) 1334 for _, receipt := range receipts { 1335 receiptMap[receipt.ActionHash] = receipt 1336 } 1337 for idx := start; idx <= end; idx++ { 1338 selp := blk.Actions[idx] 1339 actHash, err := selp.Hash() 1340 if err != nil { 1341 log.Logger("api").Debug("Skipping action due to hash error", zap.Error(err)) 1342 continue 1343 } 1344 receipt, ok := receiptMap[actHash] 1345 if !ok { 1346 log.Logger("api").With(zap.String("actionHash", hex.EncodeToString(actHash[:]))).Debug("Skipping action due to failing to get receipt") 1347 continue 1348 } 1349 gas := new(big.Int).Mul(selp.GasPrice(), big.NewInt(int64(receipt.GasConsumed))) 1350 sender := selp.SenderAddress() 1351 res = append(res, &iotexapi.ActionInfo{ 1352 Action: selp.Proto(), 1353 ActHash: hex.EncodeToString(actHash[:]), 1354 BlkHash: blkHash, 1355 Timestamp: blk.Header.BlockHeaderCoreProto().Timestamp, 1356 BlkHeight: blkHeight, 1357 Sender: sender.String(), 1358 GasFee: gas.String(), 1359 Index: uint32(idx), 1360 }) 1361 } 1362 return res 1363 } 1364 1365 func (core *coreService) LogsInBlockByHash(filter *logfilter.LogFilter, blockHash hash.Hash256) ([]*action.Log, error) { 1366 blkHeight, err := core.dao.GetBlockHeight(blockHash) 1367 if err != nil { 1368 return nil, status.Error(codes.InvalidArgument, "invalid block hash") 1369 } 1370 return core.logsInBlock(filter, blkHeight) 1371 } 1372 1373 func (core *coreService) logsInBlock(filter *logfilter.LogFilter, blockNumber uint64) ([]*action.Log, error) { 1374 logBloomFilter, err := core.bfIndexer.BlockFilterByHeight(blockNumber) 1375 if err != nil { 1376 return nil, err 1377 } 1378 1379 if !filter.ExistInBloomFilterv2(logBloomFilter) { 1380 return []*action.Log{}, nil 1381 } 1382 1383 receipts, err := core.dao.GetReceipts(blockNumber) 1384 if err != nil { 1385 return nil, err 1386 } 1387 1388 return filter.MatchLogs(receipts), nil 1389 } 1390 1391 // LogsInRange filter logs among [start, end] blocks 1392 func (core *coreService) LogsInRange(filter *logfilter.LogFilter, start, end, paginationSize uint64) ([]*action.Log, []hash.Hash256, error) { 1393 start, end, err := core.correctQueryRange(start, end) 1394 if err != nil { 1395 return nil, nil, err 1396 } 1397 if paginationSize == 0 { 1398 paginationSize = 1000 1399 } 1400 if paginationSize > 5000 { 1401 paginationSize = 5000 1402 } 1403 // getLogs via range Blooom filter [start, end] 1404 blockNumbers, err := core.bfIndexer.FilterBlocksInRange(filter, start, end, paginationSize) 1405 if err != nil { 1406 return nil, nil, err 1407 } 1408 var ( 1409 logs = []*action.Log{} 1410 hashes = []hash.Hash256{} 1411 logsInBlk = make([][]*action.Log, len(blockNumbers)) 1412 HashInBlk = make([]hash.Hash256, len(blockNumbers)) 1413 jobs = make(chan jobDesc, len(blockNumbers)) 1414 eg, ctx = errgroup.WithContext(context.Background()) 1415 ) 1416 if len(blockNumbers) == 0 { 1417 return logs, hashes, nil 1418 } 1419 1420 for i, v := range blockNumbers { 1421 jobs <- jobDesc{i, v} 1422 } 1423 close(jobs) 1424 for w := 0; w < _workerNumbers; w++ { 1425 eg.Go(func() error { 1426 for { 1427 select { 1428 case <-ctx.Done(): 1429 return ctx.Err() 1430 default: 1431 job, ok := <-jobs 1432 if !ok { 1433 return nil 1434 } 1435 logsInBlock, err := core.logsInBlock(filter, job.blkNum) 1436 if err != nil { 1437 return err 1438 } 1439 blkHash, err := core.dao.GetBlockHash(job.blkNum) 1440 if err != nil { 1441 return err 1442 } 1443 logsInBlk[job.idx] = logsInBlock 1444 HashInBlk[job.idx] = blkHash 1445 } 1446 } 1447 }) 1448 } 1449 if err := eg.Wait(); err != nil { 1450 return nil, nil, err 1451 } 1452 1453 for i := 0; i < len(blockNumbers); i++ { 1454 for j := range logsInBlk[i] { 1455 logs = append(logs, logsInBlk[i][j]) 1456 hashes = append(hashes, HashInBlk[i]) 1457 if len(logs) >= int(paginationSize) { 1458 return logs, hashes, nil 1459 } 1460 } 1461 } 1462 1463 return logs, hashes, nil 1464 } 1465 1466 func (core *coreService) correctQueryRange(start, end uint64) (uint64, uint64, error) { 1467 if start == 0 { 1468 start = core.bc.TipHeight() 1469 } 1470 if end == 0 { 1471 end = core.bc.TipHeight() 1472 } 1473 if start > end { 1474 return 0, 0, errors.New("invalid start or end height") 1475 } 1476 if start > core.bc.TipHeight() { 1477 return 0, 0, errors.New("start block > tip height") 1478 } 1479 if end > core.bc.TipHeight() { 1480 end = core.bc.TipHeight() 1481 } 1482 return start, end, nil 1483 } 1484 1485 // EstimateGasForNonExecution estimates action gas except execution 1486 func (core *coreService) EstimateGasForNonExecution(actType action.Action) (uint64, error) { 1487 act, ok := actType.(intrinsicGasCalculator) 1488 if !ok { 1489 return 0, errors.Errorf("invalid action type not supported") 1490 } 1491 return act.IntrinsicGas() 1492 } 1493 1494 // EstimateExecutionGasConsumption estimate gas consumption for execution action 1495 func (core *coreService) EstimateExecutionGasConsumption(ctx context.Context, sc *action.Execution, callerAddr address.Address) (uint64, error) { 1496 ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) 1497 state, err := accountutil.AccountState(ctx, core.sf, callerAddr) 1498 if err != nil { 1499 return 0, status.Error(codes.InvalidArgument, err.Error()) 1500 } 1501 ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 1502 BlockHeight: core.bc.TipHeight(), 1503 })) 1504 var pendingNonce uint64 1505 if protocol.MustGetFeatureCtx(ctx).RefactorFreshAccountConversion { 1506 pendingNonce = state.PendingNonceConsideringFreshAccount() 1507 } else { 1508 pendingNonce = state.PendingNonce() 1509 } 1510 sc.SetNonce(pendingNonce) 1511 //gasprice should be 0, otherwise it may cause the API to return an error, such as insufficient balance. 1512 sc.SetGasPrice(big.NewInt(0)) 1513 var ( 1514 g = core.bc.Genesis() 1515 blockGasLimit = g.BlockGasLimitByHeight(core.bc.TipHeight()) 1516 ) 1517 sc.SetGasLimit(blockGasLimit) 1518 enough, receipt, err := core.isGasLimitEnough(ctx, callerAddr, sc) 1519 if err != nil { 1520 return 0, status.Error(codes.Internal, err.Error()) 1521 } 1522 if !enough { 1523 if receipt.ExecutionRevertMsg() != "" { 1524 return 0, status.Errorf(codes.Internal, fmt.Sprintf("execution simulation is reverted due to the reason: %s", receipt.ExecutionRevertMsg())) 1525 } 1526 return 0, status.Error(codes.Internal, fmt.Sprintf("execution simulation failed: status = %d", receipt.Status)) 1527 } 1528 estimatedGas := receipt.GasConsumed 1529 sc.SetGasLimit(estimatedGas) 1530 enough, _, err = core.isGasLimitEnough(ctx, callerAddr, sc) 1531 if err != nil && err != action.ErrInsufficientFunds { 1532 return 0, status.Error(codes.Internal, err.Error()) 1533 } 1534 if !enough { 1535 low, high := estimatedGas, blockGasLimit 1536 estimatedGas = high 1537 for low <= high { 1538 mid := (low + high) / 2 1539 sc.SetGasLimit(mid) 1540 enough, _, err = core.isGasLimitEnough(ctx, callerAddr, sc) 1541 if err != nil && err != action.ErrInsufficientFunds { 1542 return 0, status.Error(codes.Internal, err.Error()) 1543 } 1544 if enough { 1545 estimatedGas = mid 1546 high = mid - 1 1547 } else { 1548 low = mid + 1 1549 } 1550 } 1551 } 1552 1553 return estimatedGas, nil 1554 } 1555 1556 func (core *coreService) isGasLimitEnough( 1557 ctx context.Context, 1558 caller address.Address, 1559 sc *action.Execution, 1560 ) (bool, *action.Receipt, error) { 1561 ctx, span := tracer.NewSpan(ctx, "Server.isGasLimitEnough") 1562 defer span.End() 1563 ctx, err := core.bc.Context(ctx) 1564 if err != nil { 1565 return false, nil, err 1566 } 1567 1568 _, receipt, err := core.simulateExecution(ctx, caller, sc, core.dao.GetBlockHash, core.getBlockTime) 1569 if err != nil { 1570 return false, nil, err 1571 } 1572 return receipt.Status == uint64(iotextypes.ReceiptStatus_Success), receipt, nil 1573 } 1574 1575 func (core *coreService) getProductivityByEpoch( 1576 rp *rolldpos.Protocol, 1577 epochNum uint64, 1578 tipHeight uint64, 1579 abps state.CandidateList, 1580 ) (uint64, map[string]uint64, error) { 1581 num, produce, err := rp.ProductivityByEpoch(epochNum, tipHeight, func(start uint64, end uint64) (map[string]uint64, error) { 1582 return blockchain.Productivity(core.bc, start, end) 1583 }) 1584 if err != nil { 1585 return 0, nil, status.Error(codes.NotFound, err.Error()) 1586 } 1587 // check if there is any active block producer who didn't prodcue any block 1588 for _, abp := range abps { 1589 if _, ok := produce[abp.Address]; !ok { 1590 produce[abp.Address] = 0 1591 } 1592 } 1593 return num, produce, nil 1594 } 1595 1596 func (core *coreService) checkActionIndex() error { 1597 if core.indexer == nil { 1598 return errors.New("no action index") 1599 } 1600 return nil 1601 } 1602 1603 func (core *coreService) getProtocolAccount(ctx context.Context, addr string) (*iotextypes.AccountMeta, *iotextypes.BlockIdentifier, error) { 1604 span := tracer.SpanFromContext(ctx) 1605 defer span.End() 1606 var ( 1607 balance string 1608 out *iotexapi.ReadStateResponse 1609 err error 1610 ) 1611 switch addr { 1612 case address.RewardingPoolAddr: 1613 if out, err = core.ReadState("rewarding", "", []byte("TotalBalance"), nil); err != nil { 1614 return nil, nil, err 1615 } 1616 val, ok := new(big.Int).SetString(string(out.GetData()), 10) 1617 if !ok { 1618 return nil, nil, errors.New("balance convert error") 1619 } 1620 balance = val.String() 1621 case address.StakingBucketPoolAddr: 1622 methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{ 1623 Method: iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT, 1624 }) 1625 if err != nil { 1626 return nil, nil, err 1627 } 1628 arg, err := proto.Marshal(&iotexapi.ReadStakingDataRequest{ 1629 Request: &iotexapi.ReadStakingDataRequest_TotalStakingAmount_{ 1630 TotalStakingAmount: &iotexapi.ReadStakingDataRequest_TotalStakingAmount{}, 1631 }, 1632 }) 1633 if err != nil { 1634 return nil, nil, err 1635 } 1636 if out, err = core.ReadState("staking", "", methodName, [][]byte{arg}); err != nil { 1637 return nil, nil, err 1638 } 1639 acc := iotextypes.AccountMeta{} 1640 if err := proto.Unmarshal(out.GetData(), &acc); err != nil { 1641 return nil, nil, errors.Wrap(err, "failed to unmarshal account meta") 1642 } 1643 balance = acc.GetBalance() 1644 default: 1645 return nil, nil, errors.Errorf("invalid address %s", addr) 1646 } 1647 return &iotextypes.AccountMeta{ 1648 Address: addr, 1649 Balance: balance, 1650 }, out.GetBlockIdentifier(), nil 1651 } 1652 1653 // ActionsInActPool returns the all Transaction Identifiers in the actpool 1654 func (core *coreService) ActionsInActPool(actHashes []string) ([]*action.SealedEnvelope, error) { 1655 var ret []*action.SealedEnvelope 1656 if len(actHashes) == 0 { 1657 for _, sealeds := range core.ap.PendingActionMap() { 1658 ret = append(ret, sealeds...) 1659 } 1660 return ret, nil 1661 } 1662 1663 for _, hashStr := range actHashes { 1664 hs, err := hash.HexStringToHash256(hashStr) 1665 if err != nil { 1666 return nil, err 1667 } 1668 sealed, err := core.ap.GetActionByHash(hs) 1669 if err != nil { 1670 return nil, err 1671 } 1672 ret = append(ret, sealed) 1673 } 1674 return ret, nil 1675 } 1676 1677 // Genesis returns the genesis of the chain 1678 func (core *coreService) Genesis() genesis.Genesis { 1679 return core.bc.Genesis() 1680 } 1681 1682 // EVMNetworkID returns the network id of evm 1683 func (core *coreService) EVMNetworkID() uint32 { 1684 return core.bc.EvmNetworkID() 1685 } 1686 1687 // ChainID returns the chain id of evm 1688 func (core *coreService) ChainID() uint32 { 1689 return core.bc.ChainID() 1690 } 1691 1692 // ReadContractStorage reads contract's storage 1693 func (core *coreService) ReadContractStorage(ctx context.Context, addr address.Address, key []byte) ([]byte, error) { 1694 ctx, err := core.bc.Context(ctx) 1695 if err != nil { 1696 return nil, status.Error(codes.Internal, err.Error()) 1697 } 1698 return core.sf.ReadContractStorage(ctx, addr, key) 1699 } 1700 1701 func (core *coreService) ReceiveBlock(blk *block.Block) error { 1702 core.readCache.Clear() 1703 return core.chainListener.ReceiveBlock(blk) 1704 } 1705 1706 func (core *coreService) SimulateExecution(ctx context.Context, addr address.Address, exec *action.Execution) ([]byte, *action.Receipt, error) { 1707 ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) 1708 state, err := accountutil.AccountState(ctx, core.sf, addr) 1709 if err != nil { 1710 return nil, nil, err 1711 } 1712 ctx, err = core.bc.Context(ctx) 1713 if err != nil { 1714 return nil, nil, err 1715 } 1716 // TODO (liuhaai): Use original nonce and gas limit properly 1717 ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 1718 BlockHeight: core.bc.TipHeight(), 1719 })) 1720 var pendingNonce uint64 1721 if protocol.MustGetFeatureCtx(ctx).RefactorFreshAccountConversion { 1722 pendingNonce = state.PendingNonceConsideringFreshAccount() 1723 } else { 1724 pendingNonce = state.PendingNonce() 1725 } 1726 exec.SetNonce(pendingNonce) 1727 var ( 1728 g = core.bc.Genesis() 1729 blockGasLimit = g.BlockGasLimitByHeight(core.bc.TipHeight()) 1730 ) 1731 exec.SetGasLimit(blockGasLimit) 1732 return core.simulateExecution(ctx, addr, exec, core.dao.GetBlockHash, core.getBlockTime) 1733 } 1734 1735 // SyncingProgress returns the syncing status of node 1736 func (core *coreService) SyncingProgress() (uint64, uint64, uint64) { 1737 startingHeight, currentHeight, targetHeight, _ := core.bs.SyncStatus() 1738 return startingHeight, currentHeight, targetHeight 1739 } 1740 1741 // TraceTransaction returns the trace result of transaction 1742 func (core *coreService) TraceTransaction(ctx context.Context, actHash string, config *tracers.TraceConfig) ([]byte, *action.Receipt, any, error) { 1743 actInfo, err := core.Action(util.Remove0xPrefix(actHash), false) 1744 if err != nil { 1745 return nil, nil, nil, err 1746 } 1747 act, err := (&action.Deserializer{}).SetEvmNetworkID(core.EVMNetworkID()).ActionToSealedEnvelope(actInfo.Action) 1748 if err != nil { 1749 return nil, nil, nil, err 1750 } 1751 sc, ok := act.Action().(*action.Execution) 1752 if !ok { 1753 return nil, nil, nil, errors.New("the type of action is not supported") 1754 } 1755 addr, _ := address.FromString(address.ZeroAddress) 1756 retval, receipt, tracer, err := core.traceTx(ctx, new(tracers.Context), config, func(ctx context.Context) ([]byte, *action.Receipt, error) { 1757 1758 return core.simulateExecution(ctx, addr, sc, core.dao.GetBlockHash, core.getBlockTime) 1759 }) 1760 return retval, receipt, tracer, err 1761 } 1762 1763 // TraceCall returns the trace result of call 1764 func (core *coreService) TraceCall(ctx context.Context, 1765 callerAddr address.Address, 1766 blkNumOrHash any, 1767 contractAddress string, 1768 nonce uint64, 1769 amount *big.Int, 1770 gasLimit uint64, 1771 data []byte, 1772 config *tracers.TraceConfig) ([]byte, *action.Receipt, any, error) { 1773 var ( 1774 g = core.bc.Genesis() 1775 blockGasLimit = g.BlockGasLimitByHeight(core.bc.TipHeight()) 1776 ) 1777 if gasLimit == 0 { 1778 gasLimit = blockGasLimit 1779 } 1780 ctx, err := core.bc.Context(ctx) 1781 if err != nil { 1782 return nil, nil, nil, err 1783 } 1784 if nonce == 0 { 1785 state, err := accountutil.AccountState(ctx, core.sf, callerAddr) 1786 if err != nil { 1787 return nil, nil, nil, err 1788 } 1789 ctx = protocol.WithFeatureCtx(protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 1790 BlockHeight: core.bc.TipHeight(), 1791 })) 1792 var pendingNonce uint64 1793 if protocol.MustGetFeatureCtx(ctx).RefactorFreshAccountConversion { 1794 pendingNonce = state.PendingNonceConsideringFreshAccount() 1795 } else { 1796 pendingNonce = state.PendingNonce() 1797 } 1798 nonce = pendingNonce 1799 } 1800 exec, err := action.NewExecution( 1801 contractAddress, 1802 nonce, 1803 amount, 1804 gasLimit, 1805 big.NewInt(0), 1806 data, 1807 ) 1808 if err != nil { 1809 return nil, nil, nil, err 1810 } 1811 retval, receipt, tracer, err := core.traceTx(ctx, new(tracers.Context), config, func(ctx context.Context) ([]byte, *action.Receipt, error) { 1812 return core.simulateExecution(ctx, callerAddr, exec, core.dao.GetBlockHash, core.getBlockTime) 1813 }) 1814 return retval, receipt, tracer, err 1815 } 1816 1817 // Track tracks the api call 1818 func (core *coreService) Track(ctx context.Context, start time.Time, method string, size int64, success bool) { 1819 if core.apiStats == nil { 1820 return 1821 } 1822 elapsed := time.Since(start) 1823 core.apiStats.ReportCall(nodestats.APIReport{ 1824 Method: method, 1825 HandlingTime: elapsed, 1826 Success: success, 1827 }, size) 1828 } 1829 1830 func (core *coreService) traceTx(ctx context.Context, txctx *tracers.Context, config *tracers.TraceConfig, simulateFn func(ctx context.Context) ([]byte, *action.Receipt, error)) ([]byte, *action.Receipt, any, error) { 1831 var ( 1832 tracer vm.EVMLogger 1833 err error 1834 ) 1835 switch { 1836 case config == nil: 1837 tracer = logger.NewStructLogger(nil) 1838 case config.Tracer != nil: 1839 // Define a meaningful timeout of a single transaction trace 1840 timeout := defaultTraceTimeout 1841 if config.Timeout != nil { 1842 if timeout, err = time.ParseDuration(*config.Timeout); err != nil { 1843 return nil, nil, nil, err 1844 } 1845 } 1846 t, err := tracers.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig) 1847 if err != nil { 1848 return nil, nil, nil, err 1849 } 1850 deadlineCtx, cancel := context.WithTimeout(ctx, timeout) 1851 defer cancel() 1852 go func() { 1853 <-deadlineCtx.Done() 1854 if errors.Is(deadlineCtx.Err(), context.DeadlineExceeded) { 1855 t.Stop(errors.New("execution timeout")) 1856 } 1857 }() 1858 tracer = t 1859 1860 default: 1861 tracer = logger.NewStructLogger(config.Config) 1862 } 1863 ctx = protocol.WithVMConfigCtx(ctx, vm.Config{ 1864 Debug: true, 1865 Tracer: tracer, 1866 NoBaseFee: true, 1867 }) 1868 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{}) 1869 ctx = genesis.WithGenesisContext(ctx, core.bc.Genesis()) 1870 ctx = protocol.WithBlockchainCtx(protocol.WithFeatureCtx(ctx), protocol.BlockchainCtx{}) 1871 retval, receipt, err := simulateFn(ctx) 1872 return retval, receipt, tracer, err 1873 } 1874 1875 func (core *coreService) simulateExecution(ctx context.Context, addr address.Address, exec *action.Execution, getBlockHash evm.GetBlockHash, getBlockTime evm.GetBlockTime) ([]byte, *action.Receipt, error) { 1876 ctx = evm.WithHelperCtx(ctx, evm.HelperContext{ 1877 GetBlockHash: getBlockHash, 1878 GetBlockTime: getBlockTime, 1879 DepositGasFunc: rewarding.DepositGasWithSGD, 1880 Sgd: core.sgdIndexer, 1881 }) 1882 return core.sf.SimulateExecution(ctx, addr, exec) 1883 } 1884 1885 func filterReceipts(receipts []*action.Receipt, actHash hash.Hash256) *action.Receipt { 1886 for _, r := range receipts { 1887 if r.ActionHash == actHash { 1888 return r 1889 } 1890 } 1891 return nil 1892 }