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 <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [hmy] Refactor - Move ErrFinalizedTransaction to hmy.go

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Make PublicHarmonyAPI use Harmony instead of Backed

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Make NewPublicBlockChainAPI use Harmony instead of Backend

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Make NewPublicTransactionPoolAPI use Harmony instead of Backend

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Make DebugAPI use Harmony instead of Backend

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Make filers use Harmony instead of Backend

* Fix unhandled errors

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmy] Refactor - Change NetVersion to ChainID

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Change API service declaration to use Harmony

* Remove irrelevant TODOs

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [node] Refactor - Move harmony create to APIs fetch

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [hmy] Refactor - Prettify var names, structure & comments

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Prettify var names, structure & comments

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Remove backend interface

* Fix lint

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [hmy + hmyapi] Refactor - Fix imports

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [hmy] Refactor - Apply changes from #3243

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>

* [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 <dvandermaden0@berkeley.edu>

* [hmyapi] Refactor - Fix imports

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>
pull/3245/head
Daniel Van Der Maden 4 years ago committed by GitHub
parent a354b93676
commit d4df3aa039
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Makefile
  2. 4
      README.md
  3. 949
      hmy/api_backend.go
  4. 114
      hmy/backend.go
  5. 244
      hmy/blockchain.go
  6. 202
      hmy/hmy.go
  7. 33
      hmy/network.go
  8. 48
      hmy/pool.go
  9. 476
      hmy/staking.go
  10. 80
      hmy/transaction.go
  11. 99
      internal/hmyapi/apiv1/backend.go
  12. 509
      internal/hmyapi/apiv1/blockchain.go
  13. 7
      internal/hmyapi/apiv1/debug.go
  14. 16
      internal/hmyapi/apiv1/harmony.go
  15. 10
      internal/hmyapi/apiv1/net.go
  16. 21
      internal/hmyapi/apiv1/sendtxargs.go
  17. 75
      internal/hmyapi/apiv1/transactionpool.go
  18. 14
      internal/hmyapi/apiv1/util.go
  19. 95
      internal/hmyapi/apiv2/backend.go
  20. 492
      internal/hmyapi/apiv2/blockchain.go
  21. 7
      internal/hmyapi/apiv2/debug.go
  22. 16
      internal/hmyapi/apiv2/harmony.go
  23. 10
      internal/hmyapi/apiv2/net.go
  24. 83
      internal/hmyapi/apiv2/sendtxargs.go
  25. 75
      internal/hmyapi/apiv2/transactionpool.go
  26. 11
      internal/hmyapi/apiv2/util.go
  27. 105
      internal/hmyapi/backend.go
  28. 19
      internal/hmyapi/filters/api.go
  29. 4
      internal/hmyapi/filters/filter.go
  30. 2
      internal/hmyapi/filters/filter_criteria.go
  31. 25
      internal/hmyapi/filters/filter_system.go
  32. 16
      node/rpc.go

@ -49,6 +49,7 @@ clean:
rm -rf ./tmp_log*
rm -rf ./.dht*
rm -rf ./db-*
rm -rf ./latest
rm -f ./*.rlp
test:

@ -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

@ -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 = &notBooted
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
}

@ -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 }

@ -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)
}

@ -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
}

@ -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,
}
}

@ -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
}

@ -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 = &notBooted
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
}

@ -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()
}

@ -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
}

@ -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
}

@ -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

@ -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()
}

@ -10,12 +10,12 @@ import (
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net p2p.Host
networkVersion uint64
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)
}

@ -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"`
}

@ -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()
}

@ -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

@ -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
}

@ -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
}

@ -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

@ -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()
}

@ -9,12 +9,12 @@ import (
// PublicNetAPI offers network related RPC methods
type PublicNetAPI struct {
net p2p.Host
networkVersion uint64
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)
}

@ -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)
}

@ -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()
}

@ -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

@ -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,
},
}
}

@ -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()

@ -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(),
}
}

@ -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"

@ -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)

@ -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,
},
}...)

Loading…
Cancel
Save