From d4df3aa039c5f671bc62207bc20d0f43ad2e5cb5 Mon Sep 17 00:00:00 2001 From: Daniel Van Der Maden Date: Wed, 22 Jul 2020 16:50:03 -0700 Subject: [PATCH] Node API Refactor (Stage 2.1 of Node API Overhaul) (#3244) * Add latest dir removal for make clean Fix docker image in README Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Expose Harmony attributes & remove accessors * Add internals for APIBackend & Harmony fuse * Add skeleton for refactor of GetTotalStakingSnapshot Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Re-implement APIBacked methods in Harmony * Add 'FIXME:done' comments to indicate implemented methods, some were left out as they are simple accessors and will transition to using the exposed harmony attributes. * Split functions into standalone files for clarity. Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Fix GetTotalStakingSnapshot in Harmony implementation Previous implementation used rps.LatestBlockNumber, which is a constant -1, thus, the cache was never used Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Move ErrFinalizedTransaction to hmy.go Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make PublicHarmonyAPI use Harmony instead of Backed Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make NewPublicBlockChainAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make NewPublicTransactionPoolAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make DebugAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make filers use Harmony instead of Backend * Fix unhandled errors Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Change NetVersion to ChainID Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Change API service declaration to use Harmony * Remove irrelevant TODOs Signed-off-by: Daniel Van Der Maden * [node] Refactor - Move harmony create to APIs fetch Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Remove api_backend.go & Backend * Update hmyapi net.go to return ChainID as Version * Remove unused err return on New Harmony obj Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Prettify var names, structure & comments Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Prettify var names, structure & comments Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Remove backend interface * Fix lint Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Remove exposure for eventMux and chainDb This is to satisfy existing interfaces, mainly the in filters. Signed-off-by: Daniel Van Der Maden * [hmy + hmyapi] Refactor - Fix imports Signed-off-by: Daniel Van Der Maden * [hmy] Refactor - Apply changes from #3243 Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Make filters use Backend instead of hmy This is for testing from eth if we need it in the future Signed-off-by: Daniel Van Der Maden * [hmyapi] Refactor - Fix imports Signed-off-by: Daniel Van Der Maden --- Makefile | 1 + README.md | 4 +- hmy/api_backend.go | 949 --------------------- hmy/backend.go | 114 --- hmy/blockchain.go | 244 ++++++ hmy/hmy.go | 202 +++++ hmy/network.go | 33 + hmy/pool.go | 48 ++ hmy/staking.go | 476 +++++++++++ hmy/transaction.go | 80 ++ internal/hmyapi/apiv1/backend.go | 99 --- internal/hmyapi/apiv1/blockchain.go | 509 ++++++----- internal/hmyapi/apiv1/debug.go | 7 +- internal/hmyapi/apiv1/harmony.go | 16 +- internal/hmyapi/apiv1/net.go | 12 +- internal/hmyapi/apiv1/sendtxargs.go | 21 - internal/hmyapi/apiv1/transactionpool.go | 75 +- internal/hmyapi/apiv1/util.go | 14 +- internal/hmyapi/apiv2/backend.go | 95 --- internal/hmyapi/apiv2/blockchain.go | 492 ++++++----- internal/hmyapi/apiv2/debug.go | 7 +- internal/hmyapi/apiv2/harmony.go | 16 +- internal/hmyapi/apiv2/net.go | 12 +- internal/hmyapi/apiv2/sendtxargs.go | 83 -- internal/hmyapi/apiv2/transactionpool.go | 75 +- internal/hmyapi/apiv2/util.go | 11 +- internal/hmyapi/backend.go | 105 +-- internal/hmyapi/filters/api.go | 19 +- internal/hmyapi/filters/filter.go | 4 +- internal/hmyapi/filters/filter_criteria.go | 2 +- internal/hmyapi/filters/filter_system.go | 25 +- node/rpc.go | 16 +- 32 files changed, 1742 insertions(+), 2124 deletions(-) delete mode 100644 hmy/api_backend.go delete mode 100644 hmy/backend.go create mode 100644 hmy/blockchain.go create mode 100644 hmy/hmy.go create mode 100644 hmy/network.go create mode 100644 hmy/pool.go create mode 100644 hmy/staking.go create mode 100644 hmy/transaction.go delete mode 100644 internal/hmyapi/apiv1/backend.go delete mode 100644 internal/hmyapi/apiv1/sendtxargs.go delete mode 100644 internal/hmyapi/apiv2/backend.go delete mode 100644 internal/hmyapi/apiv2/sendtxargs.go diff --git a/Makefile b/Makefile index 0f62c7238..df99b9753 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ clean: rm -rf ./tmp_log* rm -rf ./.dht* rm -rf ./db-* + rm -rf ./latest rm -f ./*.rlp test: diff --git a/README.md b/README.md index 46b918139..db579d9dd 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,13 @@ make ``` > Run `bash scripts/install_build_tools.sh` to ensure build tools are of correct versions. -## Dev Docker Container +## Dev Docker Image Included in this repo is a Dockerfile that has a full harmony development environment and comes with emacs, vim, ag, tig and other creature comforts. Most importantly, it already has the go environment with our C/C++ based library dependencies (`libbls` and `mcl`) set up correctly for you. -You can build the docker container for yourself with the following commands: +You can build the docker image for yourself with the following commands: ```bash cd $(go env GOPATH)/src/github.com/harmony-one/harmony make clean diff --git a/hmy/api_backend.go b/hmy/api_backend.go deleted file mode 100644 index 584002ade..000000000 --- a/hmy/api_backend.go +++ /dev/null @@ -1,949 +0,0 @@ -package hmy - -import ( - "context" - "fmt" - "math/big" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/api/proto" - "github.com/harmony-one/harmony/block" - "github.com/harmony-one/harmony/consensus/quorum" - "github.com/harmony-one/harmony/core" - "github.com/harmony-one/harmony/core/rawdb" - "github.com/harmony-one/harmony/core/state" - "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/core/vm" - "github.com/harmony-one/harmony/crypto/bls" - internal_bls "github.com/harmony-one/harmony/crypto/bls" - internal_common "github.com/harmony-one/harmony/internal/common" - nodeconfig "github.com/harmony-one/harmony/internal/configs/node" - commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" - "github.com/harmony-one/harmony/internal/params" - "github.com/harmony-one/harmony/internal/utils" - "github.com/harmony-one/harmony/numeric" - "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" - "github.com/harmony-one/harmony/staking/availability" - "github.com/harmony-one/harmony/staking/effective" - "github.com/harmony-one/harmony/staking/network" - staking "github.com/harmony-one/harmony/staking/types" - lru "github.com/hashicorp/golang-lru" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/pkg/errors" - "golang.org/x/sync/singleflight" -) - -var ( - // ErrFinalizedTransaction is returned if the transaction to be submitted is already on-chain - ErrFinalizedTransaction = errors.New("transaction already finalized") -) - -// APIBackend An implementation of internal/hmyapi/Backend. Full client. -type APIBackend struct { - hmy *Harmony - TotalStakingCache struct { - sync.Mutex - BlockHeight int64 - TotalStaking *big.Int - } - apiCache singleflight.Group - LeaderCache *lru.Cache -} - -// SingleFlightRequest ... -func (b *APIBackend) SingleFlightRequest( - key string, - fn func() (interface{}, error), -) (interface{}, error) { - res, err, _ := b.apiCache.Do(key, fn) - return res, err -} - -// SingleFlightForgetKey ... -func (b *APIBackend) SingleFlightForgetKey(key string) { - b.apiCache.Forget(key) -} - -// ChainDb ... -func (b *APIBackend) ChainDb() ethdb.Database { - return b.hmy.chainDb -} - -// GetBlock ... -func (b *APIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { - return b.hmy.blockchain.GetBlockByHash(hash), nil -} - -// GetPoolTransaction ... -func (b *APIBackend) GetPoolTransaction(hash common.Hash) types.PoolTransaction { - return b.hmy.txPool.Get(hash) -} - -// BlockByNumber ... -func (b *APIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - return nil, errors.New("not implemented") - } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.hmy.blockchain.CurrentBlock(), nil - } - return b.hmy.blockchain.GetBlockByNumber(uint64(blockNr)), nil -} - -// StateAndHeaderByNumber ... -func (b *APIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) { - // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { - return nil, nil, errors.New("not implemented") - } - // Otherwise resolve the block number and return its state - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err - } - stateDb, err := b.hmy.blockchain.StateAt(header.Root()) - return stateDb, header, err -} - -// HeaderByNumber ... -func (b *APIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - return nil, errors.New("not implemented") - } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.hmy.blockchain.CurrentBlock().Header(), nil - } - return b.hmy.blockchain.GetHeaderByNumber(uint64(blockNr)), nil -} - -// GetPoolNonce ... -func (b *APIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - return b.hmy.txPool.State().GetNonce(addr), nil -} - -// SendTx ... -func (b *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - tx, _, _, _ := rawdb.ReadTransaction(b.ChainDb(), signedTx.Hash()) - if tx == nil { - return b.hmy.nodeAPI.AddPendingTransaction(signedTx) - } - return ErrFinalizedTransaction -} - -// ChainConfig ... -func (b *APIBackend) ChainConfig() *params.ChainConfig { - return b.hmy.blockchain.Config() -} - -// CurrentBlock ... -func (b *APIBackend) CurrentBlock() *types.Block { - return types.NewBlockWithHeader(b.hmy.blockchain.CurrentHeader()) -} - -// GetReceipts ... -func (b *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - return b.hmy.blockchain.GetReceiptsByHash(hash), nil -} - -// EventMux ... -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) EventMux() *event.TypeMux { return b.hmy.eventMux } - -const ( - // BloomBitsBlocks is the number of blocks a single bloom bit section vector - // contains on the server side. - BloomBitsBlocks uint64 = 4096 -) - -// BloomStatus ... -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) BloomStatus() (uint64, uint64) { - sections, _, _ := b.hmy.bloomIndexer.Sections() - return BloomBitsBlocks, sections -} - -// ProtocolVersion ... -func (b *APIBackend) ProtocolVersion() int { - return proto.ProtocolVersion -} - -// Filter related APIs - -// GetLogs ... -func (b *APIBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) { - receipts := b.hmy.blockchain.GetReceiptsByHash(blockHash) - if receipts == nil { - return nil, errors.New("Missing receipts") - } - logs := make([][]*types.Log, len(receipts)) - for i, receipt := range receipts { - logs[i] = receipt.Logs - } - return logs, nil -} - -// HeaderByHash ... -func (b *APIBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) { - header := b.hmy.blockchain.GetHeaderByHash(blockHash) - if header == nil { - return nil, errors.New("Header is not found") - } - return header, nil -} - -// ServiceFilter ... -func (b *APIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { - // TODO(ricl): implement -} - -// SubscribeNewTxsEvent subcribes new tx event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return b.hmy.TxPool().SubscribeNewTxsEvent(ch) -} - -// SubscribeChainEvent subcribes chain event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { - return b.hmy.BlockChain().SubscribeChainEvent(ch) -} - -// SubscribeChainHeadEvent subcribes chain head event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { - return b.hmy.BlockChain().SubscribeChainHeadEvent(ch) -} - -// SubscribeChainSideEvent subcribes chain side event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return b.hmy.BlockChain().SubscribeChainSideEvent(ch) -} - -// SubscribeRemovedLogsEvent subcribes removed logs event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { - return b.hmy.BlockChain().SubscribeRemovedLogsEvent(ch) -} - -// SubscribeLogsEvent subcribes log event. -// TODO: this is not implemented or verified yet for harmony. -func (b *APIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return b.hmy.BlockChain().SubscribeLogsEvent(ch) -} - -// GetPoolTransactions returns pool transactions. -func (b *APIBackend) GetPoolTransactions() (types.PoolTransactions, error) { - pending, err := b.hmy.txPool.Pending() - if err != nil { - return nil, err - } - queued, err := b.hmy.txPool.Queued() - if err != nil { - return nil, err - } - var txs types.PoolTransactions - for _, batch := range pending { - txs = append(txs, batch...) - } - for _, batch := range queued { - txs = append(txs, batch...) - } - return txs, nil -} - -// GetPoolStats returns the number of pending and queued transactions -func (b *APIBackend) GetPoolStats() (pendingCount, queuedCount int) { - return b.hmy.txPool.Stats() -} - -// GetAccountNonce returns the nonce value of the given address for the given block number -func (b *APIBackend) GetAccountNonce( - ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) { - state, _, err := b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return 0, err - } - return state.GetNonce(address), state.Error() -} - -// GetBalance returns balance of an given address. -func (b *APIBackend) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { - state, _, err := b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err - } - return state.GetBalance(address), state.Error() -} - -// GetTransactionsHistory returns list of transactions hashes of address. -func (b *APIBackend) GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) { - return b.hmy.nodeAPI.GetTransactionsHistory(address, txType, order) -} - -// GetStakingTransactionsHistory returns list of staking transactions hashes of address. -func (b *APIBackend) GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) { - return b.hmy.nodeAPI.GetStakingTransactionsHistory(address, txType, order) -} - -// GetTransactionsCount returns the number of regular transactions of address. -func (b *APIBackend) GetTransactionsCount(address, txType string) (uint64, error) { - return b.hmy.nodeAPI.GetTransactionsCount(address, txType) -} - -// GetStakingTransactionsCount returns the number of staking transactions of address. -func (b *APIBackend) GetStakingTransactionsCount(address, txType string) (uint64, error) { - return b.hmy.nodeAPI.GetStakingTransactionsCount(address, txType) -} - -// NetVersion returns net version -func (b *APIBackend) NetVersion() uint64 { - return b.hmy.NetVersion() -} - -// GetEVM returns a new EVM entity -func (b *APIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) { - // TODO(ricl): The code is borrowed from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/40cdcf8c47ff094775aca08fd5d94051f9cf1dbb/les/api_backend.go#L114) - // [question](https://ethereum.stackexchange.com/q/72977/54923) - // Might need to reconsider the SetBalance behavior - state.SetBalance(msg.From(), math.MaxBig256) - vmError := func() error { return nil } - - context := core.NewEVMContext(msg, header, b.hmy.BlockChain(), nil) - return vm.NewEVM(context, state, b.hmy.blockchain.Config(), *b.hmy.blockchain.GetVMConfig()), vmError, nil -} - -// RPCGasCap returns the gas cap of rpc -func (b *APIBackend) RPCGasCap() *big.Int { - return b.hmy.RPCGasCap // TODO(ricl): should be hmy.config.RPCGasCap -} - -// GetShardID returns shardID of this node -func (b *APIBackend) GetShardID() uint32 { - return b.hmy.shardID -} - -// GetValidators returns validators for a particular epoch. -func (b *APIBackend) GetValidators(epoch *big.Int) (*shard.Committee, error) { - state, err := b.hmy.BlockChain().ReadShardState(epoch) - if err != nil { - return nil, err - } - for _, committee := range state.Shards { - if committee.ShardID == b.GetShardID() { - return &committee, nil - } - } - return nil, nil -} - -// ResendCx retrieve blockHash from txID and add blockHash to CxPool for resending -// Note that cross shard txn is only for regular txns, not for staking txns, so the input txn hash -// is expected to be regular txn hash -func (b *APIBackend) ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) { - blockHash, blockNum, index := b.hmy.BlockChain().ReadTxLookupEntry(txID) - if blockHash == (common.Hash{}) { - return 0, false - } - - blk := b.hmy.BlockChain().GetBlockByHash(blockHash) - if blk == nil { - return 0, false - } - - txs := blk.Transactions() - // a valid index is from 0 to len-1 - if int(index) > len(txs)-1 { - return 0, false - } - tx := txs[int(index)] - - // check whether it is a valid cross shard tx - if tx.ShardID() == tx.ToShardID() || blk.Header().ShardID() != tx.ShardID() { - return 0, false - } - entry := core.CxEntry{blockHash, tx.ToShardID()} - success := b.hmy.CxPool().Add(entry) - return blockNum, success -} - -// IsLeader exposes if node is currently leader -func (b *APIBackend) IsLeader() bool { - return b.hmy.nodeAPI.IsCurrentlyLeader() -} - -// SendStakingTx adds a staking transaction -func (b *APIBackend) SendStakingTx(ctx context.Context, signedStakingTx *staking.StakingTransaction) error { - stx, _, _, _ := rawdb.ReadStakingTransaction(b.ChainDb(), signedStakingTx.Hash()) - if stx == nil { - return b.hmy.nodeAPI.AddPendingStakingTransaction(signedStakingTx) - } - return ErrFinalizedTransaction -} - -// GetElectedValidatorAddresses returns the address of elected validators for current epoch -func (b *APIBackend) GetElectedValidatorAddresses() []common.Address { - list, _ := b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentBlock().Epoch()) - return list.StakedValidators().Addrs -} - -// GetAllValidatorAddresses returns the up to date validator candidates for next epoch -func (b *APIBackend) GetAllValidatorAddresses() []common.Address { - return b.hmy.BlockChain().ValidatorCandidates() -} - -var ( - zero = numeric.ZeroDec() -) - -// GetValidatorInformation returns the information of validator -func (b *APIBackend) GetValidatorInformation( - addr common.Address, block *types.Block, -) (*staking.ValidatorRPCEnhanced, error) { - bc := b.hmy.BlockChain() - wrapper, err := bc.ReadValidatorInformationAt(addr, block.Root()) - if err != nil { - s, _ := internal_common.AddressToBech32(addr) - return nil, errors.Wrapf(err, "not found address in current state %s", s) - } - - now := block.Epoch() - // At the last block of epoch, block epoch is e while val.LastEpochInCommittee - // is already updated to e+1. So need the >= check rather than == - inCommittee := wrapper.LastEpochInCommittee.Cmp(now) >= 0 - defaultReply := &staking.ValidatorRPCEnhanced{ - CurrentlyInCommittee: inCommittee, - Wrapper: *wrapper, - Performance: nil, - ComputedMetrics: nil, - TotalDelegated: wrapper.TotalDelegation(), - EPoSStatus: effective.ValidatorStatus( - inCommittee, wrapper.Status, - ).String(), - EPoSWinningStake: nil, - BootedStatus: nil, - ActiveStatus: wrapper.Validator.Status.String(), - Lifetime: &staking.AccumulatedOverLifetime{ - wrapper.BlockReward, - wrapper.Counters, - zero, - nil, - }, - } - - snapshot, err := bc.ReadValidatorSnapshotAtEpoch( - now, addr, - ) - - if err != nil { - return defaultReply, nil - } - - computed := availability.ComputeCurrentSigning( - snapshot.Validator, wrapper, - ) - beaconChainBlocks := uint64( - b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64(), - ) % shard.Schedule.BlocksPerEpoch() - computed.BlocksLeftInEpoch = shard.Schedule.BlocksPerEpoch() - beaconChainBlocks - - if defaultReply.CurrentlyInCommittee { - defaultReply.Performance = &staking.CurrentEpochPerformance{ - CurrentSigningPercentage: *computed, - } - } - - stats, err := bc.ReadValidatorStats(addr) - if err != nil { - // when validator has no stats, default boot-status to not booted - notBooted := effective.NotBooted.String() - defaultReply.BootedStatus = ¬Booted - return defaultReply, nil - } - - latestAPR := numeric.ZeroDec() - l := len(stats.APRs) - if l > 0 { - latestAPR = stats.APRs[l-1].Value - } - defaultReply.Lifetime.APR = latestAPR - defaultReply.Lifetime.EpochAPRs = stats.APRs - - // average apr cache keys - // key := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()) - // prevKey := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()-1) - - // delete entry for previous epoch - // b.apiCache.Forget(prevKey) - - // calculate last APRHistoryLength epochs for averaging APR - // epochFrom := bc.Config().StakingEpoch - // nowMinus := big.NewInt(0).Sub(now, big.NewInt(staking.APRHistoryLength)) - // if nowMinus.Cmp(epochFrom) > 0 { - // epochFrom = nowMinus - // } - - // if len(stats.APRs) > 0 && stats.APRs[0].Epoch.Cmp(epochFrom) > 0 { - // epochFrom = stats.APRs[0].Epoch - // } - - // epochToAPRs := map[int64]numeric.Dec{} - // for i := 0; i < len(stats.APRs); i++ { - // entry := stats.APRs[i] - // epochToAPRs[entry.Epoch.Int64()] = entry.Value - // } - - // at this point, validator is active and has apr's for the recent 100 epochs - // compute average apr over history - // if avgAPR, err := b.SingleFlightRequest( - // key, func() (interface{}, error) { - // total := numeric.ZeroDec() - // count := 0 - // for i := epochFrom.Int64(); i < now.Int64(); i++ { - // if apr, ok := epochToAPRs[i]; ok { - // total = total.Add(apr) - // } - // count++ - // } - // if count == 0 { - // return nil, errors.New("no apr snapshots available") - // } - // return total.QuoInt64(int64(count)), nil - // }, - // ); err != nil { - // // could not compute average apr from snapshot - // // assign the latest apr available from stats - // defaultReply.Lifetime.APR = numeric.ZeroDec() - // } else { - // defaultReply.Lifetime.APR = avgAPR.(numeric.Dec) - // } - - if defaultReply.CurrentlyInCommittee { - defaultReply.ComputedMetrics = stats - defaultReply.EPoSWinningStake = &stats.TotalEffectiveStake - } - - if !defaultReply.CurrentlyInCommittee { - reason := stats.BootedStatus.String() - defaultReply.BootedStatus = &reason - } - - return defaultReply, nil -} - -// GetMedianRawStakeSnapshot .. -func (b *APIBackend) GetMedianRawStakeSnapshot() ( - *committee.CompletedEPoSRound, error, -) { - blockNr := b.CurrentBlock().NumberU64() - key := fmt.Sprintf("median-%d", blockNr) - - // delete cache for previous block - prevKey := fmt.Sprintf("median-%d", blockNr-1) - b.apiCache.Forget(prevKey) - - res, err := b.SingleFlightRequest( - key, - func() (interface{}, error) { - // Compute for next epoch - epoch := big.NewInt(0).Add(b.CurrentBlock().Epoch(), big.NewInt(1)) - return committee.NewEPoSRound(epoch, b.hmy.BlockChain()) - }, - ) - if err != nil { - return nil, err - } - return res.(*committee.CompletedEPoSRound), nil -} - -// GetLatestChainHeaders .. -func (b *APIBackend) GetLatestChainHeaders() *block.HeaderPair { - return &block.HeaderPair{ - BeaconHeader: b.hmy.BeaconChain().CurrentHeader(), - ShardHeader: b.hmy.BlockChain().CurrentHeader(), - } -} - -// GetTotalStakingSnapshot .. -func (b *APIBackend) GetTotalStakingSnapshot() *big.Int { - b.TotalStakingCache.Lock() - defer b.TotalStakingCache.Unlock() - if b.TotalStakingCache.BlockHeight != -1 && - b.TotalStakingCache.BlockHeight > int64(rpc.LatestBlockNumber)-20 { - return b.TotalStakingCache.TotalStaking - } - b.TotalStakingCache.BlockHeight = int64(rpc.LatestBlockNumber) - candidates := b.hmy.BlockChain().ValidatorCandidates() - if len(candidates) == 0 { - b.TotalStakingCache.TotalStaking = big.NewInt(0) - return b.TotalStakingCache.TotalStaking - } - stakes := big.NewInt(0) - for i := range candidates { - snapshot, _ := b.hmy.BlockChain().ReadValidatorSnapshot(candidates[i]) - validator, _ := b.hmy.BlockChain().ReadValidatorInformation(candidates[i]) - if !committee.IsEligibleForEPoSAuction( - snapshot, validator, - ) { - continue - } - for i := range validator.Delegations { - stakes.Add(stakes, validator.Delegations[i].Amount) - } - } - b.TotalStakingCache.TotalStaking = stakes - return b.TotalStakingCache.TotalStaking -} - -// GetDelegationsByValidator returns all delegation information of a validator -func (b *APIBackend) GetDelegationsByValidator(validator common.Address) []*staking.Delegation { - wrapper, err := b.hmy.BlockChain().ReadValidatorInformation(validator) - if err != nil || wrapper == nil { - return nil - } - delegations := []*staking.Delegation{} - for i := range wrapper.Delegations { - delegations = append(delegations, &wrapper.Delegations[i]) - } - return delegations -} - -// GetDelegationsByDelegatorByBlock returns all delegation information of a delegator -func (b *APIBackend) GetDelegationsByDelegatorByBlock( - delegator common.Address, block *types.Block, -) ([]common.Address, []*staking.Delegation) { - addresses := []common.Address{} - delegations := []*staking.Delegation{} - delegationIndexes, err := b.hmy.BlockChain(). - ReadDelegationsByDelegatorAt(delegator, block.Number()) - if err != nil { - return nil, nil - } - - for i := range delegationIndexes { - wrapper, err := b.hmy.BlockChain().ReadValidatorInformationAt( - delegationIndexes[i].ValidatorAddress, block.Root(), - ) - if err != nil || wrapper == nil { - return nil, nil - } - - if uint64(len(wrapper.Delegations)) > delegationIndexes[i].Index { - delegations = append(delegations, &wrapper.Delegations[delegationIndexes[i].Index]) - } else { - delegations = append(delegations, nil) - } - addresses = append(addresses, delegationIndexes[i].ValidatorAddress) - } - return addresses, delegations -} - -// GetDelegationsByDelegator returns all delegation information of a delegator -func (b *APIBackend) GetDelegationsByDelegator( - delegator common.Address, -) ([]common.Address, []*staking.Delegation) { - block := b.hmy.BlockChain().CurrentBlock() - return b.GetDelegationsByDelegatorByBlock(delegator, block) -} - -// GetValidatorSelfDelegation returns the amount of staking after applying all delegated stakes -func (b *APIBackend) GetValidatorSelfDelegation(addr common.Address) *big.Int { - wrapper, err := b.hmy.BlockChain().ReadValidatorInformation(addr) - if err != nil || wrapper == nil { - return nil - } - if len(wrapper.Delegations) == 0 { - return nil - } - return wrapper.Delegations[0].Amount -} - -// GetShardState ... -func (b *APIBackend) GetShardState() (*shard.State, error) { - return b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentHeader().Epoch()) -} - -// GetCurrentStakingErrorSink .. -func (b *APIBackend) GetCurrentStakingErrorSink() types.TransactionErrorReports { - return b.hmy.nodeAPI.ReportStakingErrorSink() -} - -// GetCurrentTransactionErrorSink .. -func (b *APIBackend) GetCurrentTransactionErrorSink() types.TransactionErrorReports { - return b.hmy.nodeAPI.ReportPlainErrorSink() -} - -// GetPendingCXReceipts .. -func (b *APIBackend) GetPendingCXReceipts() []*types.CXReceiptsProof { - return b.hmy.nodeAPI.PendingCXReceipts() -} - -// GetCurrentUtilityMetrics .. -func (b *APIBackend) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) { - return network.NewUtilityMetricSnapshot(b.hmy.BlockChain()) -} - -func (b *APIBackend) readAndUpdateRawStakes( - epoch *big.Int, - decider quorum.Decider, - comm shard.Committee, - rawStakes []effective.SlotPurchase, - validatorSpreads map[common.Address]numeric.Dec, -) []effective.SlotPurchase { - for i := range comm.Slots { - slot := comm.Slots[i] - slotAddr := slot.EcdsaAddress - slotKey := slot.BLSPublicKey - spread, ok := validatorSpreads[slotAddr] - if !ok { - snapshot, err := b.hmy.BlockChain().ReadValidatorSnapshotAtEpoch(epoch, slotAddr) - if err != nil { - continue - } - wrapper := snapshot.Validator - spread = numeric.NewDecFromBigInt(wrapper.TotalDelegation()). - QuoInt64(int64(len(wrapper.SlotPubKeys))) - validatorSpreads[slotAddr] = spread - } - - commonRPC.SetRawStake(decider, slotKey, spread) - // add entry to array for median calculation - rawStakes = append(rawStakes, effective.SlotPurchase{ - slotAddr, - slotKey, - spread, - spread, - }) - } - return rawStakes -} - -func (b *APIBackend) getSuperCommittees() (*quorum.Transition, error) { - nowE := b.hmy.BlockChain().CurrentHeader().Epoch() - thenE := new(big.Int).Sub(nowE, common.Big1) - - var ( - nowCommittee, prevCommittee *shard.State - err error - ) - nowCommittee, err = b.hmy.BlockChain().ReadShardState(nowE) - if err != nil { - return nil, err - } - prevCommittee, err = b.hmy.BlockChain().ReadShardState(thenE) - if err != nil { - return nil, err - } - - stakedSlotsNow, stakedSlotsThen := - shard.ExternalSlotsAvailableForEpoch(nowE), - shard.ExternalSlotsAvailableForEpoch(thenE) - - then, now := - quorum.NewRegistry(stakedSlotsThen), - quorum.NewRegistry(stakedSlotsNow) - - rawStakes := []effective.SlotPurchase{} - validatorSpreads := map[common.Address]numeric.Dec{} - for _, comm := range prevCommittee.Shards { - decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) - // before staking skip computing - if b.hmy.BlockChain().Config().IsStaking(prevCommittee.Epoch) { - if _, err := decider.SetVoters(&comm, prevCommittee.Epoch); err != nil { - return nil, err - } - } - rawStakes = b.readAndUpdateRawStakes(thenE, decider, comm, rawStakes, validatorSpreads) - then.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider - } - then.MedianStake = effective.Median(rawStakes) - - rawStakes = []effective.SlotPurchase{} - validatorSpreads = map[common.Address]numeric.Dec{} - for _, comm := range nowCommittee.Shards { - decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) - if _, err := decider.SetVoters(&comm, nowCommittee.Epoch); err != nil { - return nil, errors.Wrapf( - err, - "committee is only available from staking epoch: %v, current epoch: %v", - b.hmy.BlockChain().Config().StakingEpoch, - b.hmy.BlockChain().CurrentHeader().Epoch(), - ) - } - rawStakes = b.readAndUpdateRawStakes(nowE, decider, comm, rawStakes, validatorSpreads) - now.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider - } - now.MedianStake = effective.Median(rawStakes) - - return &quorum.Transition{then, now}, nil -} - -// GetSuperCommittees .. -func (b *APIBackend) GetSuperCommittees() (*quorum.Transition, error) { - nowE := b.hmy.BlockChain().CurrentHeader().Epoch() - key := fmt.Sprintf("sc-%s", nowE.String()) - - res, err := b.SingleFlightRequest( - key, func() (interface{}, error) { - thenE := new(big.Int).Sub(nowE, common.Big1) - thenKey := fmt.Sprintf("sc-%s", thenE.String()) - b.apiCache.Forget(thenKey) - return b.getSuperCommittees() - }) - if err != nil { - return nil, err - } - return res.(*quorum.Transition), err -} - -// GetCurrentBadBlocks .. -func (b *APIBackend) GetCurrentBadBlocks() []core.BadBlock { - return b.hmy.BlockChain().BadBlocks() -} - -// GetLastCrossLinks .. -func (b *APIBackend) GetLastCrossLinks() ([]*types.CrossLink, error) { - crossLinks := []*types.CrossLink{} - for i := uint32(1); i < shard.Schedule.InstanceForEpoch(b.CurrentBlock().Epoch()).NumShards(); i++ { - link, err := b.hmy.BlockChain().ReadShardLastCrossLink(i) - if err != nil { - return nil, err - } - crossLinks = append(crossLinks, link) - } - - return crossLinks, nil -} - -// GetNodeMetadata .. -func (b *APIBackend) GetNodeMetadata() commonRPC.NodeMetadata { - cfg := nodeconfig.GetDefaultConfig() - header := b.CurrentBlock().Header() - var blockEpoch *uint64 - - if header.ShardID() == shard.BeaconChainShardID { - sched := shard.Schedule.InstanceForEpoch(header.Epoch()) - b := sched.BlocksPerEpoch() - blockEpoch = &b - } - - blsKeys := []string{} - if cfg.ConsensusPriKey != nil { - for _, key := range cfg.ConsensusPriKey { - blsKeys = append(blsKeys, key.Pub.Bytes.Hex()) - } - } - c := commonRPC.C{} - c.TotalKnownPeers, c.Connected, c.NotConnected = b.hmy.nodeAPI.PeerConnectivity() - - return commonRPC.NodeMetadata{ - blsKeys, - nodeconfig.GetVersion(), - string(cfg.GetNetworkType()), - *b.ChainConfig(), - b.IsLeader(), - b.GetShardID(), - header.Epoch().Uint64(), - blockEpoch, - cfg.Role().String(), - cfg.DNSZone, - cfg.GetArchival(), - b.hmy.nodeAPI.GetNodeBootTime(), - nodeconfig.GetPeerID(), - c, - } -} - -// GetPeerInfo returns the peer info to the node, including blocked peer, connected peer, number of peers -func (b *APIBackend) GetPeerInfo() commonRPC.NodePeerInfo { - - topics := b.hmy.nodeAPI.ListTopic() - p := make([]commonRPC.P, len(topics)) - - for i, t := range topics { - topicPeer := b.hmy.nodeAPI.ListPeer(t) - p[i].Topic = t - p[i].Peers = make([]peer.ID, len(topicPeer)) - copy(p[i].Peers, topicPeer) - } - - return commonRPC.NodePeerInfo{ - PeerID: nodeconfig.GetPeerID(), - BlockedPeers: b.hmy.nodeAPI.ListBlockedPeer(), - P: p, - } -} - -// GetBlockSigners .. -func (b *APIBackend) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *internal_bls.Mask, error) { - block, err := b.BlockByNumber(ctx, blockNr) - if err != nil { - return nil, nil, err - } - blockWithSigners, err := b.BlockByNumber(ctx, blockNr+1) - if err != nil { - return nil, nil, err - } - committee, err := b.GetValidators(block.Epoch()) - if err != nil { - return nil, nil, err - } - pubkeys := make([]internal_bls.PublicKeyWrapper, len(committee.Slots)) - for _, validator := range committee.Slots { - wrapper := internal_bls.PublicKeyWrapper{Bytes: validator.BLSPublicKey} - if wrapper.Object, err = bls.BytesToBLSPublicKey(wrapper.Bytes[:]); err != nil { - return nil, nil, err - } - } - mask, err := internal_bls.NewMask(pubkeys, nil) - if err != nil { - return nil, nil, err - } - err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) - if err != nil { - return nil, nil, err - } - return committee.Slots, mask, nil -} - -// IsStakingEpoch ... -func (b *APIBackend) IsStakingEpoch(epoch *big.Int) bool { - return b.hmy.BlockChain().Config().IsStaking(epoch) -} - -// GetLeaderAddress returns the one address of the leader -func (b *APIBackend) GetLeaderAddress(a common.Address, e *big.Int) string { - if b.IsStakingEpoch(e) { - if leader, exists := b.LeaderCache.Get(a); exists { - bech32, _ := internal_common.AddressToBech32(leader.(common.Address)) - return bech32 - } - committee, err := b.GetValidators(e) - if err != nil { - return "" - } - for _, v := range committee.Slots { - addr := utils.GetAddressFromBLSPubKeyBytes(v.BLSPublicKey[:]) - b.LeaderCache.Add(addr, v.EcdsaAddress) - if addr == a { - bech32, _ := internal_common.AddressToBech32(v.EcdsaAddress) - return bech32 - } - } - // Did not find matching address - return "missing" // FIXME: Change this to empty string - } - bech32, _ := internal_common.AddressToBech32(a) - return bech32 -} diff --git a/hmy/backend.go b/hmy/backend.go deleted file mode 100644 index 50c22b19b..000000000 --- a/hmy/backend.go +++ /dev/null @@ -1,114 +0,0 @@ -package hmy - -import ( - "math/big" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/harmony-one/harmony/core" - "github.com/harmony-one/harmony/core/types" - staking "github.com/harmony-one/harmony/staking/types" - lru "github.com/hashicorp/golang-lru" - "github.com/libp2p/go-libp2p-core/peer" -) - -const ( - leaderCacheSize = 250 // Approx number of BLS keys in committee -) - -// Harmony implements the Harmony full node service. -type Harmony struct { - // Channel for shutting down the service - shutdownChan chan bool // Channel for shutting down the Harmony - bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests - blockchain *core.BlockChain - beaconchain *core.BlockChain - txPool *core.TxPool - cxPool *core.CxPool - eventMux *event.TypeMux - // DB interfaces - chainDb ethdb.Database // Block chain database - bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports - APIBackend *APIBackend - nodeAPI NodeAPI - // aka network version, which is used to identify which network we are using - networkID uint64 - // RPCGasCap is the global gas cap for eth-call variants. - RPCGasCap *big.Int `toml:",omitempty"` - shardID uint32 -} - -// NodeAPI is the list of functions from node used to call rpc apis. -type NodeAPI interface { - AddPendingStakingTransaction(*staking.StakingTransaction) error - AddPendingTransaction(newTx *types.Transaction) error - Blockchain() *core.BlockChain - Beaconchain() *core.BlockChain - GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetTransactionsCount(address, txType string) (uint64, error) - GetStakingTransactionsCount(address, txType string) (uint64, error) - IsCurrentlyLeader() bool - ReportStakingErrorSink() types.TransactionErrorReports - ReportPlainErrorSink() types.TransactionErrorReports - PendingCXReceipts() []*types.CXReceiptsProof - GetNodeBootTime() int64 - PeerConnectivity() (int, int, int) - ListPeer(topic string) []peer.ID - ListTopic() []string - ListBlockedPeer() []peer.ID -} - -// New creates a new Harmony object (including the -// initialisation of the common Harmony object) -func New( - nodeAPI NodeAPI, txPool *core.TxPool, - cxPool *core.CxPool, eventMux *event.TypeMux, shardID uint32, -) (*Harmony, error) { - chainDb := nodeAPI.Blockchain().ChainDB() - hmy := &Harmony{ - shutdownChan: make(chan bool), - bloomRequests: make(chan chan *bloombits.Retrieval), - blockchain: nodeAPI.Blockchain(), - beaconchain: nodeAPI.Beaconchain(), - txPool: txPool, - cxPool: cxPool, - eventMux: eventMux, - chainDb: chainDb, - nodeAPI: nodeAPI, - networkID: 1, // TODO(ricl): this should be from config - shardID: shardID, - } - cache, _ := lru.New(leaderCacheSize) - hmy.APIBackend = &APIBackend{ - hmy: hmy, - TotalStakingCache: struct { - sync.Mutex - BlockHeight int64 - TotalStaking *big.Int - }{ - BlockHeight: -1, - TotalStaking: big.NewInt(0), - }, - LeaderCache: cache, - } - return hmy, nil -} - -// TxPool ... -func (s *Harmony) TxPool() *core.TxPool { return s.txPool } - -// CxPool is used to store the blockHashes, where the corresponding block contains the cross shard receipts to be sent -func (s *Harmony) CxPool() *core.CxPool { return s.cxPool } - -// BlockChain ... -func (s *Harmony) BlockChain() *core.BlockChain { return s.blockchain } - -//BeaconChain ... -func (s *Harmony) BeaconChain() *core.BlockChain { return s.beaconchain } - -// NetVersion returns the network version, i.e. network ID identifying which network we are using -func (s *Harmony) NetVersion() uint64 { return s.networkID } diff --git a/hmy/blockchain.go b/hmy/blockchain.go new file mode 100644 index 000000000..f6546bcfe --- /dev/null +++ b/hmy/blockchain.go @@ -0,0 +1,244 @@ +package hmy + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/rpc" + "github.com/harmony-one/harmony/block" + "github.com/harmony-one/harmony/core" + "github.com/harmony-one/harmony/core/state" + "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/crypto/bls" + internal_bls "github.com/harmony-one/harmony/crypto/bls" + internal_common "github.com/harmony-one/harmony/internal/common" + "github.com/harmony-one/harmony/internal/params" + "github.com/harmony-one/harmony/internal/utils" + "github.com/harmony-one/harmony/shard" + "github.com/pkg/errors" +) + +// ChainConfig ... +func (hmy *Harmony) ChainConfig() *params.ChainConfig { + return hmy.BlockChain.Config() +} + +// GetShardState ... +func (hmy *Harmony) GetShardState() (*shard.State, error) { + return hmy.BlockChain.ReadShardState(hmy.BlockChain.CurrentHeader().Epoch()) +} + +// GetBlockSigners .. +func (hmy *Harmony) GetBlockSigners( + ctx context.Context, blockNr rpc.BlockNumber, +) (shard.SlotList, *internal_bls.Mask, error) { + blk, err := hmy.BlockByNumber(ctx, blockNr) + if err != nil { + return nil, nil, err + } + blockWithSigners, err := hmy.BlockByNumber(ctx, blockNr+1) + if err != nil { + return nil, nil, err + } + committee, err := hmy.GetValidators(blk.Epoch()) + if err != nil { + return nil, nil, err + } + pubKeys := make([]internal_bls.PublicKeyWrapper, len(committee.Slots)) + for _, validator := range committee.Slots { + wrapper := internal_bls.PublicKeyWrapper{Bytes: validator.BLSPublicKey} + if wrapper.Object, err = bls.BytesToBLSPublicKey(wrapper.Bytes[:]); err != nil { + return nil, nil, err + } + } + mask, err := internal_bls.NewMask(pubKeys, nil) + if err != nil { + return nil, nil, err + } + err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) + if err != nil { + return nil, nil, err + } + return committee.Slots, mask, nil +} + +// GetLatestChainHeaders .. +func (hmy *Harmony) GetLatestChainHeaders() *block.HeaderPair { + return &block.HeaderPair{ + BeaconHeader: hmy.BeaconChain.CurrentHeader(), + ShardHeader: hmy.BlockChain.CurrentHeader(), + } +} + +// GetLastCrossLinks .. +func (hmy *Harmony) GetLastCrossLinks() ([]*types.CrossLink, error) { + crossLinks := []*types.CrossLink{} + for i := uint32(1); i < shard.Schedule.InstanceForEpoch(hmy.CurrentBlock().Epoch()).NumShards(); i++ { + link, err := hmy.BlockChain.ReadShardLastCrossLink(i) + if err != nil { + return nil, err + } + crossLinks = append(crossLinks, link) + } + + return crossLinks, nil +} + +// CurrentBlock ... +func (hmy *Harmony) CurrentBlock() *types.Block { + return types.NewBlockWithHeader(hmy.BlockChain.CurrentHeader()) +} + +// GetBlock ... +func (hmy *Harmony) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { + return hmy.BlockChain.GetBlockByHash(hash), nil +} + +// GetCurrentBadBlocks .. +func (hmy *Harmony) GetCurrentBadBlocks() []core.BadBlock { + return hmy.BlockChain.BadBlocks() +} + +// GetBalance returns balance of an given address. +func (hmy *Harmony) GetBalance(ctx context.Context, address common.Address, blockNum rpc.BlockNumber) (*big.Int, error) { + s, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) + if s == nil || err != nil { + return nil, err + } + return s.GetBalance(address), s.Error() +} + +// BlockByNumber ... +func (hmy *Harmony) BlockByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*types.Block, error) { + // Pending block is only known by the miner + if blockNum == rpc.PendingBlockNumber { + return nil, errors.New("not implemented") + } + // Otherwise resolve and return the block + if blockNum == rpc.LatestBlockNumber { + return hmy.BlockChain.CurrentBlock(), nil + } + return hmy.BlockChain.GetBlockByNumber(uint64(blockNum)), nil +} + +// HeaderByNumber ... +func (hmy *Harmony) HeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*block.Header, error) { + // Pending block is only known by the miner + if blockNum == rpc.PendingBlockNumber { + return nil, errors.New("not implemented") + } + // Otherwise resolve and return the block + if blockNum == rpc.LatestBlockNumber { + return hmy.BlockChain.CurrentBlock().Header(), nil + } + return hmy.BlockChain.GetHeaderByNumber(uint64(blockNum)), nil +} + +// HeaderByHash ... +func (hmy *Harmony) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) { + header := hmy.BlockChain.GetHeaderByHash(blockHash) + if header == nil { + return nil, errors.New("Header is not found") + } + return header, nil +} + +// StateAndHeaderByNumber ... +func (hmy *Harmony) StateAndHeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*state.DB, *block.Header, error) { + // Pending state is only known by the miner + if blockNum == rpc.PendingBlockNumber { + return nil, nil, errors.New("not implemented") + } + // Otherwise resolve the block number and return its state + header, err := hmy.HeaderByNumber(ctx, blockNum) + if header == nil || err != nil { + return nil, nil, err + } + stateDb, err := hmy.BlockChain.StateAt(header.Root()) + return stateDb, header, err +} + +// GetLeaderAddress returns the one address of the leader, given the coinbaseAddr. +// Note that the coinbaseAddr is overloaded with the BLS pub key hash in staking era. +func (hmy *Harmony) GetLeaderAddress(coinbaseAddr common.Address, epoch *big.Int) string { + if hmy.IsStakingEpoch(epoch) { + if leader, exists := hmy.leaderCache.Get(coinbaseAddr); exists { + bech32, _ := internal_common.AddressToBech32(leader.(common.Address)) + return bech32 + } + committee, err := hmy.GetValidators(epoch) + if err != nil { + return "" + } + for _, val := range committee.Slots { + addr := utils.GetAddressFromBLSPubKeyBytes(val.BLSPublicKey[:]) + hmy.leaderCache.Add(addr, val.EcdsaAddress) + if addr == coinbaseAddr { + bech32, _ := internal_common.AddressToBech32(val.EcdsaAddress) + return bech32 + } + } + return "" // Did not find matching address + } + bech32, _ := internal_common.AddressToBech32(coinbaseAddr) + return bech32 +} + +// Filter related APIs + +// GetLogs ... +func (hmy *Harmony) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) { + receipts := hmy.BlockChain.GetReceiptsByHash(blockHash) + if receipts == nil { + return nil, errors.New("Missing receipts") + } + logs := make([][]*types.Log, len(receipts)) + for i, receipt := range receipts { + logs[i] = receipt.Logs + } + return logs, nil +} + +// ServiceFilter ... +func (hmy *Harmony) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { + // TODO(dm): implement +} + +// SubscribeNewTxsEvent subscribes new tx event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { + return hmy.TxPool.SubscribeNewTxsEvent(ch) +} + +// SubscribeChainEvent subscribes chain event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { + return hmy.BlockChain.SubscribeChainEvent(ch) +} + +// SubscribeChainHeadEvent subcribes chain head event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { + return hmy.BlockChain.SubscribeChainHeadEvent(ch) +} + +// SubscribeChainSideEvent subcribes chain side event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { + return hmy.BlockChain.SubscribeChainSideEvent(ch) +} + +// SubscribeRemovedLogsEvent subcribes removed logs event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { + return hmy.BlockChain.SubscribeRemovedLogsEvent(ch) +} + +// SubscribeLogsEvent subcribes log event. +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { + return hmy.BlockChain.SubscribeLogsEvent(ch) +} diff --git a/hmy/hmy.go b/hmy/hmy.go new file mode 100644 index 000000000..7c78f4104 --- /dev/null +++ b/hmy/hmy.go @@ -0,0 +1,202 @@ +package hmy + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/bloombits" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" + "github.com/harmony-one/harmony/api/proto" + "github.com/harmony-one/harmony/block" + "github.com/harmony-one/harmony/core" + "github.com/harmony-one/harmony/core/state" + "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/core/vm" + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" + commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" + "github.com/harmony-one/harmony/shard" + staking "github.com/harmony-one/harmony/staking/types" + lru "github.com/hashicorp/golang-lru" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/pkg/errors" + "golang.org/x/sync/singleflight" +) + +const ( + // BloomBitsBlocks is the number of blocks a single bloom bit section vector + // contains on the server side. + BloomBitsBlocks uint64 = 4096 + leaderCacheSize = 250 // Approx number of BLS keys in committee + totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same +) + +var ( + // ErrFinalizedTransaction is returned if the transaction to be submitted is already on-chain + ErrFinalizedTransaction = errors.New("transaction already finalized") +) + +// Harmony implements the Harmony full node service. +type Harmony struct { + // Channel for shutting down the service + ShutdownChan chan bool // Channel for shutting down the Harmony + BloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests + BlockChain *core.BlockChain + BeaconChain *core.BlockChain + TxPool *core.TxPool + CxPool *core.CxPool // CxPool is used to store the blockHashes of blocks containing cx receipts to be sent + // DB interfaces + BloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports + NodeAPI NodeAPI + // ChainID is used to identify which network we are using + ChainID uint64 + // RPCGasCap is the global gas cap for eth-call variants. + RPCGasCap *big.Int `toml:",omitempty"` + ShardID uint32 + + // Internals + eventMux *event.TypeMux + chainDb ethdb.Database // Block chain database + // group for units of work which can be executed with duplicate suppression. + group singleflight.Group + // leaderCache to save on recomputation every epoch. + leaderCache *lru.Cache + // totalStakeCache to save on recomputation for `totalStakeCacheDuration` blocks. + totalStakeCache *totalStakeCache +} + +// NodeAPI is the list of functions from node used to call rpc apis. +type NodeAPI interface { + AddPendingStakingTransaction(*staking.StakingTransaction) error + AddPendingTransaction(newTx *types.Transaction) error + Blockchain() *core.BlockChain + Beaconchain() *core.BlockChain + GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) + GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) + GetTransactionsCount(address, txType string) (uint64, error) + GetStakingTransactionsCount(address, txType string) (uint64, error) + IsCurrentlyLeader() bool + ReportStakingErrorSink() types.TransactionErrorReports + ReportPlainErrorSink() types.TransactionErrorReports + PendingCXReceipts() []*types.CXReceiptsProof + GetNodeBootTime() int64 + PeerConnectivity() (int, int, int) + ListPeer(topic string) []peer.ID + ListTopic() []string + ListBlockedPeer() []peer.ID +} + +// New creates a new Harmony object (including the +// initialisation of the common Harmony object) +func New( + nodeAPI NodeAPI, txPool *core.TxPool, cxPool *core.CxPool, shardID uint32, +) *Harmony { + chainDb := nodeAPI.Blockchain().ChainDB() + leaderCache, _ := lru.New(leaderCacheSize) + totalStakeCache := newTotalStakeCache(totalStakeCacheDuration) + return &Harmony{ + ShutdownChan: make(chan bool), + BloomRequests: make(chan chan *bloombits.Retrieval), + BlockChain: nodeAPI.Blockchain(), + BeaconChain: nodeAPI.Beaconchain(), + TxPool: txPool, + CxPool: cxPool, + eventMux: new(event.TypeMux), + chainDb: chainDb, + NodeAPI: nodeAPI, + ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), + ShardID: shardID, + leaderCache: leaderCache, + totalStakeCache: totalStakeCache, + } +} + +// SingleFlightRequest .. +func (hmy *Harmony) SingleFlightRequest( + key string, + fn func() (interface{}, error), +) (interface{}, error) { + res, err, _ := hmy.group.Do(key, fn) + return res, err +} + +// SingleFlightForgetKey ... +func (hmy *Harmony) SingleFlightForgetKey(key string) { + hmy.group.Forget(key) +} + +// ProtocolVersion ... +func (hmy *Harmony) ProtocolVersion() int { + return proto.ProtocolVersion +} + +// IsLeader exposes if node is currently leader +func (hmy *Harmony) IsLeader() bool { + return hmy.NodeAPI.IsCurrentlyLeader() +} + +// GetNodeMetadata .. +func (hmy *Harmony) GetNodeMetadata() commonRPC.NodeMetadata { + cfg := nodeconfig.GetDefaultConfig() + header := hmy.CurrentBlock().Header() + var blockEpoch *uint64 + + if header.ShardID() == shard.BeaconChainShardID { + sched := shard.Schedule.InstanceForEpoch(header.Epoch()) + b := sched.BlocksPerEpoch() + blockEpoch = &b + } + + blsKeys := []string{} + if cfg.ConsensusPriKey != nil { + for _, key := range cfg.ConsensusPriKey { + blsKeys = append(blsKeys, key.Pub.Bytes.Hex()) + } + } + c := commonRPC.C{} + c.TotalKnownPeers, c.Connected, c.NotConnected = hmy.NodeAPI.PeerConnectivity() + + return commonRPC.NodeMetadata{ + BLSPublicKey: blsKeys, + Version: nodeconfig.GetVersion(), + NetworkType: string(cfg.GetNetworkType()), + ChainConfig: *hmy.ChainConfig(), + IsLeader: hmy.IsLeader(), + ShardID: hmy.ShardID, + CurrentEpoch: header.Epoch().Uint64(), + BlocksPerEpoch: blockEpoch, + Role: cfg.Role().String(), + DNSZone: cfg.DNSZone, + Archival: cfg.GetArchival(), + NodeBootTime: hmy.NodeAPI.GetNodeBootTime(), + PeerID: nodeconfig.GetPeerID(), + C: c, + } +} + +// GetEVM returns a new EVM entity +func (hmy *Harmony) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) { + state.SetBalance(msg.From(), math.MaxBig256) + vmError := func() error { return nil } + vmCtx := core.NewEVMContext(msg, header, hmy.BlockChain, nil) + return vm.NewEVM(vmCtx, state, hmy.BlockChain.Config(), *hmy.BlockChain.GetVMConfig()), vmError, nil +} + +// ChainDb .. +func (hmy *Harmony) ChainDb() ethdb.Database { + return hmy.chainDb +} + +// EventMux .. +func (hmy *Harmony) EventMux() *event.TypeMux { + return hmy.eventMux +} + +// BloomStatus ... +// TODO: this is not implemented or verified yet for harmony. +func (hmy *Harmony) BloomStatus() (uint64, uint64) { + sections, _, _ := hmy.BloomIndexer.Sections() + return BloomBitsBlocks, sections +} diff --git a/hmy/network.go b/hmy/network.go new file mode 100644 index 000000000..1e8a65089 --- /dev/null +++ b/hmy/network.go @@ -0,0 +1,33 @@ +package hmy + +import ( + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" + commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" + "github.com/harmony-one/harmony/staking/network" + "github.com/libp2p/go-libp2p-core/peer" +) + +// GetCurrentUtilityMetrics .. +func (hmy *Harmony) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) { + return network.NewUtilityMetricSnapshot(hmy.BlockChain) +} + +// GetPeerInfo returns the peer info to the node, including blocked peer, connected peer, number of peers +func (hmy *Harmony) GetPeerInfo() commonRPC.NodePeerInfo { + + topics := hmy.NodeAPI.ListTopic() + p := make([]commonRPC.P, len(topics)) + + for i, t := range topics { + topicPeer := hmy.NodeAPI.ListPeer(t) + p[i].Topic = t + p[i].Peers = make([]peer.ID, len(topicPeer)) + copy(p[i].Peers, topicPeer) + } + + return commonRPC.NodePeerInfo{ + PeerID: nodeconfig.GetPeerID(), + BlockedPeers: hmy.NodeAPI.ListBlockedPeer(), + P: p, + } +} diff --git a/hmy/pool.go b/hmy/pool.go new file mode 100644 index 000000000..f7e97a10c --- /dev/null +++ b/hmy/pool.go @@ -0,0 +1,48 @@ +package hmy + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/harmony-one/harmony/core/types" +) + +// GetPoolStats returns the number of pending and queued transactions +func (hmy *Harmony) GetPoolStats() (pendingCount, queuedCount int) { + return hmy.TxPool.Stats() +} + +// GetPoolNonce ... +func (hmy *Harmony) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { + return hmy.TxPool.State().GetNonce(addr), nil +} + +// GetPoolTransaction ... +func (hmy *Harmony) GetPoolTransaction(hash common.Hash) types.PoolTransaction { + return hmy.TxPool.Get(hash) +} + +// GetPendingCXReceipts .. +func (hmy *Harmony) GetPendingCXReceipts() []*types.CXReceiptsProof { + return hmy.NodeAPI.PendingCXReceipts() +} + +// GetPoolTransactions returns pool transactions. +func (hmy *Harmony) GetPoolTransactions() (types.PoolTransactions, error) { + pending, err := hmy.TxPool.Pending() + if err != nil { + return nil, err + } + queued, err := hmy.TxPool.Queued() + if err != nil { + return nil, err + } + var txs types.PoolTransactions + for _, batch := range pending { + txs = append(txs, batch...) + } + for _, batch := range queued { + txs = append(txs, batch...) + } + return txs, nil +} diff --git a/hmy/staking.go b/hmy/staking.go new file mode 100644 index 000000000..012f6803d --- /dev/null +++ b/hmy/staking.go @@ -0,0 +1,476 @@ +package hmy + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/harmony-one/harmony/consensus/quorum" + "github.com/harmony-one/harmony/core/rawdb" + "github.com/harmony-one/harmony/core/types" + internal_common "github.com/harmony-one/harmony/internal/common" + commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" + "github.com/harmony-one/harmony/numeric" + "github.com/harmony-one/harmony/shard" + "github.com/harmony-one/harmony/shard/committee" + "github.com/harmony-one/harmony/staking/availability" + "github.com/harmony-one/harmony/staking/effective" + staking "github.com/harmony-one/harmony/staking/types" + "github.com/pkg/errors" +) + +var ( + zero = numeric.ZeroDec() +) + +func (hmy *Harmony) readAndUpdateRawStakes( + epoch *big.Int, + decider quorum.Decider, + comm shard.Committee, + rawStakes []effective.SlotPurchase, + validatorSpreads map[common.Address]numeric.Dec, +) []effective.SlotPurchase { + for i := range comm.Slots { + slot := comm.Slots[i] + slotAddr := slot.EcdsaAddress + slotKey := slot.BLSPublicKey + spread, ok := validatorSpreads[slotAddr] + if !ok { + snapshot, err := hmy.BlockChain.ReadValidatorSnapshotAtEpoch(epoch, slotAddr) + if err != nil { + continue + } + wrapper := snapshot.Validator + spread = numeric.NewDecFromBigInt(wrapper.TotalDelegation()). + QuoInt64(int64(len(wrapper.SlotPubKeys))) + validatorSpreads[slotAddr] = spread + } + + commonRPC.SetRawStake(decider, slotKey, spread) + // add entry to array for median calculation + rawStakes = append(rawStakes, effective.SlotPurchase{ + Addr: slotAddr, + Key: slotKey, + RawStake: spread, + EPoSStake: spread, + }) + } + return rawStakes +} + +func (hmy *Harmony) getSuperCommittees() (*quorum.Transition, error) { + nowE := hmy.BlockChain.CurrentHeader().Epoch() + thenE := new(big.Int).Sub(nowE, common.Big1) + + var ( + nowCommittee, prevCommittee *shard.State + err error + ) + nowCommittee, err = hmy.BlockChain.ReadShardState(nowE) + if err != nil { + return nil, err + } + prevCommittee, err = hmy.BlockChain.ReadShardState(thenE) + if err != nil { + return nil, err + } + + stakedSlotsNow, stakedSlotsThen := + shard.ExternalSlotsAvailableForEpoch(nowE), + shard.ExternalSlotsAvailableForEpoch(thenE) + + then, now := + quorum.NewRegistry(stakedSlotsThen), + quorum.NewRegistry(stakedSlotsNow) + + rawStakes := []effective.SlotPurchase{} + validatorSpreads := map[common.Address]numeric.Dec{} + for _, comm := range prevCommittee.Shards { + decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) + // before staking skip computing + if hmy.BlockChain.Config().IsStaking(prevCommittee.Epoch) { + if _, err := decider.SetVoters(&comm, prevCommittee.Epoch); err != nil { + return nil, err + } + } + rawStakes = hmy.readAndUpdateRawStakes(thenE, decider, comm, rawStakes, validatorSpreads) + then.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider + } + then.MedianStake = effective.Median(rawStakes) + + rawStakes = []effective.SlotPurchase{} + validatorSpreads = map[common.Address]numeric.Dec{} + for _, comm := range nowCommittee.Shards { + decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) + if _, err := decider.SetVoters(&comm, nowCommittee.Epoch); err != nil { + return nil, errors.Wrapf( + err, + "committee is only available from staking epoch: %v, current epoch: %v", + hmy.BlockChain.Config().StakingEpoch, + hmy.BlockChain.CurrentHeader().Epoch(), + ) + } + rawStakes = hmy.readAndUpdateRawStakes(nowE, decider, comm, rawStakes, validatorSpreads) + now.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider + } + now.MedianStake = effective.Median(rawStakes) + + return &quorum.Transition{Previous: then, Current: now}, nil +} + +// IsStakingEpoch ... +func (hmy *Harmony) IsStakingEpoch(epoch *big.Int) bool { + return hmy.BlockChain.Config().IsStaking(epoch) +} + +// SendStakingTx adds a staking transaction +func (hmy *Harmony) SendStakingTx(ctx context.Context, signedStakingTx *staking.StakingTransaction) error { + stx, _, _, _ := rawdb.ReadStakingTransaction(hmy.chainDb, signedStakingTx.Hash()) + if stx == nil { + return hmy.NodeAPI.AddPendingStakingTransaction(signedStakingTx) + } + return ErrFinalizedTransaction +} + +// GetStakingTransactionsHistory returns list of staking transactions hashes of address. +func (hmy *Harmony) GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) { + return hmy.NodeAPI.GetStakingTransactionsHistory(address, txType, order) +} + +// GetStakingTransactionsCount returns the number of staking transactions of address. +func (hmy *Harmony) GetStakingTransactionsCount(address, txType string) (uint64, error) { + return hmy.NodeAPI.GetStakingTransactionsCount(address, txType) +} + +// GetSuperCommittees .. +func (hmy *Harmony) GetSuperCommittees() (*quorum.Transition, error) { + nowE := hmy.BlockChain.CurrentHeader().Epoch() + key := fmt.Sprintf("sc-%s", nowE.String()) + + res, err := hmy.SingleFlightRequest( + key, func() (interface{}, error) { + thenE := new(big.Int).Sub(nowE, common.Big1) + thenKey := fmt.Sprintf("sc-%s", thenE.String()) + hmy.group.Forget(thenKey) + return hmy.getSuperCommittees() + }) + if err != nil { + return nil, err + } + return res.(*quorum.Transition), err +} + +// GetValidators returns validators for a particular epoch. +func (hmy *Harmony) GetValidators(epoch *big.Int) (*shard.Committee, error) { + state, err := hmy.BlockChain.ReadShardState(epoch) + if err != nil { + return nil, err + } + for _, cmt := range state.Shards { + if cmt.ShardID == hmy.ShardID { + return &cmt, nil + } + } + return nil, nil +} + +// GetValidatorSelfDelegation returns the amount of staking after applying all delegated stakes +func (hmy *Harmony) GetValidatorSelfDelegation(addr common.Address) *big.Int { + wrapper, err := hmy.BlockChain.ReadValidatorInformation(addr) + if err != nil || wrapper == nil { + return nil + } + if len(wrapper.Delegations) == 0 { + return nil + } + return wrapper.Delegations[0].Amount +} + +// GetElectedValidatorAddresses returns the address of elected validators for current epoch +func (hmy *Harmony) GetElectedValidatorAddresses() []common.Address { + list, _ := hmy.BlockChain.ReadShardState(hmy.BlockChain.CurrentBlock().Epoch()) + return list.StakedValidators().Addrs +} + +// GetAllValidatorAddresses returns the up to date validator candidates for next epoch +func (hmy *Harmony) GetAllValidatorAddresses() []common.Address { + return hmy.BlockChain.ValidatorCandidates() +} + +// GetValidatorInformation returns the information of validator +func (hmy *Harmony) GetValidatorInformation( + addr common.Address, block *types.Block, +) (*staking.ValidatorRPCEnhanced, error) { + bc := hmy.BlockChain + wrapper, err := bc.ReadValidatorInformationAt(addr, block.Root()) + if err != nil { + s, _ := internal_common.AddressToBech32(addr) + return nil, errors.Wrapf(err, "not found address in current state %s", s) + } + + now := block.Epoch() + // At the last block of epoch, block epoch is e while val.LastEpochInCommittee + // is already updated to e+1. So need the >= check rather than == + inCommittee := wrapper.LastEpochInCommittee.Cmp(now) >= 0 + defaultReply := &staking.ValidatorRPCEnhanced{ + CurrentlyInCommittee: inCommittee, + Wrapper: *wrapper, + Performance: nil, + ComputedMetrics: nil, + TotalDelegated: wrapper.TotalDelegation(), + EPoSStatus: effective.ValidatorStatus( + inCommittee, wrapper.Status, + ).String(), + EPoSWinningStake: nil, + BootedStatus: nil, + ActiveStatus: wrapper.Validator.Status.String(), + Lifetime: &staking.AccumulatedOverLifetime{ + BlockReward: wrapper.BlockReward, + Signing: wrapper.Counters, + APR: zero, + }, + } + + snapshot, err := bc.ReadValidatorSnapshotAtEpoch( + now, addr, + ) + + if err != nil { + return defaultReply, nil + } + + computed := availability.ComputeCurrentSigning( + snapshot.Validator, wrapper, + ) + beaconChainBlocks := uint64( + hmy.BeaconChain.CurrentBlock().Header().Number().Int64(), + ) % shard.Schedule.BlocksPerEpoch() + computed.BlocksLeftInEpoch = shard.Schedule.BlocksPerEpoch() - beaconChainBlocks + + if defaultReply.CurrentlyInCommittee { + defaultReply.Performance = &staking.CurrentEpochPerformance{ + CurrentSigningPercentage: *computed, + } + } + + stats, err := bc.ReadValidatorStats(addr) + if err != nil { + // when validator has no stats, default boot-status to not booted + notBooted := effective.NotBooted.String() + defaultReply.BootedStatus = ¬Booted + return defaultReply, nil + } + + latestAPR := numeric.ZeroDec() + l := len(stats.APRs) + if l > 0 { + latestAPR = stats.APRs[l-1].Value + } + defaultReply.Lifetime.APR = latestAPR + defaultReply.Lifetime.EpochAPRs = stats.APRs + + // average apr cache keys + // key := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()) + // prevKey := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()-1) + + // delete entry for previous epoch + // b.apiCache.Forget(prevKey) + + // calculate last APRHistoryLength epochs for averaging APR + // epochFrom := bc.Config().StakingEpoch + // nowMinus := big.NewInt(0).Sub(now, big.NewInt(staking.APRHistoryLength)) + // if nowMinus.Cmp(epochFrom) > 0 { + // epochFrom = nowMinus + // } + + // if len(stats.APRs) > 0 && stats.APRs[0].Epoch.Cmp(epochFrom) > 0 { + // epochFrom = stats.APRs[0].Epoch + // } + + // epochToAPRs := map[int64]numeric.Dec{} + // for i := 0; i < len(stats.APRs); i++ { + // entry := stats.APRs[i] + // epochToAPRs[entry.Epoch.Int64()] = entry.Value + // } + + // at this point, validator is active and has apr's for the recent 100 epochs + // compute average apr over history + // if avgAPR, err := b.SingleFlightRequest( + // key, func() (interface{}, error) { + // total := numeric.ZeroDec() + // count := 0 + // for i := epochFrom.Int64(); i < now.Int64(); i++ { + // if apr, ok := epochToAPRs[i]; ok { + // total = total.Add(apr) + // } + // count++ + // } + // if count == 0 { + // return nil, errors.New("no apr snapshots available") + // } + // return total.QuoInt64(int64(count)), nil + // }, + // ); err != nil { + // // could not compute average apr from snapshot + // // assign the latest apr available from stats + // defaultReply.Lifetime.APR = numeric.ZeroDec() + // } else { + // defaultReply.Lifetime.APR = avgAPR.(numeric.Dec) + // } + + if defaultReply.CurrentlyInCommittee { + defaultReply.ComputedMetrics = stats + defaultReply.EPoSWinningStake = &stats.TotalEffectiveStake + } + + if !defaultReply.CurrentlyInCommittee { + reason := stats.BootedStatus.String() + defaultReply.BootedStatus = &reason + } + + return defaultReply, nil +} + +// GetMedianRawStakeSnapshot .. +func (hmy *Harmony) GetMedianRawStakeSnapshot() ( + *committee.CompletedEPoSRound, error, +) { + blockNum := hmy.CurrentBlock().NumberU64() + key := fmt.Sprintf("median-%d", blockNum) + + // delete cache for previous block + prevKey := fmt.Sprintf("median-%d", blockNum-1) + hmy.group.Forget(prevKey) + + res, err := hmy.SingleFlightRequest( + key, + func() (interface{}, error) { + // Compute for next epoch + epoch := big.NewInt(0).Add(hmy.CurrentBlock().Epoch(), big.NewInt(1)) + return committee.NewEPoSRound(epoch, hmy.BlockChain) + }, + ) + if err != nil { + return nil, err + } + return res.(*committee.CompletedEPoSRound), nil +} + +// GetDelegationsByValidator returns all delegation information of a validator +func (hmy *Harmony) GetDelegationsByValidator(validator common.Address) []*staking.Delegation { + wrapper, err := hmy.BlockChain.ReadValidatorInformation(validator) + if err != nil || wrapper == nil { + return nil + } + delegations := []*staking.Delegation{} + for i := range wrapper.Delegations { + delegations = append(delegations, &wrapper.Delegations[i]) + } + return delegations +} + +// GetDelegationsByDelegator returns all delegation information of a delegator +func (hmy *Harmony) GetDelegationsByDelegator( + delegator common.Address, +) ([]common.Address, []*staking.Delegation) { + block := hmy.BlockChain.CurrentBlock() + return hmy.GetDelegationsByDelegatorByBlock(delegator, block) +} + +// GetDelegationsByDelegatorByBlock returns all delegation information of a delegator +func (hmy *Harmony) GetDelegationsByDelegatorByBlock( + delegator common.Address, block *types.Block, +) ([]common.Address, []*staking.Delegation) { + addresses := []common.Address{} + delegations := []*staking.Delegation{} + delegationIndexes, err := hmy.BlockChain. + ReadDelegationsByDelegatorAt(delegator, block.Number()) + if err != nil { + return nil, nil + } + + for i := range delegationIndexes { + wrapper, err := hmy.BlockChain.ReadValidatorInformationAt( + delegationIndexes[i].ValidatorAddress, block.Root(), + ) + if err != nil || wrapper == nil { + return nil, nil + } + + if uint64(len(wrapper.Delegations)) > delegationIndexes[i].Index { + delegations = append(delegations, &wrapper.Delegations[delegationIndexes[i].Index]) + } else { + delegations = append(delegations, nil) + } + addresses = append(addresses, delegationIndexes[i].ValidatorAddress) + } + return addresses, delegations +} + +// GetTotalStakingSnapshot .. +func (hmy *Harmony) GetTotalStakingSnapshot() *big.Int { + if stake := hmy.totalStakeCache.pop(hmy.CurrentBlock().NumberU64()); stake != nil { + return stake + } + currHeight := hmy.CurrentBlock().NumberU64() + candidates := hmy.BlockChain.ValidatorCandidates() + if len(candidates) == 0 { + stake := big.NewInt(0) + hmy.totalStakeCache.push(currHeight, stake) + return stake + } + stakes := big.NewInt(0) + for i := range candidates { + snapshot, _ := hmy.BlockChain.ReadValidatorSnapshot(candidates[i]) + validator, _ := hmy.BlockChain.ReadValidatorInformation(candidates[i]) + if !committee.IsEligibleForEPoSAuction( + snapshot, validator, + ) { + continue + } + for i := range validator.Delegations { + stakes.Add(stakes, validator.Delegations[i].Amount) + } + } + hmy.totalStakeCache.push(currHeight, stakes) + return stakes +} + +// GetCurrentStakingErrorSink .. +func (hmy *Harmony) GetCurrentStakingErrorSink() types.TransactionErrorReports { + return hmy.NodeAPI.ReportStakingErrorSink() +} + +// totalStakeCache .. +type totalStakeCache struct { + sync.Mutex + cachedBlockHeight uint64 + stake *big.Int + // duration is in blocks + duration uint64 +} + +// newTotalStakeCache .. +func newTotalStakeCache(duration uint64) *totalStakeCache { + return &totalStakeCache{ + cachedBlockHeight: 0, + stake: nil, + duration: duration, + } +} + +func (c *totalStakeCache) push(currBlockHeight uint64, stake *big.Int) { + c.Lock() + defer c.Unlock() + c.cachedBlockHeight = currBlockHeight + c.stake = stake +} + +func (c *totalStakeCache) pop(currBlockHeight uint64) *big.Int { + if currBlockHeight > c.cachedBlockHeight+c.duration { + return nil + } + return c.stake +} diff --git a/hmy/transaction.go b/hmy/transaction.go new file mode 100644 index 000000000..d63a1aa05 --- /dev/null +++ b/hmy/transaction.go @@ -0,0 +1,80 @@ +package hmy + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + "github.com/harmony-one/harmony/core" + "github.com/harmony-one/harmony/core/rawdb" + "github.com/harmony-one/harmony/core/types" +) + +// SendTx ... +func (hmy *Harmony) SendTx(ctx context.Context, signedTx *types.Transaction) error { + tx, _, _, _ := rawdb.ReadTransaction(hmy.chainDb, signedTx.Hash()) + if tx == nil { + return hmy.NodeAPI.AddPendingTransaction(signedTx) + } + return ErrFinalizedTransaction +} + +// ResendCx retrieve blockHash from txID and add blockHash to CxPool for resending +// Note that cross shard txn is only for regular txns, not for staking txns, so the input txn hash +// is expected to be regular txn hash +func (hmy *Harmony) ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) { + blockHash, blockNum, index := hmy.BlockChain.ReadTxLookupEntry(txID) + if blockHash == (common.Hash{}) { + return 0, false + } + + blk := hmy.BlockChain.GetBlockByHash(blockHash) + if blk == nil { + return 0, false + } + + txs := blk.Transactions() + // a valid index is from 0 to len-1 + if int(index) > len(txs)-1 { + return 0, false + } + tx := txs[int(index)] + + // check whether it is a valid cross shard tx + if tx.ShardID() == tx.ToShardID() || blk.Header().ShardID() != tx.ShardID() { + return 0, false + } + entry := core.CxEntry{blockHash, tx.ToShardID()} + success := hmy.CxPool.Add(entry) + return blockNum, success +} + +// GetReceipts ... +func (hmy *Harmony) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { + return hmy.BlockChain.GetReceiptsByHash(hash), nil +} + +// GetTransactionsHistory returns list of transactions hashes of address. +func (hmy *Harmony) GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) { + return hmy.NodeAPI.GetTransactionsHistory(address, txType, order) +} + +// GetAccountNonce returns the nonce value of the given address for the given block number +func (hmy *Harmony) GetAccountNonce( + ctx context.Context, address common.Address, blockNum rpc.BlockNumber) (uint64, error) { + state, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) + if state == nil || err != nil { + return 0, err + } + return state.GetNonce(address), state.Error() +} + +// GetTransactionsCount returns the number of regular transactions of address. +func (hmy *Harmony) GetTransactionsCount(address, txType string) (uint64, error) { + return hmy.NodeAPI.GetTransactionsCount(address, txType) +} + +// GetCurrentTransactionErrorSink .. +func (hmy *Harmony) GetCurrentTransactionErrorSink() types.TransactionErrorReports { + return hmy.NodeAPI.ReportPlainErrorSink() +} diff --git a/internal/hmyapi/apiv1/backend.go b/internal/hmyapi/apiv1/backend.go deleted file mode 100644 index a6a3a1db2..000000000 --- a/internal/hmyapi/apiv1/backend.go +++ /dev/null @@ -1,99 +0,0 @@ -package apiv1 - -import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" - "github.com/harmony-one/harmony/consensus/quorum" - "github.com/harmony-one/harmony/core" - "github.com/harmony-one/harmony/core/state" - "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/core/vm" - "github.com/harmony-one/harmony/crypto/bls" - commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" - "github.com/harmony-one/harmony/internal/params" - "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" - "github.com/harmony-one/harmony/staking/network" - staking "github.com/harmony-one/harmony/staking/types" -) - -// Backend interface provides the common API services (that are provided by -// both full and light clients) with access to necessary functions. -// implementations: -// * hmy/api_backend.go -type Backend interface { - NetVersion() uint64 - ProtocolVersion() int - ChainDb() ethdb.Database - SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) - SingleFlightForgetKey(key string) - EventMux() *event.TypeMux - RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber( - ctx context.Context, blockNr rpc.BlockNumber, - ) (*state.DB, *block.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - - GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) - SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription - SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription - // TxPool API - SendTx(ctx context.Context, signedTx *types.Transaction) error - // GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) - GetPoolTransactions() (types.PoolTransactions, error) - GetPoolTransaction(txHash common.Hash) types.PoolTransaction - GetPoolStats() (pendingCount, queuedCount int) - GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) - // Get account nonce - GetAccountNonce(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) - // TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - ChainConfig() *params.ChainConfig - CurrentBlock() *types.Block - // Get balance - GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) - // Get validators for a particular epoch - GetValidators(epoch *big.Int) (*shard.Committee, error) - GetShardID() uint32 - GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetTransactionsCount(address, txType string) (uint64, error) - GetStakingTransactionsCount(address, txType string) (uint64, error) - // retrieve the blockHash using txID and add blockHash to CxPool for resending - ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) - IsLeader() bool - SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error - GetElectedValidatorAddresses() []common.Address - GetAllValidatorAddresses() []common.Address - GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) - GetDelegationsByValidator(validator common.Address) []*staking.Delegation - GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) - GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) - GetValidatorSelfDelegation(addr common.Address) *big.Int - GetShardState() (*shard.State, error) - GetCurrentStakingErrorSink() types.TransactionErrorReports - GetCurrentTransactionErrorSink() types.TransactionErrorReports - GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) - GetPendingCXReceipts() []*types.CXReceiptsProof - GetCurrentUtilityMetrics() (*network.UtilityMetric, error) - GetSuperCommittees() (*quorum.Transition, error) - GetTotalStakingSnapshot() *big.Int - GetCurrentBadBlocks() []core.BadBlock - GetLastCrossLinks() ([]*types.CrossLink, error) - GetLatestChainHeaders() *block.HeaderPair - GetNodeMetadata() commonRPC.NodeMetadata - GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) - IsStakingEpoch(epoch *big.Int) bool - GetLeaderAddress(a common.Address, e *big.Int) string - GetPeerInfo() commonRPC.NodePeerInfo -} diff --git a/internal/hmyapi/apiv1/blockchain.go b/internal/hmyapi/apiv1/blockchain.go index b5c07139b..0ffb9358f 100644 --- a/internal/hmyapi/apiv1/blockchain.go +++ b/internal/hmyapi/apiv1/blockchain.go @@ -17,6 +17,7 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/hmy" internal_common "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" @@ -39,12 +40,12 @@ const ( // PublicBlockChainAPI provides an API to access the Harmony blockchain. // It offers only methods that operate on public data that is freely available to anyone. type PublicBlockChainAPI struct { - b Backend + hmy *hmy.Harmony } // NewPublicBlockChainAPI creates a new Harmony blockchain API. -func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { - return &PublicBlockChainAPI{b} +func NewPublicBlockChainAPI(hmy *hmy.Harmony) *PublicBlockChainAPI { + return &PublicBlockChainAPI{hmy} } // BlockArgs is struct to include optional block formatting params. @@ -57,7 +58,7 @@ type BlockArgs struct { } func (s *PublicBlockChainAPI) isBeaconShard() error { - if s.b.GetShardID() != shard.BeaconChainShardID { + if s.hmy.ShardID != shard.BeaconChainShardID { return ErrNotBeaconShard } return nil @@ -69,24 +70,24 @@ func (s *PublicBlockChainAPI) isBlockGreaterThanLatest(blockNum rpc.BlockNumber) // since they are never greater than latest if blockNum != rpc.PendingBlockNumber && blockNum != rpc.LatestBlockNumber && - uint64(blockNum) > s.b.CurrentBlock().NumberU64() { + uint64(blockNum) > s.hmy.CurrentBlock().NumberU64() { return ErrRequestedBlockTooHigh } return nil } -// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all +// GetBlockByNumber returns the requested block. When blockNum is -1 the chain head is returned. When fullTx is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. -func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { +func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, blockNr) - if block != nil { + blk, err := s.hmy.BlockByNumber(ctx, blockNum) + if blk != nil { blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true} - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - response, err := RPCMarshalBlock(block, blockArgs, leader) - if err == nil && blockNr == rpc.PendingBlockNumber { + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + response, err := RPCMarshalBlock(blk, blockArgs, leader) + if err == nil && blockNum == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil @@ -100,34 +101,34 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) - if block != nil { + blk, err := s.hmy.GetBlock(ctx, blockHash) + if blk != nil { blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true} - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - return RPCMarshalBlock(block, blockArgs, leader) + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + return RPCMarshalBlock(blk, blockArgs, leader) } return nil, err } -// GetBlockByNumberNew returns the requested block. When blockNr is -1 the chain head is returned. When fullTx in blockArgs is true all +// GetBlockByNumberNew returns the requested block. When blockNum is -1 the chain head is returned. When fullTx in blockArgs is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true // it shows block signers for this block in list of one addresses. -func (s *PublicBlockChainAPI) GetBlockByNumberNew(ctx context.Context, blockNr rpc.BlockNumber, blockArgs BlockArgs) (map[string]interface{}, error) { - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { +func (s *PublicBlockChainAPI) GetBlockByNumberNew(ctx context.Context, blockNum rpc.BlockNumber, blockArgs BlockArgs) (map[string]interface{}, error) { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, blockNr) + blk, err := s.hmy.BlockByNumber(ctx, blockNum) blockArgs.InclTx = true if blockArgs.WithSigners { - blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNr) + blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNum) if err != nil { return nil, err } } - if block != nil { - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - response, err := RPCMarshalBlock(block, blockArgs, leader) - if err == nil && blockNr == rpc.PendingBlockNumber { + if blk != nil { + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + response, err := RPCMarshalBlock(blk, blockArgs, leader) + if err == nil && blockNum == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil @@ -142,26 +143,26 @@ func (s *PublicBlockChainAPI) GetBlockByNumberNew(ctx context.Context, blockNr r // detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true // it shows block signers for this block in list of one addresses. func (s *PublicBlockChainAPI) GetBlockByHashNew(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + blk, err := s.hmy.GetBlock(ctx, blockHash) + if err != nil { + return nil, err + } blockArgs.InclTx = true if blockArgs.WithSigners { - blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(block.NumberU64())) + blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(blk.NumberU64())) if err != nil { return nil, err } } - if block != nil { - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - return RPCMarshalBlock(block, blockArgs, leader) - } - return nil, err + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + return RPCMarshalBlock(blk, blockArgs, leader) } // GetBlocks method returns blocks in range blockStart, blockEnd just like GetBlockByNumber but all at once. func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.BlockNumber, blockEnd rpc.BlockNumber, blockArgs BlockArgs) ([]map[string]interface{}, error) { result := make([]map[string]interface{}, 0) for i := blockStart; i <= blockEnd; i++ { - block, err := s.b.BlockByNumber(ctx, i) + blk, err := s.hmy.BlockByNumber(ctx, i) blockArgs.InclTx = true if blockArgs.WithSigners { blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(i)) @@ -169,9 +170,9 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.Bloc return nil, err } } - if block != nil { - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - rpcBlock, err := RPCMarshalBlock(block, blockArgs, leader) + if blk != nil { + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + rpcBlock, err := RPCMarshalBlock(blk, blockArgs, leader) if err == nil && i == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { @@ -186,16 +187,16 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.Bloc // GetValidators returns validators list for a particular epoch. func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) { - committee, err := s.b.GetValidators(big.NewInt(epoch)) + cmt, err := s.hmy.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err } balanceQueryBlock := shard.Schedule.EpochLastBlock(uint64(epoch)) - if balanceQueryBlock > s.b.CurrentBlock().NumberU64() { - balanceQueryBlock = s.b.CurrentBlock().NumberU64() + if balanceQueryBlock > s.hmy.CurrentBlock().NumberU64() { + balanceQueryBlock = s.hmy.CurrentBlock().NumberU64() } validators := make([]map[string]interface{}, 0) - for _, validator := range committee.Slots { + for _, validator := range cmt.Slots { oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) if err != nil { return nil, err @@ -211,7 +212,7 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m validators = append(validators, validatorsFields) } result := map[string]interface{}{ - "shardID": committee.ShardID, + "shardID": cmt.ShardID, "validators": validators, } return result, nil @@ -219,13 +220,13 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m // GetValidatorKeys returns list of bls public keys in the committee for a particular epoch. func (s *PublicBlockChainAPI) GetValidatorKeys(ctx context.Context, epoch int64) ([]string, error) { - committee, err := s.b.GetValidators(big.NewInt(epoch)) + cmt, err := s.hmy.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err } - validators := make([]string, len(committee.Slots)) - for i, v := range committee.Slots { + validators := make([]string, len(cmt.Slots)) + for i, v := range cmt.Slots { validators[i] = v.BLSPublicKey.Hex() } return validators, nil @@ -248,14 +249,14 @@ func (s *PublicBlockChainAPI) EpochLastBlock(epoch uint64) (uint64, error) { } // GetBlockSigners returns signers for a particular block. -func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) ([]string, error) { - if uint64(blockNr) == 0 { +func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNum rpc.BlockNumber) ([]string, error) { + if uint64(blockNum) == 0 { return []string{}, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) + slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum) if err != nil { return nil, err } @@ -273,14 +274,14 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.B } // GetBlockSignerKeys returns bls public keys that signed the block. -func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr rpc.BlockNumber) ([]string, error) { - if uint64(blockNr) == 0 { +func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNum rpc.BlockNumber) ([]string, error) { + if uint64(blockNum) == 0 { return []string{}, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) + slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum) if err != nil { return nil, err } @@ -293,15 +294,15 @@ func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr rp return signers, nil } -// IsBlockSigner returns true if validator with address signed blockNr block. -func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.BlockNumber, address string) (bool, error) { - if uint64(blockNr) == 0 { +// IsBlockSigner returns true if validator with address signed blockNum block. +func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNum rpc.BlockNumber, address string) (bool, error) { + if uint64(blockNum) == 0 { return false, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return false, err } - slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) + slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum) if err != nil { return false, err } @@ -352,7 +353,7 @@ func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, ad if err := s.isBeaconShard(); err != nil { return hexutil.Uint64(0), err } - return hexutil.Uint64(s.b.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64()), nil + return hexutil.Uint64(s.hmy.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64()), nil } // GetValidatorTotalDelegation returns total balace stacking for validator with delegation. @@ -360,7 +361,7 @@ func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, a if err := s.isBeaconShard(); err != nil { return hexutil.Uint64(0), err } - delegations := s.b.GetDelegationsByValidator(internal_common.ParseAddr(address)) + delegations := s.hmy.GetDelegationsByValidator(internal_common.ParseAddr(address)) totalStake := big.NewInt(0) for _, delegation := range delegations { totalStake.Add(totalStake, delegation.Amount) @@ -376,18 +377,18 @@ func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[s numShard := shard.Schedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards() // Return shareding structure for each case. - return shard.Schedule.GetShardingStructure(int(numShard), int(s.b.GetShardID())), nil + return shard.Schedule.GetShardingStructure(int(numShard), int(s.hmy.ShardID)), nil } // GetShardID returns shard ID of the requested node. func (s *PublicBlockChainAPI) GetShardID(ctx context.Context) (int, error) { - return int(s.b.GetShardID()), nil + return int(s.hmy.ShardID), nil } // GetCode returns the code stored at the given address in the state for the given block number. func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { address := internal_common.ParseAddr(addr) - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { return nil, err } @@ -400,7 +401,7 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNr // numbers are also allowed. func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key string, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { address := internal_common.ParseAddr(addr) - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNr) if state == nil || err != nil { return nil, err } @@ -410,7 +411,7 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key func (s *PublicBlockChainAPI) getBalanceByBlockNumber(ctx context.Context, address string, blockNr rpc.BlockNumber) (*hexutil.Big, error) { addr := internal_common.ParseAddr(address) - balance, err := s.b.GetBalance(ctx, addr, blockNr) + balance, err := s.hmy.GetBalance(ctx, addr, blockNr) if balance == nil { return nil, err } @@ -428,7 +429,7 @@ func (s *PublicBlockChainAPI) GetBalanceByBlockNumber(ctx context.Context, addre // GetAccountNonce returns the nonce value of the given address for the given block number func (s *PublicBlockChainAPI) GetAccountNonce(ctx context.Context, address string, blockNr rpc.BlockNumber) (uint64, error) { addr := internal_common.ParseAddr(address) - return s.b.GetAccountNonce(ctx, addr, rpc.BlockNumber(blockNr)) + return s.hmy.GetAccountNonce(ctx, addr, rpc.BlockNumber(blockNr)) } // GetBalance returns the amount of Atto for the given address in the state of the @@ -440,7 +441,7 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address string, bl // BlockNumber returns the block number of the chain head. func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + header, _ := s.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available if header == nil { return 0 } @@ -453,118 +454,21 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 { // withdrawn already from the source shard but not credited yet in the // destination account due to transient failures. func (s *PublicBlockChainAPI) ResendCx(ctx context.Context, txID common.Hash) (bool, error) { - _, success := s.b.ResendCx(ctx, txID) + _, success := s.hmy.ResendCx(ctx, txID) return success, nil } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) { - result, _, _, err := doCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second, s.b.RPCGasCap()) + result, _, _, err := doCall(ctx, s.hmy, args, blockNr, vm.Config{}, 5*time.Second, s.hmy.RPCGasCap) return (hexutil.Bytes)(result), err } -func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { - defer func(start time.Time) { - utils.Logger().Debug(). - Dur("runtime", time.Since(start)). - Msg("Executing EVM call finished") - }(time.Now()) - - state, header, err := b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, 0, false, err - } - // Set sender address or use a default if none specified - var addr common.Address - if args.From == nil { - // TODO(ricl): this logic was borrowed from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/f578d41ee6b3087f8021fd561a0b5665aea3dba6/internal/ethapi/api.go#L738) - // [question](https://ethereum.stackexchange.com/questions/72979/why-does-the-docall-function-use-the-first-account-by-default) - // Might need to reconsider the logic - // if wallets := b.AccountManager().Wallets(); len(wallets) > 0 { - // if accounts := wallets[0].Accounts(); len(accounts) > 0 { - // addr = accounts[0].Address - // } - // } - // The logic in ethereum is to pick a random address managed under the account manager. - // Currently Harmony no longers support the account manager. - // Any address does not affect the logic of this call. - addr = common.HexToAddress(defaultFromAddress) - } else { - addr = *args.From - } - // Set default gas & gas price if none were set - gas := uint64(math.MaxUint64 / 2) - if args.Gas != nil { - gas = uint64(*args.Gas) - } - if globalGasCap != nil && globalGasCap.Uint64() < gas { - utils.Logger().Warn(). - Uint64("requested", gas). - Uint64("cap", globalGasCap.Uint64()). - Msg("Caller gas above allowance, capping") - gas = globalGasCap.Uint64() - } - gasPrice := new(big.Int).SetUint64(defaultGasPrice) - if args.GasPrice != nil { - gasPrice = args.GasPrice.ToInt() - } - - value := new(big.Int) - if args.Value != nil { - value = args.Value.ToInt() - } - - var data []byte - if args.Data != nil { - data = []byte(*args.Data) - } - - // Create new call message - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) - - // Setup context so it may be cancelled the call has completed - // or, in case of unmetered gas, setup a context with a timeout. - var cancel context.CancelFunc - if timeout > 0 { - ctx, cancel = context.WithTimeout(ctx, timeout) - } else { - ctx, cancel = context.WithCancel(ctx) - } - // Make sure the context is cancelled when the call has completed - // this makes sure resources are cleaned up. - defer cancel() - - // Get a new instance of the EVM. - evm, vmError, err := b.GetEVM(ctx, msg, state, header) - if err != nil { - return nil, 0, false, err - } - // Wait for the context to be done and cancel the evm. Even if the - // EVM has finished, cancelling may be done (repeatedly) - go func() { - <-ctx.Done() - evm.Cancel() - }() - - // Setup the gas pool (also for unmetered requests) - // and apply the message. - gp := new(core.GasPool).AddGas(math.MaxUint64) - res, gas, failed, err := core.ApplyMessage(evm, msg, gp) - if err := vmError(); err != nil { - return nil, 0, false, err - } - // If the timer caused an abort, return an appropriate error message - if evm.Cancelled() { - return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout) - } - return res, gas, failed, err -} - // LatestHeader returns the latest header information func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformation { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available - leader := s.b.GetLeaderAddress(header.Coinbase(), header.Epoch()) + header, _ := s.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch()) return newHeaderInformation(header, leader) } @@ -573,11 +477,11 @@ func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, blockNum rp if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - header, err := s.b.HeaderByNumber(context.Background(), blockNum) + header, err := s.hmy.HeaderByNumber(context.Background(), blockNum) if err != nil { return nil, err } - leader := s.b.GetLeaderAddress(header.Coinbase(), header.Epoch()) + leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch()) return newHeaderInformation(header, leader), nil } @@ -587,7 +491,7 @@ func (s *PublicBlockChainAPI) GetTotalStaking() (*big.Int, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetTotalStakingSnapshot(), nil + return s.hmy.GetTotalStakingSnapshot(), nil } // GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain @@ -598,12 +502,12 @@ func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() ( if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetMedianRawStakeSnapshot() + return s.hmy.GetMedianRawStakeSnapshot() } // GetLatestChainHeaders .. func (s *PublicBlockChainAPI) GetLatestChainHeaders() *block.HeaderPair { - return s.b.GetLatestChainHeaders() + return s.hmy.GetLatestChainHeaders() } // GetAllValidatorAddresses returns all validator addresses. @@ -611,7 +515,7 @@ func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]string, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - validatorAddresses := s.b.GetAllValidatorAddresses() + validatorAddresses := s.hmy.GetAllValidatorAddresses() addresses := make([]string, len(validatorAddresses)) for i, addr := range validatorAddresses { oneAddr, _ := internal_common.AddressToBech32(addr) @@ -625,7 +529,7 @@ func (s *PublicBlockChainAPI) GetElectedValidatorAddresses() ([]string, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - electedAddresses := s.b.GetElectedValidatorAddresses() + electedAddresses := s.hmy.GetElectedValidatorAddresses() addresses := make([]string, len(electedAddresses)) for i, addr := range electedAddresses { oneAddr, _ := internal_common.AddressToBech32(addr) @@ -641,12 +545,12 @@ func (s *PublicBlockChainAPI) GetValidatorInformation( if err := s.isBeaconShard(); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the latest block information") + return nil, errors.Wrapf(err, "could not retrieve the latest blk information") } - return s.b.GetValidatorInformation( - internal_common.ParseAddr(address), block, + return s.hmy.GetValidatorInformation( + internal_common.ParseAddr(address), blk, ) } @@ -660,12 +564,12 @@ func (s *PublicBlockChainAPI) GetValidatorInformationByBlockNumber( if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr) } - return s.b.GetValidatorInformation( - internal_common.ParseAddr(address), block, + return s.hmy.GetValidatorInformation( + internal_common.ParseAddr(address), blk, ) } @@ -675,7 +579,7 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( if page < -1 { return nil, errors.Errorf("page given %d cannot be less than -1", page) } - addresses := s.b.GetAllValidatorAddresses() + addresses := s.hmy.GetAllValidatorAddresses() if page != -1 && len(addresses) <= page*validatorsPageSize { return make([]*staking.ValidatorRPCEnhanced, 0), nil } @@ -689,12 +593,12 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( } } validators := []*staking.ValidatorRPCEnhanced{} - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr) } for i := start; i < start+validatorsNum; i++ { - information, err := s.b.GetValidatorInformation(addresses[i], block) + information, err := s.hmy.GetValidatorInformation(addresses[i], blk) if err == nil { validators = append(validators, information) } @@ -710,14 +614,14 @@ func (s *PublicBlockChainAPI) GetAllValidatorInformation( if err := s.isBeaconShard(); err != nil { return nil, err } - blockNr := s.b.CurrentBlock().NumberU64() + blockNr := s.hmy.CurrentBlock().NumberU64() // delete cache for previous block prevKey := fmt.Sprintf("all-info-%d", blockNr-1) - s.b.SingleFlightForgetKey(prevKey) + s.hmy.SingleFlightForgetKey(prevKey) key := fmt.Sprintf("all-info-%d", blockNr) - res, err := s.b.SingleFlightRequest( + res, err := s.hmy.SingleFlightRequest( key, func() (interface{}, error) { return s.getAllValidatorInformation(ctx, page, rpc.LatestBlockNumber) @@ -753,7 +657,7 @@ func (s *PublicBlockChainAPI) GetAllDelegationInformation(ctx context.Context, p if page < -1 { return make([][]*RPCDelegation, 0), nil } - addresses := s.b.GetAllValidatorAddresses() + addresses := s.hmy.GetAllValidatorAddresses() if page != -1 && len(addresses) <= page*validatorsPageSize { return make([][]*RPCDelegation, 0), nil } @@ -783,7 +687,7 @@ func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, add return nil, err } delegatorAddress := internal_common.ParseAddr(address) - validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) + validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress) result := []*RPCDelegation{} for i := range delegations { delegation := delegations[i] @@ -820,11 +724,11 @@ func (s *PublicBlockChainAPI) GetDelegationsByDelegatorByBlockNumber( return nil, err } delegatorAddress := internal_common.ParseAddr(address) - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNum) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum) } - validators, delegations := s.b.GetDelegationsByDelegatorByBlock(delegatorAddress, block) + validators, delegations := s.hmy.GetDelegationsByDelegatorByBlock(delegatorAddress, blk) result := make([]*RPCDelegation, len(delegations)) for i := range delegations { delegation := delegations[i] @@ -856,7 +760,7 @@ func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, add return nil, err } validatorAddress := internal_common.ParseAddr(address) - delegations := s.b.GetDelegationsByValidator(validatorAddress) + delegations := s.hmy.GetDelegationsByValidator(validatorAddress) result := make([]*RPCDelegation, 0) for _, delegation := range delegations { @@ -888,7 +792,7 @@ func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.C } delegatorAddress := internal_common.ParseAddr(address) validatorAddress := internal_common.ParseAddr(validator) - validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) + validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress) for i := range delegations { if validators[i] != validatorAddress { continue @@ -916,67 +820,10 @@ func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.C return nil, nil } -// doEstimateGas .. -func doEstimateGas(ctx context.Context, b Backend, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) { - // Binary search the gas requirement, as it may be higher than the amount used - var ( - lo = params.TxGas - 1 - hi uint64 - cap uint64 - ) - blockNum := rpc.LatestBlockNumber - if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { - hi = uint64(*args.Gas) - } else { - // Retrieve the block to act as the gas ceiling - block, err := b.BlockByNumber(ctx, blockNum) - if err != nil { - return 0, err - } - hi = block.GasLimit() - } - if gasCap != nil && hi > gasCap.Uint64() { - // log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap) - hi = gasCap.Uint64() - } - cap = hi - - // Use zero-address if none other is available - if args.From == nil { - args.From = &common.Address{} - } - // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) bool { - args.Gas = (*hexutil.Uint64)(&gas) - - _, _, failed, err := doCall(ctx, b, args, blockNum, vm.Config{}, 0, big.NewInt(int64(cap))) - if err != nil || failed { - return false - } - return true - } - // Execute the binary search and hone in on an executable gas limit - for lo+1 < hi { - mid := (hi + lo) / 2 - if !executable(mid) { - lo = mid - } else { - hi = mid - } - } - // Reject the transaction as invalid if it still fails at the highest allowance - if hi == cap { - if !executable(hi) { - return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap) - } - } - return hexutil.Uint64(hi), nil -} - // EstimateGas returns an estimate of the amount of gas needed to execute the // given transaction against the current pending block. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) { - return doEstimateGas(ctx, s.b, args, nil) + return doEstimateGas(ctx, s.hmy, args, nil) } // GetCurrentUtilityMetrics .. @@ -984,7 +831,7 @@ func (s *PublicBlockChainAPI) GetCurrentUtilityMetrics() (*network.UtilityMetric if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetCurrentUtilityMetrics() + return s.hmy.GetCurrentUtilityMetrics() } // GetSuperCommittees .. @@ -992,12 +839,12 @@ func (s *PublicBlockChainAPI) GetSuperCommittees() (*quorum.Transition, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetSuperCommittees() + return s.hmy.GetSuperCommittees() } // GetCurrentBadBlocks .. func (s *PublicBlockChainAPI) GetCurrentBadBlocks() []core.BadBlock { - return s.b.GetCurrentBadBlocks() + return s.hmy.GetCurrentBadBlocks() } // GetTotalSupply .. @@ -1038,5 +885,149 @@ func (s *PublicBlockChainAPI) GetLastCrossLinks() ([]*types.CrossLink, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetLastCrossLinks() + return s.hmy.GetLastCrossLinks() +} + +// docall executes an EVM call +func doCall(ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { + defer func(start time.Time) { + utils.Logger().Debug(). + Dur("runtime", time.Since(start)). + Msg("Executing EVM call finished") + }(time.Now()) + + state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, 0, false, err + } + // Set sender address or use a default if none specified + var addr common.Address + if args.From == nil { + // Any address does not affect the logic of this call. + addr = common.HexToAddress(defaultFromAddress) + } else { + addr = *args.From + } + // Set default gas & gas price if none were set + gas := uint64(math.MaxUint64 / 2) + if args.Gas != nil { + gas = uint64(*args.Gas) + } + if globalGasCap != nil && globalGasCap.Uint64() < gas { + utils.Logger().Warn(). + Uint64("requested", gas). + Uint64("cap", globalGasCap.Uint64()). + Msg("Caller gas above allowance, capping") + gas = globalGasCap.Uint64() + } + gasPrice := new(big.Int).SetUint64(defaultGasPrice) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + + value := new(big.Int) + if args.Value != nil { + value = args.Value.ToInt() + } + + var data []byte + if args.Data != nil { + data = []byte(*args.Data) + } + + // Create new call message + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) + + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer cancel() + + // Get a new instance of the EVM. + evm, vmError, err := hmy.GetEVM(ctx, msg, state, header) + if err != nil { + return nil, 0, false, err + } + // Wait for the context to be done and cancel the evm. Even if the + // EVM has finished, cancelling may be done (repeatedly) + go func() { + <-ctx.Done() + evm.Cancel() + }() + + // Setup the gas pool (also for unmetered requests) + // and apply the message. + gp := new(core.GasPool).AddGas(math.MaxUint64) + res, gas, failed, err := core.ApplyMessage(evm, msg, gp) + if err := vmError(); err != nil { + return nil, 0, false, err + } + // If the timer caused an abort, return an appropriate error message + if evm.Cancelled() { + return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout) + } + return res, gas, failed, err +} + +// doEstimateGas .. +func doEstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) { + // Binary search the gas requirement, as it may be higher than the amount used + var ( + lo = params.TxGas - 1 + hi uint64 + max uint64 + ) + blockNum := rpc.LatestBlockNumber + if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { + hi = uint64(*args.Gas) + } else { + // Retrieve the blk to act as the gas ceiling + blk, err := hmy.BlockByNumber(ctx, blockNum) + if err != nil { + return 0, err + } + hi = blk.GasLimit() + } + if gasCap != nil && hi > gasCap.Uint64() { + hi = gasCap.Uint64() + } + max = hi + + // Use zero-address if none other is available + if args.From == nil { + args.From = &common.Address{} + } + // Create a helper to check if a gas allowance results in an executable transaction + executable := func(gas uint64) bool { + args.Gas = (*hexutil.Uint64)(&gas) + + _, _, failed, err := doCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max))) + if err != nil || failed { + return false + } + return true + } + // Execute the binary search and hone in on an executable gas limit + for lo+1 < hi { + mid := (hi + lo) / 2 + if !executable(mid) { + lo = mid + } else { + hi = mid + } + } + // Reject the transaction as invalid if it still fails at the highest allowance + if hi == max { + if !executable(hi) { + return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", max) + } + } + return hexutil.Uint64(hi), nil } diff --git a/internal/hmyapi/apiv1/debug.go b/internal/hmyapi/apiv1/debug.go index 608e587a4..c08ef06f7 100644 --- a/internal/hmyapi/apiv1/debug.go +++ b/internal/hmyapi/apiv1/debug.go @@ -4,17 +4,18 @@ import ( "context" "github.com/ethereum/go-ethereum/log" + "github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/internal/utils" ) // DebugAPI Internal JSON RPC for debugging purpose type DebugAPI struct { - b Backend + hmy *hmy.Harmony } // NewDebugAPI Creates a new DebugAPI instance -func NewDebugAPI(b Backend) *DebugAPI { - return &DebugAPI{b} +func NewDebugAPI(hmy *hmy.Harmony) *DebugAPI { + return &DebugAPI{hmy} } // SetLogVerbosity Sets log verbosity on runtime diff --git a/internal/hmyapi/apiv1/harmony.go b/internal/hmyapi/apiv1/harmony.go index 39740f31d..a51a2ef95 100644 --- a/internal/hmyapi/apiv1/harmony.go +++ b/internal/hmyapi/apiv1/harmony.go @@ -5,24 +5,24 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/harmony-one/harmony/api/proto" + "github.com/harmony-one/harmony/hmy" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" ) // PublicHarmonyAPI provides an API to access Harmony related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicHarmonyAPI struct { - b Backend + hmy *hmy.Harmony } // NewPublicHarmonyAPI ... -func NewPublicHarmonyAPI(b Backend) *PublicHarmonyAPI { - return &PublicHarmonyAPI{b} +func NewPublicHarmonyAPI(hmy *hmy.Harmony) *PublicHarmonyAPI { + return &PublicHarmonyAPI{hmy} } // ProtocolVersion returns the current Harmony protocol version this node supports func (s *PublicHarmonyAPI) ProtocolVersion() hexutil.Uint { - return hexutil.Uint(proto.ProtocolVersion) + return hexutil.Uint(s.hmy.ProtocolVersion()) } // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not @@ -33,7 +33,7 @@ func (s *PublicHarmonyAPI) ProtocolVersion() hexutil.Uint { // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled func (s *PublicHarmonyAPI) Syncing() (interface{}, error) { - // TODO(ricl): find our Downloader module for syncing blocks + // TODO(dm): find our Downloader module for syncing blocks return false, nil } @@ -45,10 +45,10 @@ func (s *PublicHarmonyAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) { // GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node func (s *PublicHarmonyAPI) GetNodeMetadata() commonRPC.NodeMetadata { - return s.b.GetNodeMetadata() + return s.hmy.GetNodeMetadata() } // GetPeerInfo produces a NodePeerInfo record func (s *PublicHarmonyAPI) GetPeerInfo() commonRPC.NodePeerInfo { - return s.b.GetPeerInfo() + return s.hmy.GetPeerInfo() } diff --git a/internal/hmyapi/apiv1/net.go b/internal/hmyapi/apiv1/net.go index afa47f437..c50098ef1 100644 --- a/internal/hmyapi/apiv1/net.go +++ b/internal/hmyapi/apiv1/net.go @@ -9,13 +9,13 @@ import ( // PublicNetAPI offers network related RPC methods type PublicNetAPI struct { - net p2p.Host - networkVersion uint64 + net p2p.Host + chainID uint64 } // NewPublicNetAPI creates a new net API instance. -func NewPublicNetAPI(net p2p.Host, networkVersion uint64) *PublicNetAPI { - return &PublicNetAPI{net, networkVersion} +func NewPublicNetAPI(net p2p.Host, chainID uint64) *PublicNetAPI { + return &PublicNetAPI{net, chainID} } // PeerCount returns the number of connected peers @@ -23,7 +23,7 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint { return hexutil.Uint(s.net.GetPeerCount()) } -// Version returns the network version, i.e. network ID identifying which network we are using +// Version returns the network version, i.e. ChainID identifying which network we are using func (s *PublicNetAPI) Version() string { - return fmt.Sprintf("%d", s.networkVersion) // TODO(ricl): we should add support for network id (https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version) + return fmt.Sprintf("%d", s.chainID) } diff --git a/internal/hmyapi/apiv1/sendtxargs.go b/internal/hmyapi/apiv1/sendtxargs.go deleted file mode 100644 index ee822faa8..000000000 --- a/internal/hmyapi/apiv1/sendtxargs.go +++ /dev/null @@ -1,21 +0,0 @@ -package apiv1 - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. -type SendTxArgs struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - ShardID uint32 `json:"shardID"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Value *hexutil.Big `json:"value"` - Nonce *hexutil.Uint64 `json:"nonce"` - // We accept "data" and "input" for backwards-compatibility reasons. "input" is the - // newer name and should be preferred by clients. - Data *hexutil.Bytes `json:"data"` - Input *hexutil.Bytes `json:"input"` -} diff --git a/internal/hmyapi/apiv1/transactionpool.go b/internal/hmyapi/apiv1/transactionpool.go index 0f70cf427..9e5b6d6fb 100644 --- a/internal/hmyapi/apiv1/transactionpool.go +++ b/internal/hmyapi/apiv1/transactionpool.go @@ -11,6 +11,7 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/hmy" internal_common "github.com/harmony-one/harmony/internal/common" staking "github.com/harmony-one/harmony/staking/types" "github.com/pkg/errors" @@ -28,13 +29,13 @@ type TxHistoryArgs struct { // PublicTransactionPoolAPI exposes methods for the RPC interface type PublicTransactionPoolAPI struct { - b Backend + hmy *hmy.Harmony nonceLock *AddrLocker } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { - return &PublicTransactionPoolAPI{b, nonceLock} +func NewPublicTransactionPoolAPI(hmy *hmy.Harmony, nonceLock *AddrLocker) *PublicTransactionPoolAPI { + return &PublicTransactionPoolAPI{hmy, nonceLock} } // GetTransactionsHistory returns the list of transactions hashes that involve a particular address. @@ -51,7 +52,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, a return nil, err } } - hashes, err := s.b.GetTransactionsHistory(address, args.TxType, args.Order) + hashes, err := s.hmy.GetTransactionsHistory(address, args.TxType, args.Order) if err != nil { return nil, err } @@ -68,8 +69,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, a } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. -func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNum rpc.BlockNumber) *hexutil.Uint { + if block, _ := s.hmy.BlockByNumber(ctx, blockNum); block != nil { n := hexutil.Uint(len(block.Transactions())) return &n } @@ -78,7 +79,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context. // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { n := hexutil.Uint(len(block.Transactions())) return &n } @@ -86,8 +87,8 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNum rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { + if block, _ := s.hmy.BlockByNumber(ctx, blockNum); block != nil { return newRPCTransactionFromBlockIndex(block, uint64(index)) } return nil @@ -95,7 +96,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx conte // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return newRPCTransactionFromBlockIndex(block, uint64(index)) } return nil @@ -104,8 +105,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context // GetTransactionByHash returns the plain transaction for the given hash func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { // Try to return an already finalized transaction - tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) - block, _ := s.b.GetBlock(ctx, blockHash) + tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.hmy.ChainDb(), hash) + block, _ := s.hmy.GetBlock(ctx, blockHash) if block == nil { return nil } @@ -119,8 +120,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has // GetStakingTransactionByHash returns the staking transaction for the given hash func (s *PublicTransactionPoolAPI) GetStakingTransactionByHash(ctx context.Context, hash common.Hash) *RPCStakingTransaction { // Try to return an already finalized transaction - stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) - block, _ := s.b.GetBlock(ctx, blockHash) + stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash) + block, _ := s.hmy.GetBlock(ctx, blockHash) if block == nil { return nil } @@ -132,8 +133,8 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionByHash(ctx context.Conte } // GetStakingTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCStakingTransaction { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ctx context.Context, blockNum rpc.BlockNumber, index hexutil.Uint) *RPCStakingTransaction { + if block, _ := s.hmy.BlockByNumber(ctx, blockNum); block != nil { return newRPCStakingTransactionFromBlockIndex(block, uint64(index)) } return nil @@ -141,7 +142,7 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ct // GetStakingTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCStakingTransaction { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return newRPCStakingTransactionFromBlockIndex(block, uint64(index)) } return nil @@ -150,18 +151,18 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockHashAndIndex(ctx // GetTransactionCount returns the number of transactions the given address has sent for the given block number. // Legacy for apiv1. For apiv2, please use getAccountNonce/getPoolNonce/getTransactionsCount/getStakingTransactionsCount apis for // more granular transaction counts queries -func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr string, blockNr rpc.BlockNumber) (*hexutil.Uint64, error) { +func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr string, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) { address := internal_common.ParseAddr(addr) // Ask transaction pool for the nonce which includes pending transactions - if blockNr == rpc.PendingBlockNumber { - nonce, err := s.b.GetPoolNonce(ctx, address) + if blockNum == rpc.PendingBlockNumber { + nonce, err := s.hmy.GetPoolNonce(ctx, address) if err != nil { return nil, err } return (*hexutil.Uint64)(&nonce), nil } // Resolve block number and use its state to ask for the nonce - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) + state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum) if state == nil || err != nil { return nil, err } @@ -179,7 +180,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionsCount(ctx context.Context, add return 0, err } } - return s.b.GetTransactionsCount(address, txType) + return s.hmy.GetTransactionsCount(address, txType) } // GetStakingTransactionsCount returns the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") @@ -192,7 +193,7 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionsCount(ctx context.Conte return 0, err } } - return s.b.GetStakingTransactionsCount(address, txType) + return s.hmy.GetStakingTransactionsCount(address, txType) } // SendRawStakingTransaction will add the signed transaction to the transaction pool. @@ -208,13 +209,13 @@ func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err } - c := s.b.ChainConfig().ChainID + c := s.hmy.ChainConfig().ChainID if id := tx.ChainID(); id.Cmp(c) != 0 { return common.Hash{}, errors.Wrapf( ErrInvalidChainID, "blockchain chain id:%s, given %s", c.String(), id.String(), ) } - return SubmitStakingTransaction(ctx, s.b, tx) + return SubmitStakingTransaction(ctx, s.hmy, tx) } // SendRawTransaction will add the signed transaction to the transaction pool. @@ -230,13 +231,13 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction( if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err } - c := s.b.ChainConfig().ChainID + c := s.hmy.ChainConfig().ChainID if id := tx.ChainID(); id.Cmp(c) != 0 { return common.Hash{}, errors.Wrapf( ErrInvalidChainID, "blockchain chain id:%s, given %s", c.String(), id.String(), ) } - return SubmitTransaction(ctx, s.b, tx) + return SubmitTransaction(ctx, s.hmy, tx) } func (s *PublicTransactionPoolAPI) fillTransactionFields(tx *types.Transaction, fields map[string]interface{}) error { @@ -281,14 +282,14 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha var stx *staking.StakingTransaction var blockHash common.Hash var blockNumber, index uint64 - tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.b.ChainDb(), hash) + tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.hmy.ChainDb(), hash) if tx == nil { - stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) + stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash) if stx == nil { return nil, nil } } - receipts, err := s.b.GetReceipts(ctx, blockHash) + receipts, err := s.hmy.GetReceipts(ctx, blockHash) if err != nil { return nil, err } @@ -334,7 +335,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha // GetPoolStats returns stats for the tx-pool func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} { - pendingCount, queuedCount := s.b.GetPoolStats() + pendingCount, queuedCount := s.hmy.GetPoolStats() return map[string]interface{}{ "executable-count": pendingCount, "non-executable-count": queuedCount, @@ -343,7 +344,7 @@ func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} { // PendingTransactions returns the plain transactions that are in the transaction pool func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) { - pending, err := s.b.GetPoolTransactions() + pending, err := s.hmy.GetPoolTransactions() if err != nil { return nil, err } @@ -364,7 +365,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err // PendingStakingTransactions returns the staking transactions that are in the transaction pool func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTransaction, error) { - pending, err := s.b.GetPoolTransactions() + pending, err := s.hmy.GetPoolTransactions() if err != nil { return nil, err } @@ -385,19 +386,19 @@ func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTr // GetCurrentTransactionErrorSink .. func (s *PublicTransactionPoolAPI) GetCurrentTransactionErrorSink() types.TransactionErrorReports { - return s.b.GetCurrentTransactionErrorSink() + return s.hmy.GetCurrentTransactionErrorSink() } // GetCurrentStakingErrorSink .. func (s *PublicTransactionPoolAPI) GetCurrentStakingErrorSink() types.TransactionErrorReports { - return s.b.GetCurrentStakingErrorSink() + return s.hmy.GetCurrentStakingErrorSink() } // GetCXReceiptByHash returns the transaction for the given hash func (s *PublicTransactionPoolAPI) GetCXReceiptByHash( ctx context.Context, hash common.Hash, ) *RPCCXReceipt { - if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.b.ChainDb(), hash); cx != nil { + if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.hmy.ChainDb(), hash); cx != nil { return newRPCCXReceipt(cx, blockHash, blockNumber) } return nil @@ -405,5 +406,5 @@ func (s *PublicTransactionPoolAPI) GetCXReceiptByHash( // GetPendingCXReceipts .. func (s *PublicTransactionPoolAPI) GetPendingCXReceipts(ctx context.Context) []*types.CXReceiptsProof { - return s.b.GetPendingCXReceipts() + return s.hmy.GetPendingCXReceipts() } diff --git a/internal/hmyapi/apiv1/util.go b/internal/hmyapi/apiv1/util.go index d04c8a2c8..4e545f6e6 100644 --- a/internal/hmyapi/apiv1/util.go +++ b/internal/hmyapi/apiv1/util.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/hmy" common2 "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/utils" staking "github.com/harmony-one/harmony/staking/types" @@ -33,14 +34,14 @@ func ReturnWithPagination(hashes []common.Hash, pageIndex uint32, pageSize uint3 // SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction( - ctx context.Context, b Backend, tx *types.Transaction, + ctx context.Context, hmy *hmy.Harmony, tx *types.Transaction, ) (common.Hash, error) { - if err := b.SendTx(ctx, tx); err != nil { + if err := hmy.SendTx(ctx, tx); err != nil { utils.Logger().Warn().Err(err).Msg("Could not submit transaction") return tx.Hash(), err } if tx.To() == nil { - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Epoch()) + signer := types.MakeSigner(hmy.ChainConfig(), hmy.CurrentBlock().Epoch()) from, err := types.Sender(signer, tx) if err != nil { return common.Hash{}, err @@ -61,12 +62,11 @@ func SubmitTransaction( // SubmitStakingTransaction is a helper function that submits tx to txPool and logs a message. func SubmitStakingTransaction( - ctx context.Context, b Backend, tx *staking.StakingTransaction, + ctx context.Context, hmy *hmy.Harmony, tx *staking.StakingTransaction, ) (common.Hash, error) { - if err := b.SendStakingTx(ctx, tx); err != nil { - // legacy behavior is to never return error and always return tx hash + if err := hmy.SendStakingTx(ctx, tx); err != nil { utils.Logger().Warn().Err(err).Msg("Could not submit staking transaction") - return tx.Hash(), nil + return tx.Hash(), err } utils.Logger().Info().Str("fullhash", tx.Hash().Hex()).Msg("Submitted Staking transaction") return tx.Hash(), nil diff --git a/internal/hmyapi/apiv2/backend.go b/internal/hmyapi/apiv2/backend.go deleted file mode 100644 index d9513d4df..000000000 --- a/internal/hmyapi/apiv2/backend.go +++ /dev/null @@ -1,95 +0,0 @@ -package apiv2 - -import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" - "github.com/harmony-one/harmony/consensus/quorum" - "github.com/harmony-one/harmony/core" - "github.com/harmony-one/harmony/core/state" - "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/core/vm" - "github.com/harmony-one/harmony/crypto/bls" - commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" - "github.com/harmony-one/harmony/internal/params" - "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" - "github.com/harmony-one/harmony/staking/network" - staking "github.com/harmony-one/harmony/staking/types" -) - -// Backend interface provides the common API services (that are provided by -// both full and light clients) with access to necessary functions. -// implementations: -// * hmy/api_backend.go -type Backend interface { - NetVersion() uint64 - ProtocolVersion() int - SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) - SingleFlightForgetKey(key string) - ChainDb() ethdb.Database - EventMux() *event.TypeMux - RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber( - ctx context.Context, blockNr rpc.BlockNumber, - ) (*state.DB, *block.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetEVM( - ctx context.Context, msg core.Message, - state *state.DB, header *block.Header, - ) (*vm.EVM, func() error, error) - SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription - SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription - SendTx(ctx context.Context, signedTx *types.Transaction) error - GetPoolTransactions() (types.PoolTransactions, error) - GetPoolTransaction(txHash common.Hash) types.PoolTransaction - GetPoolStats() (pendingCount, queuedCount int) - GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) - GetAccountNonce(ctx context.Context, addr common.Address, blockNr rpc.BlockNumber) (uint64, error) - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - ChainConfig() *params.ChainConfig - CurrentBlock() *types.Block - GetBalance( - ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) - GetValidators(epoch *big.Int) (*shard.Committee, error) - GetShardID() uint32 - GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetTransactionsCount(address, txType string) (uint64, error) - GetStakingTransactionsCount(address, txType string) (uint64, error) - ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) - IsLeader() bool - SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error - GetElectedValidatorAddresses() []common.Address - GetAllValidatorAddresses() []common.Address - GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) - GetDelegationsByValidator(validator common.Address) []*staking.Delegation - GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) - GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) - GetValidatorSelfDelegation(addr common.Address) *big.Int - GetShardState() (*shard.State, error) - GetCurrentStakingErrorSink() types.TransactionErrorReports - GetCurrentTransactionErrorSink() types.TransactionErrorReports - GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) - GetPendingCXReceipts() []*types.CXReceiptsProof - GetCurrentUtilityMetrics() (*network.UtilityMetric, error) - GetSuperCommittees() (*quorum.Transition, error) - GetTotalStakingSnapshot() *big.Int - GetCurrentBadBlocks() []core.BadBlock - GetLastCrossLinks() ([]*types.CrossLink, error) - GetLatestChainHeaders() *block.HeaderPair - GetNodeMetadata() commonRPC.NodeMetadata - GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) - IsStakingEpoch(epoch *big.Int) bool - GetLeaderAddress(a common.Address, e *big.Int) string - GetPeerInfo() commonRPC.NodePeerInfo -} diff --git a/internal/hmyapi/apiv2/blockchain.go b/internal/hmyapi/apiv2/blockchain.go index 80f044eb1..e579a9ad3 100644 --- a/internal/hmyapi/apiv2/blockchain.go +++ b/internal/hmyapi/apiv2/blockchain.go @@ -17,6 +17,7 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/hmy" internal_common "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" @@ -39,12 +40,12 @@ const ( // PublicBlockChainAPI provides an API to access the Harmony blockchain. // It offers only methods that operate on public data that is freely available to anyone. type PublicBlockChainAPI struct { - b Backend + hmy *hmy.Harmony } // NewPublicBlockChainAPI creates a new Harmony blockchain API. -func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { - return &PublicBlockChainAPI{b} +func NewPublicBlockChainAPI(hmy *hmy.Harmony) *PublicBlockChainAPI { + return &PublicBlockChainAPI{hmy} } // BlockArgs is struct to include optional block formatting params. @@ -57,14 +58,14 @@ type BlockArgs struct { } func (s *PublicBlockChainAPI) isBeaconShard() error { - if s.b.GetShardID() != shard.BeaconChainShardID { + if s.hmy.ShardID != shard.BeaconChainShardID { return ErrNotBeaconShard } return nil } func (s *PublicBlockChainAPI) isBlockGreaterThanLatest(blockNum uint64) error { - if blockNum > s.b.CurrentBlock().NumberU64() { + if blockNum > s.hmy.CurrentBlock().NumberU64() { return ErrRequestedBlockTooHigh } return nil @@ -72,22 +73,22 @@ func (s *PublicBlockChainAPI) isBlockGreaterThanLatest(blockNum uint64) error { // GetBlockByNumber returns the requested block. When fullTx in blockArgs is true all transactions in the block are returned in full detail, // otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true it shows block signers for this block in list of one addresses. -func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr uint64, blockArgs BlockArgs) (map[string]interface{}, error) { - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { +func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNum uint64, blockArgs BlockArgs) (map[string]interface{}, error) { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) blockArgs.InclTx = true - if block != nil { + if blk != nil { if blockArgs.WithSigners { - blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNr) + blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNum) if err != nil { return nil, err } } - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - response, err := RPCMarshalBlock(block, blockArgs, leader) - if err == nil && rpc.BlockNumber(blockNr) == rpc.PendingBlockNumber { + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + response, err := RPCMarshalBlock(blk, blockArgs, leader) + if err == nil && rpc.BlockNumber(blockNum) == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { response[field] = nil @@ -102,17 +103,17 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr uint // detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true // it shows block signers for this block in list of one addresses. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + blk, err := s.hmy.GetBlock(ctx, blockHash) blockArgs.InclTx = true - if block != nil { + if blk != nil { if blockArgs.WithSigners { - blockArgs.Signers, err = s.GetBlockSigners(ctx, block.NumberU64()) + blockArgs.Signers, err = s.GetBlockSigners(ctx, blk.NumberU64()) if err != nil { return nil, err } } - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - return RPCMarshalBlock(block, blockArgs, leader) + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + return RPCMarshalBlock(blk, blockArgs, leader) } return nil, err } @@ -121,7 +122,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash comm func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart, blockEnd uint64, blockArgs BlockArgs) ([]map[string]interface{}, error) { result := make([]map[string]interface{}, 0) for i := blockStart; i <= blockEnd; i++ { - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(i)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(i)) blockArgs.InclTx = true if blockArgs.WithSigners { blockArgs.Signers, err = s.GetBlockSigners(ctx, i) @@ -129,9 +130,9 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart, blockEn return nil, err } } - if block != nil { - leader := s.b.GetLeaderAddress(block.Header().Coinbase(), block.Header().Epoch()) - rpcBlock, err := RPCMarshalBlock(block, blockArgs, leader) + if blk != nil { + leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) + rpcBlock, err := RPCMarshalBlock(blk, blockArgs, leader) if err == nil && rpc.BlockNumber(i) == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { @@ -146,22 +147,22 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart, blockEn // GetValidators returns validators list for a particular epoch. func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) { - committee, err := s.b.GetValidators(big.NewInt(epoch)) + cmt, err := s.hmy.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err } balanceQueryBlock := shard.Schedule.EpochLastBlock(uint64(epoch)) - if balanceQueryBlock > s.b.CurrentBlock().NumberU64() { - balanceQueryBlock = s.b.CurrentBlock().NumberU64() + if balanceQueryBlock > s.hmy.CurrentBlock().NumberU64() { + balanceQueryBlock = s.hmy.CurrentBlock().NumberU64() } validators := make([]map[string]interface{}, 0) - for _, validator := range committee.Slots { + for _, validator := range cmt.Slots { oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) if err != nil { return nil, err } addr := internal_common.ParseAddr(oneAddress) - validatorBalance, err := s.b.GetBalance(ctx, addr, rpc.BlockNumber(balanceQueryBlock)) + validatorBalance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumber(balanceQueryBlock)) if err != nil { return nil, err } @@ -172,7 +173,7 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m validators = append(validators, validatorsFields) } result := map[string]interface{}{ - "shardID": committee.ShardID, + "shardID": cmt.ShardID, "validators": validators, } return result, nil @@ -180,13 +181,13 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m // GetValidatorKeys returns list of bls public keys in the committee for a particular epoch. func (s *PublicBlockChainAPI) GetValidatorKeys(ctx context.Context, epoch int64) ([]string, error) { - committee, err := s.b.GetValidators(big.NewInt(epoch)) + cmt, err := s.hmy.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err } - validators := make([]string, len(committee.Slots)) - for i, v := range committee.Slots { + validators := make([]string, len(cmt.Slots)) + for i, v := range cmt.Slots { validators[i] = v.BLSPublicKey.Hex() } return validators, nil @@ -209,14 +210,14 @@ func (s *PublicBlockChainAPI) EpochLastBlock(epoch uint64) (uint64, error) { } // GetBlockSigners returns signers for a particular block. -func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr uint64) ([]string, error) { - if blockNr == 0 || blockNr >= uint64(s.BlockNumber()) { +func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNum uint64) ([]string, error) { + if blockNum == 0 || blockNum >= uint64(s.BlockNumber()) { return []string{}, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) + slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum)) if err != nil { return nil, err } @@ -234,14 +235,14 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr uint6 } // GetBlockSignerKeys returns bls public keys that signed the block. -func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr uint64) ([]string, error) { - if blockNr == 0 || blockNr >= uint64(s.BlockNumber()) { +func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNum uint64) ([]string, error) { + if blockNum == 0 || blockNum >= uint64(s.BlockNumber()) { return []string{}, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) + slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum)) if err != nil { return nil, err } @@ -254,15 +255,15 @@ func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr ui return signers, nil } -// IsBlockSigner returns true if validator with address signed blockNr block. -func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr uint64, address string) (bool, error) { - if blockNr == 0 { +// IsBlockSigner returns true if validator with address signed blockNum block. +func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNum uint64, address string) (bool, error) { + if blockNum == 0 { return false, nil } - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return false, err } - slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) + slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum)) if err != nil { return false, err } @@ -313,7 +314,7 @@ func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, ad if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetValidatorSelfDelegation(internal_common.ParseAddr(address)), nil + return s.hmy.GetValidatorSelfDelegation(internal_common.ParseAddr(address)), nil } // GetValidatorTotalDelegation returns total balace stacking for validator with delegation. @@ -321,7 +322,7 @@ func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, a if err := s.isBeaconShard(); err != nil { return nil, err } - delegations := s.b.GetDelegationsByValidator(internal_common.ParseAddr(address)) + delegations := s.hmy.GetDelegationsByValidator(internal_common.ParseAddr(address)) totalStake := big.NewInt(0) for _, delegation := range delegations { totalStake.Add(totalStake, delegation.Amount) @@ -336,18 +337,18 @@ func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[s numShard := shard.Schedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards() // Return shareding structure for each case. - return shard.Schedule.GetShardingStructure(int(numShard), int(s.b.GetShardID())), nil + return shard.Schedule.GetShardingStructure(int(numShard), int(s.hmy.ShardID)), nil } // GetShardID returns shard ID of the requested node. func (s *PublicBlockChainAPI) GetShardID(ctx context.Context) (int, error) { - return int(s.b.GetShardID()), nil + return int(s.hmy.ShardID), nil } // GetCode returns the code stored at the given address in the state for the given block number. -func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNr uint64) (hexutil.Bytes, error) { +func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNum uint64) (hexutil.Bytes, error) { address := internal_common.ParseAddr(addr) - state, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNr)) + state, _, err := s.hmy.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNum)) if state == nil || err != nil { return nil, err } @@ -358,9 +359,9 @@ func (s *PublicBlockChainAPI) GetCode(ctx context.Context, addr string, blockNr // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. -func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key string, blockNr uint64) (hexutil.Bytes, error) { +func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key string, blockNum uint64) (hexutil.Bytes, error) { address := internal_common.ParseAddr(addr) - state, _, err := s.b.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNr)) + state, _, err := s.hmy.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNum)) if state == nil || err != nil { return nil, err } @@ -369,18 +370,18 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, addr string, key } // GetBalanceByBlockNumber returns balance by block number. -func (s *PublicBlockChainAPI) GetBalanceByBlockNumber(ctx context.Context, address string, blockNr uint64) (*big.Int, error) { - if err := s.isBlockGreaterThanLatest(blockNr); err != nil { +func (s *PublicBlockChainAPI) GetBalanceByBlockNumber(ctx context.Context, address string, blockNum uint64) (*big.Int, error) { + if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } addr := internal_common.ParseAddr(address) - return s.b.GetBalance(ctx, addr, rpc.BlockNumber(blockNr)) + return s.hmy.GetBalance(ctx, addr, rpc.BlockNumber(blockNum)) } // GetAccountNonce returns the nonce value of the given address for the given block number func (s *PublicBlockChainAPI) GetAccountNonce(ctx context.Context, address string, blockNr rpc.BlockNumber) (uint64, error) { addr := internal_common.ParseAddr(address) - return s.b.GetAccountNonce(ctx, addr, rpc.BlockNumber(blockNr)) + return s.hmy.GetAccountNonce(ctx, addr, blockNr) } // GetBalance returns the amount of Atto for the given address in the state of the @@ -388,12 +389,12 @@ func (s *PublicBlockChainAPI) GetAccountNonce(ctx context.Context, address strin // block numbers are also allowed. func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address string) (*big.Int, error) { addr := internal_common.ParseAddr(address) - return s.b.GetBalance(ctx, addr, rpc.BlockNumber(-1)) + return s.hmy.GetBalance(ctx, addr, rpc.BlockNumber(-1)) } // BlockNumber returns the block number of the chain head. func (s *PublicBlockChainAPI) BlockNumber() uint64 { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + header, _ := s.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available return header.Number().Uint64() } @@ -403,118 +404,21 @@ func (s *PublicBlockChainAPI) BlockNumber() uint64 { // withdrawn already from the source shard but not credited yet in the // destination account due to transient failures. func (s *PublicBlockChainAPI) ResendCx(ctx context.Context, txID common.Hash) (bool, error) { - _, success := s.b.ResendCx(ctx, txID) + _, success := s.hmy.ResendCx(ctx, txID) return success, nil } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr uint64) (hexutil.Bytes, error) { - result, _, _, err := doCall(ctx, s.b, args, rpc.BlockNumber(blockNr), vm.Config{}, 5*time.Second, s.b.RPCGasCap()) + result, _, _, err := doCall(ctx, s.hmy, args, rpc.BlockNumber(blockNr), vm.Config{}, 5*time.Second, s.hmy.RPCGasCap) return (hexutil.Bytes)(result), err } -func doCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { - defer func(start time.Time) { - utils.Logger().Debug(). - Dur("runtime", time.Since(start)). - Msg("Executing EVM call finished") - }(time.Now()) - - state, header, err := b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, 0, false, err - } - // Set sender address or use a default if none specified - var addr common.Address - if args.From == nil { - // TODO(ricl): this logic was borrowed from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/f578d41ee6b3087f8021fd561a0b5665aea3dba6/internal/ethapi/api.go#L738) - // [question](https://ethereum.stackexchange.com/questions/72979/why-does-the-docall-function-use-the-first-account-by-default) - // Might need to reconsider the logic - // if wallets := b.AccountManager().Wallets(); len(wallets) > 0 { - // if accounts := wallets[0].Accounts(); len(accounts) > 0 { - // addr = accounts[0].Address - // } - // } - // The logic in ethereum is to pick a random address managed under the account manager. - // Currently Harmony no longers support the account manager. - // Any address does not affect the logic of this call. - addr = common.HexToAddress(defaultFromAddress) - } else { - addr = *args.From - } - // Set default gas & gas price if none were set - gas := uint64(math.MaxUint64 / 2) - if args.Gas != nil { - gas = uint64(*args.Gas) - } - if globalGasCap != nil && globalGasCap.Uint64() < gas { - utils.Logger().Warn(). - Uint64("requested", gas). - Uint64("cap", globalGasCap.Uint64()). - Msg("Caller gas above allowance, capping") - gas = globalGasCap.Uint64() - } - gasPrice := new(big.Int).SetUint64(defaultGasPrice) - if args.GasPrice != nil { - gasPrice = args.GasPrice.ToInt() - } - - value := new(big.Int) - if args.Value != nil { - value = args.Value.ToInt() - } - - var data []byte - if args.Data != nil { - data = []byte(*args.Data) - } - - // Create new call message - msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) - - // Setup context so it may be cancelled the call has completed - // or, in case of unmetered gas, setup a context with a timeout. - var cancel context.CancelFunc - if timeout > 0 { - ctx, cancel = context.WithTimeout(ctx, timeout) - } else { - ctx, cancel = context.WithCancel(ctx) - } - // Make sure the context is cancelled when the call has completed - // this makes sure resources are cleaned up. - defer cancel() - - // Get a new instance of the EVM. - evm, vmError, err := b.GetEVM(ctx, msg, state, header) - if err != nil { - return nil, 0, false, err - } - // Wait for the context to be done and cancel the evm. Even if the - // EVM has finished, cancelling may be done (repeatedly) - go func() { - <-ctx.Done() - evm.Cancel() - }() - - // Setup the gas pool (also for unmetered requests) - // and apply the message. - gp := new(core.GasPool).AddGas(math.MaxUint64) - res, gas, failed, err := core.ApplyMessage(evm, msg, gp) - if err := vmError(); err != nil { - return nil, 0, false, err - } - // If the timer caused an abort, return an appropriate error message - if evm.Cancelled() { - return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout) - } - return res, gas, failed, err -} - // LatestHeader returns the latest header information func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformation { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available - leader := s.b.GetLeaderAddress(header.Coinbase(), header.Epoch()) + header, _ := s.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch()) return newHeaderInformation(header, leader) } @@ -523,11 +427,11 @@ func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, blockNum ui if err := s.isBlockGreaterThanLatest(blockNum); err != nil { return nil, err } - header, err := s.b.HeaderByNumber(context.Background(), rpc.BlockNumber(blockNum)) + header, err := s.hmy.HeaderByNumber(context.Background(), rpc.BlockNumber(blockNum)) if err != nil { return nil, err } - leader := s.b.GetLeaderAddress(header.Coinbase(), header.Epoch()) + leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch()) return newHeaderInformation(header, leader), nil } @@ -537,7 +441,7 @@ func (s *PublicBlockChainAPI) GetTotalStaking() (*big.Int, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetTotalStakingSnapshot(), nil + return s.hmy.GetTotalStakingSnapshot(), nil } // GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain @@ -548,7 +452,7 @@ func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() ( if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetMedianRawStakeSnapshot() + return s.hmy.GetMedianRawStakeSnapshot() } // GetAllValidatorAddresses returns all validator addresses. @@ -556,7 +460,7 @@ func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]string, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - validatorAddresses := s.b.GetAllValidatorAddresses() + validatorAddresses := s.hmy.GetAllValidatorAddresses() addresses := make([]string, len(validatorAddresses)) for i, addr := range validatorAddresses { oneAddr, _ := internal_common.AddressToBech32(addr) @@ -570,7 +474,7 @@ func (s *PublicBlockChainAPI) GetElectedValidatorAddresses() ([]string, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - electedAddresses := s.b.GetElectedValidatorAddresses() + electedAddresses := s.hmy.GetElectedValidatorAddresses() addresses := make([]string, len(electedAddresses)) for i, addr := range electedAddresses { oneAddr, _ := internal_common.AddressToBech32(addr) @@ -586,12 +490,12 @@ func (s *PublicBlockChainAPI) GetValidatorInformation( if err := s.isBeaconShard(); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the latest block information") + return nil, errors.Wrapf(err, "could not retrieve the latest blk information") } - return s.b.GetValidatorInformation( - internal_common.ParseAddr(address), block, + return s.hmy.GetValidatorInformation( + internal_common.ParseAddr(address), blk, ) } @@ -605,12 +509,12 @@ func (s *PublicBlockChainAPI) GetValidatorInformationByBlockNumber( if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr) } - return s.b.GetValidatorInformation( - internal_common.ParseAddr(address), block, + return s.hmy.GetValidatorInformation( + internal_common.ParseAddr(address), blk, ) } @@ -620,7 +524,7 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( if page < -1 { return nil, errors.Errorf("page given %d cannot be less than -1", page) } - addresses := s.b.GetAllValidatorAddresses() + addresses := s.hmy.GetAllValidatorAddresses() if page != -1 && len(addresses) <= page*validatorsPageSize { return make([]*staking.ValidatorRPCEnhanced, 0), nil } @@ -634,12 +538,12 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( } } validators := []*staking.ValidatorRPCEnhanced{} - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr) } for i := start; i < start+validatorsNum; i++ { - information, err := s.b.GetValidatorInformation(addresses[i], block) + information, err := s.hmy.GetValidatorInformation(addresses[i], blk) if err == nil { validators = append(validators, information) } @@ -656,14 +560,14 @@ func (s *PublicBlockChainAPI) GetAllValidatorInformation( return nil, err } - blockNr := s.b.CurrentBlock().NumberU64() + blockNr := s.hmy.CurrentBlock().NumberU64() // delete cache for previous block prevKey := fmt.Sprintf("all-info-%d", blockNr-1) - s.b.SingleFlightForgetKey(prevKey) + s.hmy.SingleFlightForgetKey(prevKey) key := fmt.Sprintf("all-info-%d", blockNr) - res, err := s.b.SingleFlightRequest( + res, err := s.hmy.SingleFlightRequest( key, func() (interface{}, error) { return s.getAllValidatorInformation(ctx, page, rpc.LatestBlockNumber) @@ -701,7 +605,7 @@ func (s *PublicBlockChainAPI) GetAllDelegationInformation(ctx context.Context, p if page < -1 { return make([][]*RPCDelegation, 0), nil } - addresses := s.b.GetAllValidatorAddresses() + addresses := s.hmy.GetAllValidatorAddresses() if page != -1 && len(addresses) <= page*validatorsPageSize { return make([][]*RPCDelegation, 0), nil } @@ -731,7 +635,7 @@ func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, add return nil, err } delegatorAddress := internal_common.ParseAddr(address) - validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) + validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress) result := []*RPCDelegation{} for i := range delegations { delegation := delegations[i] @@ -768,11 +672,11 @@ func (s *PublicBlockChainAPI) GetDelegationsByDelegatorByBlockNumber( return nil, err } delegatorAddress := internal_common.ParseAddr(address) - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) if err != nil { - return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNum) + return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum) } - validators, delegations := s.b.GetDelegationsByDelegatorByBlock(delegatorAddress, block) + validators, delegations := s.hmy.GetDelegationsByDelegatorByBlock(delegatorAddress, blk) result := make([]*RPCDelegation, len(delegations)) for i := range delegations { delegation := delegations[i] @@ -804,7 +708,7 @@ func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, add return nil, err } validatorAddress := internal_common.ParseAddr(address) - delegations := s.b.GetDelegationsByValidator(validatorAddress) + delegations := s.hmy.GetDelegationsByValidator(validatorAddress) result := make([]*RPCDelegation, 0) for _, delegation := range delegations { @@ -836,7 +740,7 @@ func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.C } delegatorAddress := internal_common.ParseAddr(address) validatorAddress := internal_common.ParseAddr(validator) - validators, delegations := s.b.GetDelegationsByDelegator(delegatorAddress) + validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress) for i := range delegations { if validators[i] != validatorAddress { continue @@ -864,67 +768,10 @@ func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.C return nil, nil } -// doEstimateGas .. -func doEstimateGas(ctx context.Context, b Backend, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) { - // Binary search the gas requirement, as it may be higher than the amount used - var ( - lo = params.TxGas - 1 - hi uint64 - cap uint64 - ) - blockNum := rpc.LatestBlockNumber - if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { - hi = uint64(*args.Gas) - } else { - // Retrieve the block to act as the gas ceiling - block, err := b.BlockByNumber(ctx, blockNum) - if err != nil { - return 0, err - } - hi = block.GasLimit() - } - if gasCap != nil && hi > gasCap.Uint64() { - // log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap) - hi = gasCap.Uint64() - } - cap = hi - - // Use zero-address if none other is available - if args.From == nil { - args.From = &common.Address{} - } - // Create a helper to check if a gas allowance results in an executable transaction - executable := func(gas uint64) bool { - args.Gas = (*hexutil.Uint64)(&gas) - - _, _, failed, err := doCall(ctx, b, args, blockNum, vm.Config{}, 0, big.NewInt(int64(cap))) - if err != nil || failed { - return false - } - return true - } - // Execute the binary search and hone in on an executable gas limit - for lo+1 < hi { - mid := (hi + lo) / 2 - if !executable(mid) { - lo = mid - } else { - hi = mid - } - } - // Reject the transaction as invalid if it still fails at the highest allowance - if hi == cap { - if !executable(hi) { - return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", cap) - } - } - return hexutil.Uint64(hi), nil -} - // EstimateGas returns an estimate of the amount of gas needed to execute the // given transaction against the current pending block. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) { - return doEstimateGas(ctx, s.b, args, nil) + return doEstimateGas(ctx, s.hmy, args, nil) } // GetCurrentUtilityMetrics .. @@ -932,7 +779,7 @@ func (s *PublicBlockChainAPI) GetCurrentUtilityMetrics() (*network.UtilityMetric if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetCurrentUtilityMetrics() + return s.hmy.GetCurrentUtilityMetrics() } // GetSuperCommittees .. @@ -940,12 +787,12 @@ func (s *PublicBlockChainAPI) GetSuperCommittees() (*quorum.Transition, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetSuperCommittees() + return s.hmy.GetSuperCommittees() } // GetCurrentBadBlocks .. func (s *PublicBlockChainAPI) GetCurrentBadBlocks() []core.BadBlock { - return s.b.GetCurrentBadBlocks() + return s.hmy.GetCurrentBadBlocks() } // GetTotalSupply .. @@ -986,10 +833,155 @@ func (s *PublicBlockChainAPI) GetLastCrossLinks() ([]*types.CrossLink, error) { if err := s.isBeaconShard(); err != nil { return nil, err } - return s.b.GetLastCrossLinks() + return s.hmy.GetLastCrossLinks() } // GetLatestChainHeaders .. func (s *PublicBlockChainAPI) GetLatestChainHeaders() *block.HeaderPair { - return s.b.GetLatestChainHeaders() + return s.hmy.GetLatestChainHeaders() +} + +// docall executes an EVM call +func doCall(ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) { + defer func(start time.Time) { + utils.Logger().Debug(). + Dur("runtime", time.Since(start)). + Msg("Executing EVM call finished") + }(time.Now()) + + state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNr) + if state == nil || err != nil { + return nil, 0, false, err + } + // Set sender address or use a default if none specified + var addr common.Address + if args.From == nil { + // Any address does not affect the logic of this call. + addr = common.HexToAddress(defaultFromAddress) + } else { + addr = *args.From + } + // Set default gas & gas price if none were set + gas := uint64(math.MaxUint64 / 2) + if args.Gas != nil { + gas = uint64(*args.Gas) + } + if globalGasCap != nil && globalGasCap.Uint64() < gas { + utils.Logger().Warn(). + Uint64("requested", gas). + Uint64("cap", globalGasCap.Uint64()). + Msg("Caller gas above allowance, capping") + gas = globalGasCap.Uint64() + } + gasPrice := new(big.Int).SetUint64(defaultGasPrice) + if args.GasPrice != nil { + gasPrice = args.GasPrice.ToInt() + } + + value := new(big.Int) + if args.Value != nil { + value = args.Value.ToInt() + } + + var data []byte + if args.Data != nil { + data = []byte(*args.Data) + } + + // Create new call message + msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false) + + // Setup context so it may be cancelled the call has completed + // or, in case of unmetered gas, setup a context with a timeout. + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(ctx, timeout) + } else { + ctx, cancel = context.WithCancel(ctx) + } + // Make sure the context is cancelled when the call has completed + // this makes sure resources are cleaned up. + defer cancel() + + // Get a new instance of the EVM. + evm, vmError, err := hmy.GetEVM(ctx, msg, state, header) + if err != nil { + return nil, 0, false, err + } + // Wait for the context to be done and cancel the evm. Even if the + // EVM has finished, cancelling may be done (repeatedly) + go func() { + <-ctx.Done() + evm.Cancel() + }() + + // Setup the gas pool (also for unmetered requests) + // and apply the message. + gp := new(core.GasPool).AddGas(math.MaxUint64) + res, gas, failed, err := core.ApplyMessage(evm, msg, gp) + if err := vmError(); err != nil { + return nil, 0, false, err + } + // If the timer caused an abort, return an appropriate error message + if evm.Cancelled() { + return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout) + } + return res, gas, failed, err +} + +// doEstimateGas .. +func doEstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) { + // Binary search the gas requirement, as it may be higher than the amount used + var ( + lo = params.TxGas - 1 + hi uint64 + max uint64 + ) + blockNum := rpc.LatestBlockNumber + if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { + hi = uint64(*args.Gas) + } else { + // Retrieve the blk to act as the gas ceiling + blk, err := hmy.BlockByNumber(ctx, blockNum) + if err != nil { + return 0, err + } + hi = blk.GasLimit() + } + if gasCap != nil && hi > gasCap.Uint64() { + // log.Warn("Caller gas above allowance, capping", "requested", hi, "max", gasCap) + hi = gasCap.Uint64() + } + max = hi + + // Use zero-address if none other is available + if args.From == nil { + args.From = &common.Address{} + } + // Create a helper to check if a gas allowance results in an executable transaction + executable := func(gas uint64) bool { + args.Gas = (*hexutil.Uint64)(&gas) + + _, _, failed, err := doCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max))) + if err != nil || failed { + return false + } + return true + } + // Execute the binary search and hone in on an executable gas limit + for lo+1 < hi { + mid := (hi + lo) / 2 + if !executable(mid) { + lo = mid + } else { + hi = mid + } + } + // Reject the transaction as invalid if it still fails at the highest allowance + if hi == max { + if !executable(hi) { + return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", max) + } + } + return hexutil.Uint64(hi), nil } diff --git a/internal/hmyapi/apiv2/debug.go b/internal/hmyapi/apiv2/debug.go index 3376b1f78..5eb37effc 100644 --- a/internal/hmyapi/apiv2/debug.go +++ b/internal/hmyapi/apiv2/debug.go @@ -4,17 +4,18 @@ import ( "context" "github.com/ethereum/go-ethereum/log" + "github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/internal/utils" ) // DebugAPI Internal JSON RPC for debugging purpose type DebugAPI struct { - b Backend + hmy *hmy.Harmony } // NewDebugAPI Creates a new DebugAPI instance -func NewDebugAPI(b Backend) *DebugAPI { - return &DebugAPI{b} +func NewDebugAPI(hmy *hmy.Harmony) *DebugAPI { + return &DebugAPI{hmy} } // SetLogVerbosity Sets log verbosity on runtime diff --git a/internal/hmyapi/apiv2/harmony.go b/internal/hmyapi/apiv2/harmony.go index 885fc9b63..276f3121a 100644 --- a/internal/hmyapi/apiv2/harmony.go +++ b/internal/hmyapi/apiv2/harmony.go @@ -4,7 +4,7 @@ import ( "context" "math/big" - "github.com/harmony-one/harmony/api/proto" + "github.com/harmony-one/harmony/hmy" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" "github.com/harmony-one/harmony/internal/params" ) @@ -12,17 +12,17 @@ import ( // PublicHarmonyAPI provides an API to access Harmony related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicHarmonyAPI struct { - b Backend + hmy *hmy.Harmony } // NewPublicHarmonyAPI ... -func NewPublicHarmonyAPI(b Backend) *PublicHarmonyAPI { - return &PublicHarmonyAPI{b} +func NewPublicHarmonyAPI(hmy *hmy.Harmony) *PublicHarmonyAPI { + return &PublicHarmonyAPI{hmy} } // ProtocolVersion returns the current Harmony protocol version this node supports func (s *PublicHarmonyAPI) ProtocolVersion() int { - return proto.ProtocolVersion + return s.hmy.ProtocolVersion() } // Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not @@ -33,7 +33,7 @@ func (s *PublicHarmonyAPI) ProtocolVersion() int { // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled func (s *PublicHarmonyAPI) Syncing() (interface{}, error) { - // TODO(ricl): find our Downloader module for syncing blocks + // TODO(dm): find our Downloader module for syncing blocks return false, nil } @@ -60,10 +60,10 @@ type NodeMetadata struct { // GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node func (s *PublicHarmonyAPI) GetNodeMetadata() commonRPC.NodeMetadata { - return s.b.GetNodeMetadata() + return s.hmy.GetNodeMetadata() } // GetPeerInfo produces a NodePeerInfo record, containing peer info of the node func (s *PublicHarmonyAPI) GetPeerInfo() commonRPC.NodePeerInfo { - return s.b.GetPeerInfo() + return s.hmy.GetPeerInfo() } diff --git a/internal/hmyapi/apiv2/net.go b/internal/hmyapi/apiv2/net.go index c9c06de09..8fc979621 100644 --- a/internal/hmyapi/apiv2/net.go +++ b/internal/hmyapi/apiv2/net.go @@ -8,13 +8,13 @@ import ( // PublicNetAPI offers network related RPC methods type PublicNetAPI struct { - net p2p.Host - networkVersion uint64 + net p2p.Host + chainID uint64 } // NewPublicNetAPI creates a new net API instance. -func NewPublicNetAPI(net p2p.Host, networkVersion uint64) *PublicNetAPI { - return &PublicNetAPI{net, networkVersion} +func NewPublicNetAPI(net p2p.Host, chainID uint64) *PublicNetAPI { + return &PublicNetAPI{net, chainID} } // PeerCount returns the number of connected peers @@ -22,7 +22,7 @@ func (s *PublicNetAPI) PeerCount() int { return s.net.GetPeerCount() } -// Version returns the network version, i.e. network ID identifying which network we are using +// Version returns the network version, i.e. ChainID identifying which network we are using func (s *PublicNetAPI) Version() string { - return fmt.Sprintf("%d", s.networkVersion) // TODO(ricl): we should add support for network id (https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version) + return fmt.Sprintf("%d", s.chainID) } diff --git a/internal/hmyapi/apiv2/sendtxargs.go b/internal/hmyapi/apiv2/sendtxargs.go deleted file mode 100644 index d2cd57641..000000000 --- a/internal/hmyapi/apiv2/sendtxargs.go +++ /dev/null @@ -1,83 +0,0 @@ -package apiv2 - -import ( - "bytes" - "context" - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/harmony-one/harmony/core/types" -) - -// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. -type SendTxArgs struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - ShardID uint32 `json:"shardID"` - Gas *hexutil.Uint64 `json:"gas"` - GasPrice *hexutil.Big `json:"gasPrice"` - Value *hexutil.Big `json:"value"` - Nonce *hexutil.Uint64 `json:"nonce"` - // We accept "data" and "input" for backwards-compatibility reasons. "input" is the - // newer name and should be preferred by clients. - Data *hexutil.Bytes `json:"data"` - Input *hexutil.Bytes `json:"input"` -} - -// setDefaults is a helper function that fills in default values for unspecified tx fields. -func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { - if args.Gas == nil { - args.Gas = new(hexutil.Uint64) - *(*uint64)(args.Gas) = 90000 - } - // TODO(ricl): add check for shardID - // if args.GasPrice == nil { - // TODO(ricl): port - // price, err := b.SuggestPrice(ctx) - // if err != nil { - // return err - // } - // args.GasPrice = (*hexutil.Big)(price) - // } - if args.Value == nil { - args.Value = new(hexutil.Big) - } - if args.Nonce == nil { - nonce, err := b.GetPoolNonce(ctx, args.From) - if err != nil { - return err - } - args.Nonce = (*hexutil.Uint64)(&nonce) - } - if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { - return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) - } - if args.To == nil { - // Contract creation - var input []byte - if args.Data != nil { - input = *args.Data - } else if args.Input != nil { - input = *args.Input - } - if len(input) == 0 { - return errors.New(`contract creation without any data provided`) - } - } - return nil -} - -func (args *SendTxArgs) toTransaction() *types.Transaction { - var input []byte - if args.Data != nil { - input = *args.Data - } else if args.Input != nil { - input = *args.Input - } - if args.To == nil { - return types.NewContractCreation(uint64(*args.Nonce), args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) - } - return types.NewTransaction(uint64(*args.Nonce), *args.To, args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) -} diff --git a/internal/hmyapi/apiv2/transactionpool.go b/internal/hmyapi/apiv2/transactionpool.go index 0f3c6b71f..51347bd80 100644 --- a/internal/hmyapi/apiv2/transactionpool.go +++ b/internal/hmyapi/apiv2/transactionpool.go @@ -11,6 +11,7 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/rawdb" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/hmy" internal_common "github.com/harmony-one/harmony/internal/common" staking "github.com/harmony-one/harmony/staking/types" "github.com/pkg/errors" @@ -28,13 +29,13 @@ type TxHistoryArgs struct { // PublicTransactionPoolAPI exposes methods for the RPC interface type PublicTransactionPoolAPI struct { - b Backend + hmy *hmy.Harmony nonceLock *AddrLocker } // NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransactionPoolAPI { - return &PublicTransactionPoolAPI{b, nonceLock} +func NewPublicTransactionPoolAPI(hmy *hmy.Harmony, nonceLock *AddrLocker) *PublicTransactionPoolAPI { + return &PublicTransactionPoolAPI{hmy, nonceLock} } // GetTransactionsHistory returns the list of transactions hashes that involve a particular address. @@ -51,7 +52,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, a return nil, err } } - hashes, err := s.b.GetTransactionsHistory(address, args.TxType, args.Order) + hashes, err := s.hmy.GetTransactionsHistory(address, args.TxType, args.Order) if err != nil { return nil, err } @@ -70,8 +71,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, a } // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. -func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr uint64) int { - if block, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)); block != nil { +func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNum uint64) int { + if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil { return len(block.Transactions()) } return 0 @@ -79,15 +80,15 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context. // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) int { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return len(block.Transactions()) } return 0 } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr uint64, index uint64) *RPCTransaction { - if block, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNum uint64, index uint64) *RPCTransaction { + if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil { return newRPCTransactionFromBlockIndex(block, index) } return nil @@ -95,7 +96,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx conte // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index uint64) *RPCTransaction { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return newRPCTransactionFromBlockIndex(block, index) } return nil @@ -104,8 +105,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context // GetTransactionByHash returns the plain transaction for the given hash func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { // Try to return an already finalized transaction - tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) - block, _ := s.b.GetBlock(ctx, blockHash) + tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.hmy.ChainDb(), hash) + block, _ := s.hmy.GetBlock(ctx, blockHash) if block == nil { return nil } @@ -130,7 +131,7 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionsHistory(ctx context.Con return nil, err } } - hashes, err := s.b.GetStakingTransactionsHistory(address, args.TxType, args.Order) + hashes, err := s.hmy.GetStakingTransactionsHistory(address, args.TxType, args.Order) if err != nil { return nil, err } @@ -149,8 +150,8 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionsHistory(ctx context.Con } // GetBlockStakingTransactionCountByNumber returns the number of staking transactions in the block with the given block number. -func (s *PublicTransactionPoolAPI) GetBlockStakingTransactionCountByNumber(ctx context.Context, blockNr uint64) int { - if block, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)); block != nil { +func (s *PublicTransactionPoolAPI) GetBlockStakingTransactionCountByNumber(ctx context.Context, blockNum uint64) int { + if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil { return len(block.StakingTransactions()) } return 0 @@ -158,15 +159,15 @@ func (s *PublicTransactionPoolAPI) GetBlockStakingTransactionCountByNumber(ctx c // GetBlockStakingTransactionCountByHash returns the number of staking transactions in the block with the given hash. func (s *PublicTransactionPoolAPI) GetBlockStakingTransactionCountByHash(ctx context.Context, blockHash common.Hash) int { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return len(block.StakingTransactions()) } return 0 } // GetStakingTransactionByBlockNumberAndIndex returns the staking transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ctx context.Context, blockNr uint64, index uint64) *RPCStakingTransaction { - if block, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)); block != nil { +func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ctx context.Context, blockNum uint64, index uint64) *RPCStakingTransaction { + if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil { return newRPCStakingTransactionFromBlockIndex(block, index) } return nil @@ -174,7 +175,7 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockNumberAndIndex(ct // GetStakingTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index uint64) *RPCStakingTransaction { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.hmy.GetBlock(ctx, blockHash); block != nil { return newRPCStakingTransactionFromBlockIndex(block, index) } return nil @@ -183,8 +184,8 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionByBlockHashAndIndex(ctx // GetStakingTransactionByHash returns the staking transaction for the given hash func (s *PublicTransactionPoolAPI) GetStakingTransactionByHash(ctx context.Context, hash common.Hash) *RPCStakingTransaction { // Try to return an already finalized transaction - stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) - block, _ := s.b.GetBlock(ctx, blockHash) + stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash) + block, _ := s.hmy.GetBlock(ctx, blockHash) if block == nil { return nil } @@ -205,7 +206,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionsCount(ctx context.Context, add return 0, err } } - return s.b.GetTransactionsCount(address, txType) + return s.hmy.GetTransactionsCount(address, txType) } // GetStakingTransactionsCount returns the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") @@ -218,7 +219,7 @@ func (s *PublicTransactionPoolAPI) GetStakingTransactionsCount(ctx context.Conte return 0, err } } - return s.b.GetStakingTransactionsCount(address, txType) + return s.hmy.GetStakingTransactionsCount(address, txType) } // SendRawStakingTransaction will add the signed transaction to the transaction pool. @@ -234,13 +235,13 @@ func (s *PublicTransactionPoolAPI) SendRawStakingTransaction( if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err } - c := s.b.ChainConfig().ChainID + c := s.hmy.ChainConfig().ChainID if id := tx.ChainID(); id.Cmp(c) != 0 { return common.Hash{}, errors.Wrapf( ErrInvalidChainID, "blockchain chain id:%s, given %s", c.String(), id.String(), ) } - return SubmitStakingTransaction(ctx, s.b, tx) + return SubmitStakingTransaction(ctx, s.hmy, tx) } // SendRawTransaction will add the signed transaction to the transaction pool. @@ -254,13 +255,13 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod if err := rlp.DecodeBytes(encodedTx, tx); err != nil { return common.Hash{}, err } - c := s.b.ChainConfig().ChainID + c := s.hmy.ChainConfig().ChainID if id := tx.ChainID(); id.Cmp(c) != 0 { return common.Hash{}, errors.Wrapf( ErrInvalidChainID, "blockchain chain id:%s, given %s", c.String(), id.String(), ) } - return SubmitTransaction(ctx, s.b, tx) + return SubmitTransaction(ctx, s.hmy, tx) } func (s *PublicTransactionPoolAPI) fillTransactionFields(tx *types.Transaction, fields map[string]interface{}) error { @@ -305,14 +306,14 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha var stx *staking.StakingTransaction var blockHash common.Hash var blockNumber, index uint64 - tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.b.ChainDb(), hash) + tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.hmy.ChainDb(), hash) if tx == nil { - stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.b.ChainDb(), hash) + stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash) if stx == nil { return nil, nil } } - receipts, err := s.b.GetReceipts(ctx, blockHash) + receipts, err := s.hmy.GetReceipts(ctx, blockHash) if err != nil { return nil, err } @@ -358,7 +359,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, ha // GetPoolStats returns stats for the tx-pool func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} { - pendingCount, queuedCount := s.b.GetPoolStats() + pendingCount, queuedCount := s.hmy.GetPoolStats() return map[string]interface{}{ "executable-count": pendingCount, "non-executable-count": queuedCount, @@ -367,7 +368,7 @@ func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} { // PendingTransactions returns the plain transactions that are in the transaction pool func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) { - pending, err := s.b.GetPoolTransactions() + pending, err := s.hmy.GetPoolTransactions() if err != nil { return nil, err } @@ -388,7 +389,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, err // PendingStakingTransactions returns the staking transactions that are in the transaction pool func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTransaction, error) { - pending, err := s.b.GetPoolTransactions() + pending, err := s.hmy.GetPoolTransactions() if err != nil { return nil, err } @@ -409,17 +410,17 @@ func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTr // GetCurrentTransactionErrorSink .. func (s *PublicTransactionPoolAPI) GetCurrentTransactionErrorSink() types.TransactionErrorReports { - return s.b.GetCurrentTransactionErrorSink() + return s.hmy.GetCurrentTransactionErrorSink() } // GetCurrentStakingErrorSink .. func (s *PublicTransactionPoolAPI) GetCurrentStakingErrorSink() types.TransactionErrorReports { - return s.b.GetCurrentStakingErrorSink() + return s.hmy.GetCurrentStakingErrorSink() } // GetCXReceiptByHash returns the transaction for the given hash func (s *PublicTransactionPoolAPI) GetCXReceiptByHash(ctx context.Context, hash common.Hash) *RPCCXReceipt { - if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.b.ChainDb(), hash); cx != nil { + if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.hmy.ChainDb(), hash); cx != nil { return newRPCCXReceipt(cx, blockHash, blockNumber) } return nil @@ -427,5 +428,5 @@ func (s *PublicTransactionPoolAPI) GetCXReceiptByHash(ctx context.Context, hash // GetPendingCXReceipts .. func (s *PublicTransactionPoolAPI) GetPendingCXReceipts(ctx context.Context) []*types.CXReceiptsProof { - return s.b.GetPendingCXReceipts() + return s.hmy.GetPendingCXReceipts() } diff --git a/internal/hmyapi/apiv2/util.go b/internal/hmyapi/apiv2/util.go index 14c0ea8e6..60418dec7 100644 --- a/internal/hmyapi/apiv2/util.go +++ b/internal/hmyapi/apiv2/util.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/hmy" common2 "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/utils" staking "github.com/harmony-one/harmony/staking/types" @@ -33,14 +34,14 @@ func ReturnWithPagination(hashes []common.Hash, pageIndex uint32, pageSize uint3 // SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction( - ctx context.Context, b Backend, tx *types.Transaction, + ctx context.Context, hmy *hmy.Harmony, tx *types.Transaction, ) (common.Hash, error) { - if err := b.SendTx(ctx, tx); err != nil { + if err := hmy.SendTx(ctx, tx); err != nil { utils.Logger().Warn().Err(err).Msg("Could not submit transaction") return tx.Hash(), err } if tx.To() == nil { - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Epoch()) + signer := types.MakeSigner(hmy.ChainConfig(), hmy.CurrentBlock().Epoch()) from, err := types.Sender(signer, tx) if err != nil { return common.Hash{}, err @@ -61,9 +62,9 @@ func SubmitTransaction( // SubmitStakingTransaction is a helper function that submits tx to txPool and logs a message. func SubmitStakingTransaction( - ctx context.Context, b Backend, tx *staking.StakingTransaction, + ctx context.Context, hmy *hmy.Harmony, tx *staking.StakingTransaction, ) (common.Hash, error) { - if err := b.SendStakingTx(ctx, tx); err != nil { + if err := hmy.SendStakingTx(ctx, tx); err != nil { // legacy behavior is to never return error and always return tx hash utils.Logger().Warn().Err(err).Msg("Could not submit staking transaction") return tx.Hash(), nil diff --git a/internal/hmyapi/backend.go b/internal/hmyapi/backend.go index 713171ef0..ffb31ea52 100644 --- a/internal/hmyapi/backend.go +++ b/internal/hmyapi/backend.go @@ -1,145 +1,64 @@ package hmyapi import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" - "github.com/harmony-one/harmony/consensus/quorum" - "github.com/harmony-one/harmony/core" - "github.com/harmony-one/harmony/core/state" - "github.com/harmony-one/harmony/core/types" - "github.com/harmony-one/harmony/core/vm" - "github.com/harmony-one/harmony/crypto/bls" + "github.com/harmony-one/harmony/hmy" "github.com/harmony-one/harmony/internal/hmyapi/apiv1" "github.com/harmony-one/harmony/internal/hmyapi/apiv2" - commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" - "github.com/harmony-one/harmony/internal/params" - "github.com/harmony-one/harmony/shard" - "github.com/harmony-one/harmony/shard/committee" - "github.com/harmony-one/harmony/staking/network" - staking "github.com/harmony-one/harmony/staking/types" ) -// Backend .. -type Backend interface { - NetVersion() uint64 - ProtocolVersion() int - ChainDb() ethdb.Database - SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) - SingleFlightForgetKey(key string) - EventMux() *event.TypeMux - RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) - SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription - SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription - // TxPool API - SendTx(ctx context.Context, signedTx *types.Transaction) error - GetPoolTransactions() (types.PoolTransactions, error) - GetPoolTransaction(txHash common.Hash) types.PoolTransaction - GetPoolStats() (pendingCount, queuedCount int) - GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) - GetAccountNonce(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription - ChainConfig() *params.ChainConfig - CurrentBlock() *types.Block - GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) - GetValidators(epoch *big.Int) (*shard.Committee, error) - GetShardID() uint32 - GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) - GetTransactionsCount(address, txType string) (uint64, error) - GetStakingTransactionsCount(address, txType string) (uint64, error) - ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) - IsLeader() bool - SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error - GetElectedValidatorAddresses() []common.Address - GetAllValidatorAddresses() []common.Address - GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) - GetDelegationsByValidator(validator common.Address) []*staking.Delegation - GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) - GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) - GetValidatorSelfDelegation(addr common.Address) *big.Int - GetShardState() (*shard.State, error) - GetCurrentStakingErrorSink() types.TransactionErrorReports - GetCurrentTransactionErrorSink() types.TransactionErrorReports - GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) - GetPendingCXReceipts() []*types.CXReceiptsProof - GetCurrentUtilityMetrics() (*network.UtilityMetric, error) - GetSuperCommittees() (*quorum.Transition, error) - GetTotalStakingSnapshot() *big.Int - GetCurrentBadBlocks() []core.BadBlock - GetLastCrossLinks() ([]*types.CrossLink, error) - GetLatestChainHeaders() *block.HeaderPair - GetNodeMetadata() commonRPC.NodeMetadata - GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) - IsStakingEpoch(epoch *big.Int) bool - GetLeaderAddress(a common.Address, e *big.Int) string - GetPeerInfo() commonRPC.NodePeerInfo -} - // GetAPIs returns all the APIs. -func GetAPIs(b Backend) []rpc.API { +func GetAPIs(hmy *hmy.Harmony) []rpc.API { nonceLock := new(apiv1.AddrLocker) nonceLockV2 := new(apiv2.AddrLocker) return []rpc.API{ { Namespace: "hmy", Version: "1.0", - Service: apiv1.NewPublicHarmonyAPI(b), + Service: apiv1.NewPublicHarmonyAPI(hmy), Public: true, }, { Namespace: "hmy", Version: "1.0", - Service: apiv1.NewPublicBlockChainAPI(b), + Service: apiv1.NewPublicBlockChainAPI(hmy), Public: true, }, { Namespace: "hmy", Version: "1.0", - Service: apiv1.NewPublicTransactionPoolAPI(b, nonceLock), + Service: apiv1.NewPublicTransactionPoolAPI(hmy, nonceLock), Public: true, }, { Namespace: "hmy", Version: "1.0", - Service: apiv1.NewDebugAPI(b), - Public: true, // FIXME: change to false once IPC implemented + Service: apiv1.NewDebugAPI(hmy), + Public: false, }, { Namespace: "hmyv2", Version: "1.0", - Service: apiv2.NewPublicHarmonyAPI(b), + Service: apiv2.NewPublicHarmonyAPI(hmy), Public: true, }, { Namespace: "hmyv2", Version: "1.0", - Service: apiv2.NewPublicBlockChainAPI(b), + Service: apiv2.NewPublicBlockChainAPI(hmy), Public: true, }, { Namespace: "hmyv2", Version: "1.0", - Service: apiv2.NewPublicTransactionPoolAPI(b, nonceLockV2), + Service: apiv2.NewPublicTransactionPoolAPI(hmy, nonceLockV2), Public: true, }, { Namespace: "hmyv2", Version: "1.0", - Service: apiv2.NewDebugAPI(b), - Public: true, // FIXME: change to false once IPC implemented + Service: apiv2.NewDebugAPI(hmy), + Public: false, }, } } diff --git a/internal/hmyapi/filters/api.go b/internal/hmyapi/filters/api.go index af989e298..2f1cd4d59 100644 --- a/internal/hmyapi/filters/api.go +++ b/internal/hmyapi/filters/api.go @@ -6,12 +6,9 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/core/types" ) @@ -35,9 +32,7 @@ type filter struct { // information related to the Ethereum protocol such als blocks, transactions and logs. type PublicFilterAPI struct { backend Backend - mux *event.TypeMux quit chan struct{} - chainDb ethdb.Database events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter @@ -47,9 +42,7 @@ type PublicFilterAPI struct { func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { api := &PublicFilterAPI{ backend: backend, - mux: backend.EventMux(), - chainDb: backend.ChainDb(), - events: NewEventSystem(backend.EventMux(), backend, lightMode), + events: NewEventSystem(backend, lightMode), filters: make(map[rpc.ID]*filter), } go api.timeoutLoop() @@ -134,9 +127,9 @@ func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Su select { case hashes := <-txHashes: // To keep the original behaviour, send a single tx hash in one notification. - // TODO(rjl493456442) Send a batch of tx hashes in one notification + // TODO(dm) Send a batch of tx hashes in one notification for _, h := range hashes { - notifier.Notify(rpcSub.ID, h) + _ = notifier.Notify(rpcSub.ID, h) } case <-rpcSub.Err(): pendingTxSub.Unsubscribe() @@ -202,7 +195,7 @@ func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, er for { select { case h := <-headers: - notifier.Notify(rpcSub.ID, h) + _ = notifier.Notify(rpcSub.ID, h) case <-rpcSub.Err(): headersSub.Unsubscribe() return @@ -291,7 +284,7 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc select { case logs := <-matchedLogs: for _, log := range logs { - notifier.Notify(rpcSub.ID, &log) + _ = notifier.Notify(rpcSub.ID, &log) } case <-rpcSub.Err(): // client send an unsubscribe request logsSub.Unsubscribe() diff --git a/internal/hmyapi/filters/filter.go b/internal/hmyapi/filters/filter.go index e5dbf0e41..d45d78b15 100644 --- a/internal/hmyapi/filters/filter.go +++ b/internal/hmyapi/filters/filter.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" @@ -37,7 +36,7 @@ import ( type Backend interface { ChainDb() ethdb.Database EventMux() *event.TypeMux - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) + HeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*block.Header, error) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) @@ -114,7 +113,6 @@ func newFilter(backend Backend, addresses []common.Address, topics [][]common.Ha backend: backend, addresses: addresses, topics: topics, - db: backend.ChainDb(), } } diff --git a/internal/hmyapi/filters/filter_criteria.go b/internal/hmyapi/filters/filter_criteria.go index 2de7b554f..265bd674a 100644 --- a/internal/hmyapi/filters/filter_criteria.go +++ b/internal/hmyapi/filters/filter_criteria.go @@ -6,7 +6,7 @@ import ( "fmt" "math/big" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" diff --git a/internal/hmyapi/filters/filter_system.go b/internal/hmyapi/filters/filter_system.go index ab66dcd06..37c4b5057 100644 --- a/internal/hmyapi/filters/filter_system.go +++ b/internal/hmyapi/filters/filter_system.go @@ -24,12 +24,11 @@ import ( "sync" "time" - ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" - "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/rawdb" @@ -113,9 +112,9 @@ type EventSystem struct { // // The returned manager has a loop that needs to be stopped with the Stop function // or by stopping the given mux. -func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventSystem { +func NewEventSystem(backend Backend, lightMode bool) *EventSystem { m := &EventSystem{ - mux: mux, + mux: backend.EventMux(), backend: backend, lightMode: lightMode, install: make(chan *subscription), @@ -131,7 +130,7 @@ func NewEventSystem(mux *event.TypeMux, backend Backend, lightMode bool) *EventS m.logsSub = m.backend.SubscribeLogsEvent(m.logsCh) m.rmLogsSub = m.backend.SubscribeRemovedLogsEvent(m.rmLogsCh) m.chainSub = m.backend.SubscribeChainEvent(m.chainCh) - // TODO(rjl493456442): use feed to subscribe pending log event + // TODO(dm): use feed to subscribe pending log event m.pendingLogSub = m.mux.Subscribe(core.PendingLogsEvent{}) // Make sure none of the subscriptions are empty @@ -413,10 +412,10 @@ func (es *EventSystem) lightFilterLogs(header *block.Header, addresses []common. } var unfiltered []*types.Log for _, logs := range logsList { - for _, log := range logs { - logcopy := *log - logcopy.Removed = remove - unfiltered = append(unfiltered, &logcopy) + for _, lg := range logs { + logCopy := *lg + logCopy.Removed = remove + unfiltered = append(unfiltered, &logCopy) } } logs := filterLogs(unfiltered, nil, nil, addresses, topics) @@ -428,10 +427,10 @@ func (es *EventSystem) lightFilterLogs(header *block.Header, addresses []common. } unfiltered = unfiltered[:0] for _, receipt := range receipts { - for _, log := range receipt.Logs { - logcopy := *log - logcopy.Removed = remove - unfiltered = append(unfiltered, &logcopy) + for _, lg := range receipt.Logs { + logCopy := *lg + logCopy.Removed = remove + unfiltered = append(unfiltered, &logCopy) } } logs = filterLogs(unfiltered, nil, nil, addresses, topics) diff --git a/node/rpc.go b/node/rpc.go index 9b8990212..49697dbed 100644 --- a/node/rpc.go +++ b/node/rpc.go @@ -6,7 +6,6 @@ import ( "strconv" "strings" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/hmy" @@ -93,10 +92,6 @@ func (node *Node) ReportPlainErrorSink() types.TransactionErrorReports { // StartRPC start RPC service func (node *Node) StartRPC(nodePort string) error { // Gather all the possible APIs to surface - harmony, _ = hmy.New( - node, node.TxPool, node.CxPool, new(event.TypeMux), node.Consensus.ShardID, - ) - apis := node.APIs() for _, service := range node.serviceManager.GetServices() { @@ -180,26 +175,29 @@ func (node *Node) startWS( // APIs return the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (node *Node) APIs() []rpc.API { + if harmony == nil { + harmony = hmy.New(node, node.TxPool, node.CxPool, node.Consensus.ShardID) + } // Gather all the possible APIs to surface - apis := hmyapi.GetAPIs(harmony.APIBackend) + apis := hmyapi.GetAPIs(harmony) // Append all the local APIs and return return append(apis, []rpc.API{ { Namespace: "hmy", Version: "1.0", - Service: filters.NewPublicFilterAPI(harmony.APIBackend, false), + Service: filters.NewPublicFilterAPI(harmony, false), Public: true, }, { Namespace: "net", Version: "1.0", - Service: apiv1.NewPublicNetAPI(node.host, harmony.APIBackend.NetVersion()), + Service: apiv1.NewPublicNetAPI(node.host, harmony.ChainID), Public: true, }, { Namespace: "netv2", Version: "1.0", - Service: apiv2.NewPublicNetAPI(node.host, harmony.APIBackend.NetVersion()), + Service: apiv2.NewPublicNetAPI(node.host, harmony.ChainID), Public: true, }, }...)