diff --git a/hmy/blockchain.go b/hmy/blockchain.go
index f6546bcfe..eb9a827a5 100644
--- a/hmy/blockchain.go
+++ b/hmy/blockchain.go
@@ -33,13 +33,13 @@ func (hmy *Harmony) GetShardState() (*shard.State, error) {
// GetBlockSigners ..
func (hmy *Harmony) GetBlockSigners(
- ctx context.Context, blockNr rpc.BlockNumber,
+ ctx context.Context, blockNum rpc.BlockNumber,
) (shard.SlotList, *internal_bls.Mask, error) {
- blk, err := hmy.BlockByNumber(ctx, blockNr)
+ blk, err := hmy.BlockByNumber(ctx, blockNum)
if err != nil {
return nil, nil, err
}
- blockWithSigners, err := hmy.BlockByNumber(ctx, blockNr+1)
+ blockWithSigners, err := hmy.BlockByNumber(ctx, blockNum+1)
if err != nil {
return nil, nil, err
}
diff --git a/hmy/hmy.go b/hmy/hmy.go
index 00ef8d3d3..3f9ed6f65 100644
--- a/hmy/hmy.go
+++ b/hmy/hmy.go
@@ -17,7 +17,7 @@ import (
"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"
+ commonRPC "github.com/harmony-one/harmony/rpc/common"
"github.com/harmony-one/harmony/shard"
staking "github.com/harmony-one/harmony/staking/types"
lru "github.com/hashicorp/golang-lru"
diff --git a/hmy/network.go b/hmy/net.go
similarity index 93%
rename from hmy/network.go
rename to hmy/net.go
index 1e8a65089..a1a7c685b 100644
--- a/hmy/network.go
+++ b/hmy/net.go
@@ -2,7 +2,7 @@ package hmy
import (
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
- commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common"
+ commonRPC "github.com/harmony-one/harmony/rpc/common"
"github.com/harmony-one/harmony/staking/network"
"github.com/libp2p/go-libp2p-core/peer"
)
diff --git a/hmy/staking.go b/hmy/staking.go
index de62a1b45..aefaf101c 100644
--- a/hmy/staking.go
+++ b/hmy/staking.go
@@ -11,8 +11,8 @@ import (
"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"
+ commonRPC "github.com/harmony-one/harmony/rpc/common"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
"github.com/harmony-one/harmony/staking/availability"
diff --git a/internal/hmyapi/README.md b/internal/hmyapi/README.md
deleted file mode 100644
index b9d4d19fe..000000000
--- a/internal/hmyapi/README.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# JSON RPC
-
-## JSSDK
-[FireStack-Lab/Harmony-sdk-core](https://github.com/FireStack-Lab/Harmony-sdk-core)
-
-## JSON-RPC methods
-
-### Network info related
-* [ ] net_listening - check if network is connected
-* [x] hmy_protocolVersion - check protocol version
-* [ ] net_version - get network id
-* [ ] net_peerCount - peer count
-* [x] hmy_getNodeMetadata - get node's version, bls key
-
-### BlockChain info related
-* [ ] hmy_gasPrice - return min-gas-price
-* [ ] hmy_estimateGas - calculating estimate gas using signed bytes
-* [x] hmy_blockNumber - get latest block number
-* [x] hmy_getBlockByHash - get block by block hash
-* [x] hmy_getBlockByNumber
-* [ ] hmy_getUncleByBlockHashAndIndex - get uncle by block hash and index number
-* [ ] hmy_getUncleByBlockNumberAndIndex - get uncle by block number and index number
-* [ ] hmy_getUncleCountByBlockHash - get uncle count by block hash
-* [ ] hmy_getUncleCountByBlockNumber - get uncle count by block number
-* [ ] hmy_syncing - Returns an object with data about the sync status
-* [ ] hmy_coinbase - return coinbase address
-* [ ] hmy_mining - return if mining client is mining
-* [ ] hmy_hashrate - return current hash rate for blockchain
-
-
-### Account related
-* [x] hmy_getBalance - get balance for account address
-* [x] hmy_getAccountNonce - get nonce for account address
-* [ ] hmy_accounts - return accounts that lives in node
-
-### Transactions related
-* [ ] hmy_getTransactionReceipt - get transaction receipt by given transaction hash
-* [ ] hmy_sendRawTransaction - send transaction bytes(signed) to blockchain
-* [ ] hmy_sendTransaction - send transaction object(with signature) to blockchain
-* [x] hmy_getBlockTransactionCountByHash - get transaction count of block by block hash
-* [x] hmy_getBlockTransactionCountByNumber - get transaction count of block by block number
-* [x] hmy_getTransactionByHash - get transaction object of block by block hash
-* [x] hmy_getTransactionByBlockHashAndIndex - get transaction object of block by block hash and index number
-* [x] hmy_getTransactionByBlockNumberAndIndex - get transaction object of block by block number and index number
-* [ ] hmy_sign - sign message using node specific sign method.
-* [ ] hmy_pendingTransactions - returns the pending transactions list.
-* [ ] hmy_getTransactionsCount - returns the number of confirmed (not pending) transactions count for the account
-* [ ] hmy_getStakingTransactionsCount - returns the number of confirmed (not pending) staking transactions count for the account
-
-
-### Contract related
-* [ ] hmy_call - call contract method
-* [x] hmy_getCode - get deployed contract's byte code
-* [x] hmy_getStorageAt - get storage position at a given address
-* ~~[ ] hmy_getCompilers~~ - DEPRECATED
-* ~~[ ] hmy_compileLLL~~ - DEPRECATED
-* ~~[ ] hmy_compileSolidity~~ - DEPRECATED
-* ~~[ ] hmy_compileSerpent~~ - DEPRECATED
-
-### Subscribes
-* [ ] hmy_getLogs - log subscriber
-* [x] hmy_newFilter - creates a filter object, based on filter options
-* [x] hmy_newBlockFilter - creates a filter in the node, to notify when a new block arrives
-* [x] hmy_newPendingTransactionFilter - creates a filter in the node, to notify when new pending transactions arrive
-* [ ] hmy_getFilterChanges - polling method for a filter
-* [ ] hmy_getFilterLogs - returns an array of all logs matching filter with given id.
-* [x] hmy_uninstallFilter - uninstalls a filter with given id
-
-
-### Others, not very important for current stage of work
-* [ ] web3_clientVersion
-* [ ] web3_sha3
-* [ ] hmy_getWork
-* [ ] hmy_submitWork
-* [ ] hmy_submitHashrate
-* [ ] hmy_getProof
-* [ ] db_putString
-* [ ] db_getString
-* [ ] db_putHex
-* [ ] db_getHex
-
-### SHH Whisper Protocol
-The ``shh`` is for the whisper protocol to communicate p2p and broadcast
-
-* [ ] shh_post
-* [ ] shh_version
-* [ ] shh_newIdentity
-* [ ] shh_hasIdentity
-* [ ] shh_newGroup
-* [ ] shh_addToGroup
-* [ ] shh_newFilter
-* [ ] shh_uninstallFilter
-* [ ] shh_getFilterChanges
-* [ ] shh_getMessages
-
-### API Versions
-* For API V1 you specify 1.0 version in curl
-* For API V2 you specify 2.0 version in curl, V2 has output numbers were changed to decimals and also fixed few errors
diff --git a/internal/hmyapi/apiv1/addrlock.go b/internal/hmyapi/apiv1/addrlock.go
deleted file mode 100644
index bda2e8471..000000000
--- a/internal/hmyapi/apiv1/addrlock.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package apiv1
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// AddrLocker ...
-type AddrLocker struct {
- mu sync.Mutex
- locks map[common.Address]*sync.Mutex
-}
-
-// lock returns the lock of the given address.
-func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
- l.mu.Lock()
- defer l.mu.Unlock()
- if l.locks == nil {
- l.locks = make(map[common.Address]*sync.Mutex)
- }
- if _, ok := l.locks[address]; !ok {
- l.locks[address] = new(sync.Mutex)
- }
- return l.locks[address]
-}
-
-// LockAddr locks an account's mutex. This is used to prevent another tx getting the
-// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
-// being read again during the time that the first transaction is being signed.
-func (l *AddrLocker) LockAddr(address common.Address) {
- l.lock(address).Lock()
-}
-
-// UnlockAddr unlocks the mutex of the given account.
-func (l *AddrLocker) UnlockAddr(address common.Address) {
- l.lock(address).Unlock()
-}
diff --git a/internal/hmyapi/apiv1/blockchain.go b/internal/hmyapi/apiv1/blockchain.go
deleted file mode 100644
index 0ffb9358f..000000000
--- a/internal/hmyapi/apiv1/blockchain.go
+++ /dev/null
@@ -1,1033 +0,0 @@
-package apiv1
-
-import (
- "context"
- "fmt"
- "math/big"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/harmony-one/harmony/block"
- "github.com/harmony-one/harmony/common/denominations"
- "github.com/harmony-one/harmony/consensus/quorum"
- "github.com/harmony-one/harmony/consensus/reward"
- "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"
- "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/network"
- staking "github.com/harmony-one/harmony/staking/types"
- "github.com/pkg/errors"
-)
-
-const (
- defaultGasPrice = denominations.Nano
- defaultFromAddress = "0x0000000000000000000000000000000000000000"
- defaultBlocksPeriod = 15000
- validatorsPageSize = 100
- initSupply = int64(12600000000)
-)
-
-// 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 {
- hmy *hmy.Harmony
-}
-
-// NewPublicBlockChainAPI creates a new Harmony blockchain API.
-func NewPublicBlockChainAPI(hmy *hmy.Harmony) *PublicBlockChainAPI {
- return &PublicBlockChainAPI{hmy}
-}
-
-// BlockArgs is struct to include optional block formatting params.
-type BlockArgs struct {
- WithSigners bool `json:"withSigners"`
- InclTx bool `json:"inclTx"`
- FullTx bool `json:"fullTx"`
- Signers []string `json:"signers"`
- InclStaking bool `json:"inclStaking"`
-}
-
-func (s *PublicBlockChainAPI) isBeaconShard() error {
- if s.hmy.ShardID != shard.BeaconChainShardID {
- return ErrNotBeaconShard
- }
- return nil
-}
-
-func (s *PublicBlockChainAPI) isBlockGreaterThanLatest(blockNum rpc.BlockNumber) error {
- // rpc.BlockNumber is int64 (latest = -1. pending = -2) and currentBlockNum is uint64.
- // Most straightfoward to make sure to return nil error for latest and pending block num
- // since they are never greater than latest
- if blockNum != rpc.PendingBlockNumber &&
- blockNum != rpc.LatestBlockNumber &&
- uint64(blockNum) > s.hmy.CurrentBlock().NumberU64() {
- return ErrRequestedBlockTooHigh
- }
- return nil
-}
-
-// 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, blockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, blockNum)
- if blk != nil {
- blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true}
- 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
- }
- }
- return response, err
- }
- return nil, err
-}
-
-// 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) {
- blk, err := s.hmy.GetBlock(ctx, blockHash)
- if blk != nil {
- blockArgs := BlockArgs{WithSigners: false, InclTx: true, FullTx: fullTx, InclStaking: true}
- leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
- return RPCMarshalBlock(blk, blockArgs, leader)
- }
- return nil, err
-}
-
-// 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, blockNum rpc.BlockNumber, blockArgs BlockArgs) (map[string]interface{}, error) {
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, blockNum)
- blockArgs.InclTx = true
- if blockArgs.WithSigners {
- blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNum)
- if err != nil {
- return nil, err
- }
- }
- 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
- }
- }
- return response, err
- }
- return nil, err
-}
-
-// GetBlockByHashNew 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) GetBlockByHashNew(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) {
- 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(blk.NumberU64()))
- if err != nil {
- 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++ {
- blk, err := s.hmy.BlockByNumber(ctx, i)
- blockArgs.InclTx = true
- if blockArgs.WithSigners {
- blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(i))
- if err != nil {
- return nil, err
- }
- }
- 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"} {
- rpcBlock[field] = nil
- }
- }
- result = append(result, rpcBlock)
- }
- }
- return result, nil
-}
-
-// GetValidators returns validators list for a particular epoch.
-func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) {
- cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
- if err != nil {
- return nil, err
- }
- balanceQueryBlock := shard.Schedule.EpochLastBlock(uint64(epoch))
- if balanceQueryBlock > s.hmy.CurrentBlock().NumberU64() {
- balanceQueryBlock = s.hmy.CurrentBlock().NumberU64()
- }
- validators := make([]map[string]interface{}, 0)
- for _, validator := range cmt.Slots {
- oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
- if err != nil {
- return nil, err
- }
- validatorBalance, err := s.getBalanceByBlockNumber(ctx, oneAddress, rpc.BlockNumber(balanceQueryBlock))
- if err != nil {
- return nil, err
- }
- validatorsFields := map[string]interface{}{
- "address": oneAddress,
- "balance": (*hexutil.Big)(validatorBalance),
- }
- validators = append(validators, validatorsFields)
- }
- result := map[string]interface{}{
- "shardID": cmt.ShardID,
- "validators": validators,
- }
- return result, nil
-}
-
-// 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) {
- cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
- if err != nil {
- return nil, err
- }
-
- validators := make([]string, len(cmt.Slots))
- for i, v := range cmt.Slots {
- validators[i] = v.BLSPublicKey.Hex()
- }
- return validators, nil
-}
-
-// IsLastBlock checks if block is last epoch block.
-func (s *PublicBlockChainAPI) IsLastBlock(blockNum uint64) (bool, error) {
- if err := s.isBeaconShard(); err != nil {
- return false, err
- }
- return shard.Schedule.IsLastBlock(blockNum), nil
-}
-
-// EpochLastBlock returns epoch last block.
-func (s *PublicBlockChainAPI) EpochLastBlock(epoch uint64) (uint64, error) {
- if err := s.isBeaconShard(); err != nil {
- return 0, err
- }
- return shard.Schedule.EpochLastBlock(epoch), nil
-}
-
-// GetBlockSigners returns signers for a particular block.
-func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNum rpc.BlockNumber) ([]string, error) {
- if uint64(blockNum) == 0 {
- return []string{}, nil
- }
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
- if err != nil {
- return nil, err
- }
- signers := []string{}
- for _, validator := range slots {
- oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
- if err != nil {
- return nil, err
- }
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- signers = append(signers, oneAddress)
- }
- }
- return signers, nil
-}
-
-// GetBlockSignerKeys returns bls public keys that signed the block.
-func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNum rpc.BlockNumber) ([]string, error) {
- if uint64(blockNum) == 0 {
- return []string{}, nil
- }
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
- if err != nil {
- return nil, err
- }
- signers := []string{}
- for _, validator := range slots {
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- signers = append(signers, validator.BLSPublicKey.Hex())
- }
- }
- return signers, nil
-}
-
-// 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(blockNum); err != nil {
- return false, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
- if err != nil {
- return false, err
- }
- for _, validator := range slots {
- oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
- if err != nil {
- return false, err
- }
- if oneAddress != address {
- continue
- }
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- return true, nil
- }
- }
- return false, nil
-}
-
-// GetSignedBlocks returns how many blocks a particular validator signed for last defaultBlocksPeriod (3 hours ~ 1500 blocks).
-func (s *PublicBlockChainAPI) GetSignedBlocks(ctx context.Context, address string) hexutil.Uint64 {
- totalSigned := uint64(0)
- lastBlock := uint64(0)
- blockHeight := uint64(s.BlockNumber())
- if blockHeight >= defaultBlocksPeriod {
- lastBlock = blockHeight - defaultBlocksPeriod + 1
- }
- for i := lastBlock; i <= blockHeight; i++ {
- signed, err := s.IsBlockSigner(ctx, rpc.BlockNumber(i), address)
- if err == nil && signed {
- totalSigned++
- }
- }
- return hexutil.Uint64(totalSigned)
-}
-
-// GetEpoch returns current epoch.
-func (s *PublicBlockChainAPI) GetEpoch(ctx context.Context) hexutil.Uint64 {
- return hexutil.Uint64(s.LatestHeader(ctx).Epoch)
-}
-
-// GetLeader returns current shard leader.
-func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string {
- return s.LatestHeader(ctx).Leader
-}
-
-// GetValidatorSelfDelegation returns validator stake.
-func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, address string) (hexutil.Uint64, error) {
- if err := s.isBeaconShard(); err != nil {
- return hexutil.Uint64(0), err
- }
- return hexutil.Uint64(s.hmy.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64()), nil
-}
-
-// GetValidatorTotalDelegation returns total balace stacking for validator with delegation.
-func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, address string) (hexutil.Uint64, error) {
- if err := s.isBeaconShard(); err != nil {
- return hexutil.Uint64(0), err
- }
- delegations := s.hmy.GetDelegationsByValidator(internal_common.ParseAddr(address))
- totalStake := big.NewInt(0)
- for _, delegation := range delegations {
- totalStake.Add(totalStake, delegation.Amount)
- }
- // TODO: return more than uint64
- return hexutil.Uint64(totalStake.Uint64()), nil
-}
-
-// GetShardingStructure returns an array of sharding structures.
-func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[string]interface{}, error) {
- // Get header and number of shards.
- epoch := s.GetEpoch(ctx)
- numShard := shard.Schedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards()
-
- // Return shareding structure for each case.
- 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.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.hmy.StateAndHeaderByNumber(ctx, blockNr)
- if state == nil || err != nil {
- return nil, err
- }
- code := state.GetCode(address)
- return code, state.Error()
-}
-
-// 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 rpc.BlockNumber) (hexutil.Bytes, error) {
- address := internal_common.ParseAddr(addr)
- state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNr)
- if state == nil || err != nil {
- return nil, err
- }
- res := state.GetState(address, common.HexToHash(key))
- return res[:], state.Error()
-}
-
-func (s *PublicBlockChainAPI) getBalanceByBlockNumber(ctx context.Context, address string, blockNr rpc.BlockNumber) (*hexutil.Big, error) {
- addr := internal_common.ParseAddr(address)
- balance, err := s.hmy.GetBalance(ctx, addr, blockNr)
- if balance == nil {
- return nil, err
- }
- return (*hexutil.Big)(balance), err
-}
-
-// GetBalanceByBlockNumber returns balance by block number.
-func (s *PublicBlockChainAPI) GetBalanceByBlockNumber(ctx context.Context, address string, blockNr rpc.BlockNumber) (*hexutil.Big, error) {
- if err := s.isBlockGreaterThanLatest(blockNr); err != nil {
- return nil, err
- }
- return s.getBalanceByBlockNumber(ctx, address, blockNr)
-}
-
-// 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.hmy.GetAccountNonce(ctx, addr, rpc.BlockNumber(blockNr))
-}
-
-// GetBalance returns the amount of Atto for the given address in the state of the
-// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
-// block numbers are also allowed.
-func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address string, blockNr rpc.BlockNumber) (*hexutil.Big, error) {
- return s.getBalanceByBlockNumber(ctx, address, rpc.BlockNumber(blockNr))
-}
-
-// BlockNumber returns the block number of the chain head.
-func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 {
- header, _ := s.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
- if header == nil {
- return 0
- }
- return hexutil.Uint64(header.Number().Uint64())
-}
-
-// ResendCx requests that the egress receipt for the given cross-shard
-// transaction be sent to the destination shard for credit. This is used for
-// unblocking a half-complete cross-shard transaction whose fund has been
-// 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.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.hmy, args, blockNr, vm.Config{}, 5*time.Second, s.hmy.RPCGasCap)
- return (hexutil.Bytes)(result), err
-}
-
-// LatestHeader returns the latest header information
-func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformation {
- 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)
-}
-
-// GetHeaderByNumber returns block header at given number
-func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*HeaderInformation, error) {
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- header, err := s.hmy.HeaderByNumber(context.Background(), blockNum)
- if err != nil {
- return nil, err
- }
- leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch())
- return newHeaderInformation(header, leader), nil
-}
-
-// GetTotalStaking returns total staking by validators, only meant to be called on beaconchain
-// explorer node
-func (s *PublicBlockChainAPI) GetTotalStaking() (*big.Int, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetTotalStakingSnapshot(), nil
-}
-
-// GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain
-// explorer node
-func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() (
- *committee.CompletedEPoSRound, error,
-) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetMedianRawStakeSnapshot()
-}
-
-// GetLatestChainHeaders ..
-func (s *PublicBlockChainAPI) GetLatestChainHeaders() *block.HeaderPair {
- return s.hmy.GetLatestChainHeaders()
-}
-
-// GetAllValidatorAddresses returns all validator addresses.
-func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]string, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- validatorAddresses := s.hmy.GetAllValidatorAddresses()
- addresses := make([]string, len(validatorAddresses))
- for i, addr := range validatorAddresses {
- oneAddr, _ := internal_common.AddressToBech32(addr)
- addresses[i] = oneAddr
- }
- return addresses, nil
-}
-
-// GetElectedValidatorAddresses returns elected validator addresses.
-func (s *PublicBlockChainAPI) GetElectedValidatorAddresses() ([]string, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- electedAddresses := s.hmy.GetElectedValidatorAddresses()
- addresses := make([]string, len(electedAddresses))
- for i, addr := range electedAddresses {
- oneAddr, _ := internal_common.AddressToBech32(addr)
- addresses[i] = oneAddr
- }
- return addresses, nil
-}
-
-// GetValidatorInformation returns information about a validator.
-func (s *PublicBlockChainAPI) GetValidatorInformation(
- ctx context.Context, address string,
-) (*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the latest blk information")
- }
- return s.hmy.GetValidatorInformation(
- internal_common.ParseAddr(address), blk,
- )
-}
-
-// GetValidatorInformationByBlockNumber returns information about a validator.
-func (s *PublicBlockChainAPI) GetValidatorInformationByBlockNumber(
- ctx context.Context, address string, blockNr rpc.BlockNumber,
-) (*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNr); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr)
- }
- return s.hmy.GetValidatorInformation(
- internal_common.ParseAddr(address), blk,
- )
-}
-
-func (s *PublicBlockChainAPI) getAllValidatorInformation(
- ctx context.Context, page int, blockNr rpc.BlockNumber,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if page < -1 {
- return nil, errors.Errorf("page given %d cannot be less than -1", page)
- }
- addresses := s.hmy.GetAllValidatorAddresses()
- if page != -1 && len(addresses) <= page*validatorsPageSize {
- return make([]*staking.ValidatorRPCEnhanced, 0), nil
- }
- validatorsNum := len(addresses)
- start := 0
- if page != -1 {
- validatorsNum = validatorsPageSize
- start = page * validatorsPageSize
- if len(addresses)-start < validatorsPageSize {
- validatorsNum = len(addresses) - start
- }
- }
- validators := []*staking.ValidatorRPCEnhanced{}
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr))
- if err != nil {
- 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.hmy.GetValidatorInformation(addresses[i], blk)
- if err == nil {
- validators = append(validators, information)
- }
- }
- return validators, nil
-}
-
-// GetAllValidatorInformation returns information about all validators.
-// If page is -1, return all instead of `validatorsPageSize` elements.
-func (s *PublicBlockChainAPI) GetAllValidatorInformation(
- ctx context.Context, page int,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- blockNr := s.hmy.CurrentBlock().NumberU64()
-
- // delete cache for previous block
- prevKey := fmt.Sprintf("all-info-%d", blockNr-1)
- s.hmy.SingleFlightForgetKey(prevKey)
-
- key := fmt.Sprintf("all-info-%d", blockNr)
- res, err := s.hmy.SingleFlightRequest(
- key,
- func() (interface{}, error) {
- return s.getAllValidatorInformation(ctx, page, rpc.LatestBlockNumber)
- },
- )
- if err != nil {
- return nil, err
- }
- return res.([]*staking.ValidatorRPCEnhanced), nil
-}
-
-// GetAllValidatorInformationByBlockNumber returns information about all validators.
-// If page is -1, return all instead of `validatorsPageSize` elements.
-func (s *PublicBlockChainAPI) GetAllValidatorInformationByBlockNumber(
- ctx context.Context, page int, blockNr rpc.BlockNumber,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNr); err != nil {
- return nil, err
- }
- return s.getAllValidatorInformation(ctx, page, blockNr)
-}
-
-// GetAllDelegationInformation returns delegation information about `validatorsPageSize` validators,
-// starting at `page*validatorsPageSize`.
-// If page is -1, return all instead of `validatorsPageSize` elements.
-func (s *PublicBlockChainAPI) GetAllDelegationInformation(ctx context.Context, page int) ([][]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if page < -1 {
- return make([][]*RPCDelegation, 0), nil
- }
- addresses := s.hmy.GetAllValidatorAddresses()
- if page != -1 && len(addresses) <= page*validatorsPageSize {
- return make([][]*RPCDelegation, 0), nil
- }
- validatorsNum := len(addresses)
- start := 0
- if page != -1 {
- validatorsNum = validatorsPageSize
- start = page * validatorsPageSize
- if len(addresses)-start < validatorsPageSize {
- validatorsNum = len(addresses) - start
- }
- }
- validators := make([][]*RPCDelegation, validatorsNum)
- var err error
- for i := start; i < start+validatorsNum; i++ {
- validators[i-start], err = s.GetDelegationsByValidator(ctx, addresses[i].String())
- if err != nil {
- return nil, err
- }
- }
- return validators, nil
-}
-
-// GetDelegationsByDelegator returns list of delegations for a delegator address.
-func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, address string) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
- result := []*RPCDelegation{}
- for i := range delegations {
- delegation := delegations[i]
-
- undelegations := make([]RPCUndelegation, len(delegation.Undelegations))
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validators[i])
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- result = append(result, &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- })
- }
- return result, nil
-}
-
-// GetDelegationsByDelegatorByBlockNumber returns list of delegations for a delegator address at given block number
-func (s *PublicBlockChainAPI) GetDelegationsByDelegatorByBlockNumber(
- ctx context.Context, address string, blockNum rpc.BlockNumber,
-) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum)
- }
- validators, delegations := s.hmy.GetDelegationsByDelegatorByBlock(delegatorAddress, blk)
- result := make([]*RPCDelegation, len(delegations))
- for i := range delegations {
- delegation := delegations[i]
-
- undelegations := make([]RPCUndelegation, len(delegation.Undelegations))
-
- for j := range delegation.Undelegations {
- undelegations[j] = RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- }
- }
- valAddr, _ := internal_common.AddressToBech32(validators[i])
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- result[i] = &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- }
- }
- return result, nil
-}
-
-// GetDelegationsByValidator returns list of delegations for a validator address.
-func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, address string) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- validatorAddress := internal_common.ParseAddr(address)
- delegations := s.hmy.GetDelegationsByValidator(validatorAddress)
- result := make([]*RPCDelegation, 0)
- for _, delegation := range delegations {
-
- undelegations := []RPCUndelegation{}
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validatorAddress)
- delAddr, _ := internal_common.AddressToBech32(delegation.DelegatorAddress)
- result = append(result, &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- })
- }
- return result, nil
-}
-
-// GetDelegationByDelegatorAndValidator returns a delegation for delegator and validator.
-func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.Context, address string, validator string) (*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- validatorAddress := internal_common.ParseAddr(validator)
- validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
- for i := range delegations {
- if validators[i] != validatorAddress {
- continue
- }
- delegation := delegations[i]
-
- undelegations := []RPCUndelegation{}
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validatorAddress)
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- return &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- }, nil
- }
- return nil, 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.hmy, args, nil)
-}
-
-// GetCurrentUtilityMetrics ..
-func (s *PublicBlockChainAPI) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetCurrentUtilityMetrics()
-}
-
-// GetSuperCommittees ..
-func (s *PublicBlockChainAPI) GetSuperCommittees() (*quorum.Transition, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetSuperCommittees()
-}
-
-// GetCurrentBadBlocks ..
-func (s *PublicBlockChainAPI) GetCurrentBadBlocks() []core.BadBlock {
- return s.hmy.GetCurrentBadBlocks()
-}
-
-// GetTotalSupply ..
-func (s *PublicBlockChainAPI) GetTotalSupply() (numeric.Dec, error) {
- return numeric.NewDec(initSupply), nil
-}
-
-// GetCirculatingSupply ..
-func (s *PublicBlockChainAPI) GetCirculatingSupply() (numeric.Dec, error) {
- timestamp := time.Now()
- return numeric.NewDec(initSupply).Mul(reward.PercentageForTimeStamp(timestamp.Unix())), nil
-}
-
-// GetStakingNetworkInfo ..
-func (s *PublicBlockChainAPI) GetStakingNetworkInfo(
- ctx context.Context,
-) (*StakingNetworkInfo, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- totalStaking, _ := s.GetTotalStaking()
- round, _ := s.GetMedianRawStakeSnapshot()
- epoch := s.LatestHeader(ctx).Epoch
- epochLastBlock, _ := s.EpochLastBlock(epoch)
- totalSupply, _ := s.GetTotalSupply()
- circulatingSupply, _ := s.GetCirculatingSupply()
- return &StakingNetworkInfo{
- TotalSupply: totalSupply,
- CirculatingSupply: circulatingSupply,
- EpochLastBlock: epochLastBlock,
- TotalStaking: totalStaking,
- MedianRawStake: round.MedianStake,
- }, nil
-}
-
-// GetLastCrossLinks ..
-func (s *PublicBlockChainAPI) GetLastCrossLinks() ([]*types.CrossLink, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetLastCrossLinks()
-}
-
-// docall executes an EVM call
-func doCall(ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
- defer func(start time.Time) {
- utils.Logger().Debug().
- Dur("runtime", time.Since(start)).
- Msg("Executing EVM call finished")
- }(time.Now())
-
- state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNr)
- if state == nil || err != nil {
- return nil, 0, false, err
- }
- // Set sender address or use a default if none specified
- var addr common.Address
- if args.From == nil {
- // Any address does not affect the logic of this call.
- addr = common.HexToAddress(defaultFromAddress)
- } else {
- addr = *args.From
- }
- // Set default gas & gas price if none were set
- gas := uint64(math.MaxUint64 / 2)
- if args.Gas != nil {
- gas = uint64(*args.Gas)
- }
- if globalGasCap != nil && globalGasCap.Uint64() < gas {
- utils.Logger().Warn().
- Uint64("requested", gas).
- Uint64("cap", globalGasCap.Uint64()).
- Msg("Caller gas above allowance, capping")
- gas = globalGasCap.Uint64()
- }
- gasPrice := new(big.Int).SetUint64(defaultGasPrice)
- if args.GasPrice != nil {
- gasPrice = args.GasPrice.ToInt()
- }
-
- value := new(big.Int)
- if args.Value != nil {
- value = args.Value.ToInt()
- }
-
- var data []byte
- if args.Data != nil {
- data = []byte(*args.Data)
- }
-
- // Create new call message
- msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false)
-
- // Setup context so it may be cancelled the call has completed
- // or, in case of unmetered gas, setup a context with a timeout.
- var cancel context.CancelFunc
- if timeout > 0 {
- ctx, cancel = context.WithTimeout(ctx, timeout)
- } else {
- ctx, cancel = context.WithCancel(ctx)
- }
- // Make sure the context is cancelled when the call has completed
- // this makes sure resources are cleaned up.
- defer cancel()
-
- // Get a new instance of the EVM.
- evm, vmError, err := hmy.GetEVM(ctx, msg, state, header)
- if err != nil {
- return nil, 0, false, err
- }
- // Wait for the context to be done and cancel the evm. Even if the
- // EVM has finished, cancelling may be done (repeatedly)
- go func() {
- <-ctx.Done()
- evm.Cancel()
- }()
-
- // Setup the gas pool (also for unmetered requests)
- // and apply the message.
- gp := new(core.GasPool).AddGas(math.MaxUint64)
- res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
- if err := vmError(); err != nil {
- return nil, 0, false, err
- }
- // If the timer caused an abort, return an appropriate error message
- if evm.Cancelled() {
- return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout)
- }
- return res, gas, failed, err
-}
-
-// doEstimateGas ..
-func doEstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) {
- // Binary search the gas requirement, as it may be higher than the amount used
- var (
- lo = params.TxGas - 1
- hi uint64
- max uint64
- )
- blockNum := rpc.LatestBlockNumber
- if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
- hi = uint64(*args.Gas)
- } else {
- // Retrieve the blk to act as the gas ceiling
- blk, err := hmy.BlockByNumber(ctx, blockNum)
- if err != nil {
- return 0, err
- }
- hi = blk.GasLimit()
- }
- if gasCap != nil && hi > gasCap.Uint64() {
- hi = gasCap.Uint64()
- }
- max = hi
-
- // Use zero-address if none other is available
- if args.From == nil {
- args.From = &common.Address{}
- }
- // Create a helper to check if a gas allowance results in an executable transaction
- executable := func(gas uint64) bool {
- args.Gas = (*hexutil.Uint64)(&gas)
-
- _, _, failed, err := doCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max)))
- if err != nil || failed {
- return false
- }
- return true
- }
- // Execute the binary search and hone in on an executable gas limit
- for lo+1 < hi {
- mid := (hi + lo) / 2
- if !executable(mid) {
- lo = mid
- } else {
- hi = mid
- }
- }
- // Reject the transaction as invalid if it still fails at the highest allowance
- if hi == max {
- if !executable(hi) {
- return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", max)
- }
- }
- return hexutil.Uint64(hi), nil
-}
diff --git a/internal/hmyapi/apiv1/debug.go b/internal/hmyapi/apiv1/debug.go
deleted file mode 100644
index c08ef06f7..000000000
--- a/internal/hmyapi/apiv1/debug.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package apiv1
-
-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 {
- hmy *hmy.Harmony
-}
-
-// NewDebugAPI Creates a new DebugAPI instance
-func NewDebugAPI(hmy *hmy.Harmony) *DebugAPI {
- return &DebugAPI{hmy}
-}
-
-// SetLogVerbosity Sets log verbosity on runtime
-// Example usage:
-// curl -H "Content-Type: application/json" -d '{"method":"debug_setLogVerbosity","params":[0],"id":1}' http://localhost:9123
-func (*DebugAPI) SetLogVerbosity(ctx context.Context, level int) (map[string]interface{}, error) {
- if level < int(log.LvlCrit) || level > int(log.LvlTrace) {
- return nil, ErrInvalidLogLevel
- }
-
- verbosity := log.Lvl(level)
- utils.SetLogVerbosity(verbosity)
- return map[string]interface{}{"verbosity": verbosity.String()}, nil
-}
diff --git a/internal/hmyapi/apiv1/error.go b/internal/hmyapi/apiv1/error.go
deleted file mode 100644
index b0d0b7a27..000000000
--- a/internal/hmyapi/apiv1/error.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package apiv1
-
-import (
- "errors"
-)
-
-var (
- // ErrInvalidLogLevel when invalid log level is provided
- ErrInvalidLogLevel = errors.New("invalid log level")
- // ErrIncorrectChainID when ChainID does not match running node
- ErrIncorrectChainID = errors.New("incorrect chain id")
- // ErrInvalidChainID when ChainID of signer does not match that of running node
- ErrInvalidChainID = errors.New("invalid chain id for signer")
- // ErrNotBeaconShard when rpc is called on not beacon chain node
- ErrNotBeaconShard = errors.New("cannot call this rpc on non beaconchain node")
- // ErrRequestedBlockTooHigh when given block is greater than latest block number
- ErrRequestedBlockTooHigh = errors.New("requested block number greater than current block number")
-)
diff --git a/internal/hmyapi/apiv1/harmony.go b/internal/hmyapi/apiv1/harmony.go
deleted file mode 100644
index a51a2ef95..000000000
--- a/internal/hmyapi/apiv1/harmony.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package apiv1
-
-import (
- "context"
- "math/big"
-
- "github.com/ethereum/go-ethereum/common/hexutil"
- "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 {
- hmy *hmy.Harmony
-}
-
-// NewPublicHarmonyAPI ...
-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(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
-// yet received the latest block headers from its pears. In case it is synchronizing:
-// - startingBlock: block number this node started to synchronise from
-// - currentBlock: block number this node is currently importing
-// - highestBlock: block number of the highest block header this node has received from peers
-// - 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(dm): find our Downloader module for syncing blocks
- return false, nil
-}
-
-// GasPrice returns a suggestion for a gas price.
-func (s *PublicHarmonyAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
- // TODO(ricl): add SuggestPrice API
- return (*hexutil.Big)(big.NewInt(1)), nil
-}
-
-// GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node
-func (s *PublicHarmonyAPI) GetNodeMetadata() commonRPC.NodeMetadata {
- return s.hmy.GetNodeMetadata()
-}
-
-// GetPeerInfo produces a NodePeerInfo record
-func (s *PublicHarmonyAPI) GetPeerInfo() commonRPC.NodePeerInfo {
- return s.hmy.GetPeerInfo()
-}
diff --git a/internal/hmyapi/apiv1/net.go b/internal/hmyapi/apiv1/net.go
deleted file mode 100644
index c50098ef1..000000000
--- a/internal/hmyapi/apiv1/net.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package apiv1
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/harmony-one/harmony/p2p"
-)
-
-// PublicNetAPI offers network related RPC methods
-type PublicNetAPI struct {
- net p2p.Host
- chainID uint64
-}
-
-// NewPublicNetAPI creates a new net API instance.
-func NewPublicNetAPI(net p2p.Host, chainID uint64) *PublicNetAPI {
- return &PublicNetAPI{net, chainID}
-}
-
-// PeerCount returns the number of connected peers
-func (s *PublicNetAPI) PeerCount() hexutil.Uint {
- return hexutil.Uint(s.net.GetPeerCount())
-}
-
-// Version returns the network version, i.e. ChainID identifying which network we are using
-func (s *PublicNetAPI) Version() string {
- return fmt.Sprintf("%d", s.chainID)
-}
diff --git a/internal/hmyapi/apiv1/transactionpool.go b/internal/hmyapi/apiv1/transactionpool.go
deleted file mode 100644
index 9e5b6d6fb..000000000
--- a/internal/hmyapi/apiv1/transactionpool.go
+++ /dev/null
@@ -1,410 +0,0 @@
-package apiv1
-
-import (
- "context"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/rlp"
- "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"
- "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"
-)
-
-// TxHistoryArgs is struct to make GetTransactionsHistory request
-type TxHistoryArgs struct {
- Address string `json:"address"`
- PageIndex uint32 `json:"pageIndex"`
- PageSize uint32 `json:"pageSize"`
- FullTx bool `json:"fullTx"`
- TxType string `json:"txType"`
- Order string `json:"order"`
-}
-
-// PublicTransactionPoolAPI exposes methods for the RPC interface
-type PublicTransactionPoolAPI struct {
- hmy *hmy.Harmony
- nonceLock *AddrLocker
-}
-
-// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
-func NewPublicTransactionPoolAPI(hmy *hmy.Harmony, nonceLock *AddrLocker) *PublicTransactionPoolAPI {
- return &PublicTransactionPoolAPI{hmy, nonceLock}
-}
-
-// GetTransactionsHistory returns the list of transactions hashes that involve a particular address.
-func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, args TxHistoryArgs) (map[string]interface{}, error) {
- var address string
- var result []common.Hash
- var err error
- if strings.HasPrefix(args.Address, "one1") {
- address = args.Address
- } else {
- addr := internal_common.ParseAddr(args.Address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return nil, err
- }
- }
- hashes, err := s.hmy.GetTransactionsHistory(address, args.TxType, args.Order)
- if err != nil {
- return nil, err
- }
- result = ReturnWithPagination(hashes, args.PageIndex, args.PageSize)
- if !args.FullTx {
- return map[string]interface{}{"transactions": result}, nil
- }
- txs := []*RPCTransaction{}
- for _, hash := range result {
- tx := s.GetTransactionByHash(ctx, hash)
- txs = append(txs, tx)
- }
- return map[string]interface{}{"transactions": txs}, nil
-}
-
-// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
-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
- }
- return nil
-}
-
-// 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.hmy.GetBlock(ctx, blockHash); block != nil {
- n := hexutil.Uint(len(block.Transactions()))
- return &n
- }
- return nil
-}
-
-// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
-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
-}
-
-// 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.hmy.GetBlock(ctx, blockHash); block != nil {
- return newRPCTransactionFromBlockIndex(block, uint64(index))
- }
- return nil
-}
-
-// 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.hmy.ChainDb(), hash)
- block, _ := s.hmy.GetBlock(ctx, blockHash)
- if block == nil {
- return nil
- }
- if tx != nil {
- return newRPCTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index)
- }
- // Transaction unknown, return as such
- return nil
-}
-
-// 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.hmy.ChainDb(), hash)
- block, _ := s.hmy.GetBlock(ctx, blockHash)
- if block == nil {
- return nil
- }
- if stx != nil {
- return newRPCStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index)
- }
- // Transaction unknown, return as such
- return nil
-}
-
-// GetStakingTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
-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
-}
-
-// 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.hmy.GetBlock(ctx, blockHash); block != nil {
- return newRPCStakingTransactionFromBlockIndex(block, uint64(index))
- }
- return nil
-}
-
-// 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, blockNum rpc.BlockNumber) (*hexutil.Uint64, error) {
- address := internal_common.ParseAddr(addr)
- // Ask transaction pool for the nonce which includes pending transactions
- 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.hmy.StateAndHeaderByNumber(ctx, blockNum)
- if state == nil || err != nil {
- return nil, err
- }
- nonce := state.GetNonce(address)
- return (*hexutil.Uint64)(&nonce), state.Error()
-}
-
-// GetTransactionsCount returns the number of regular transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
-func (s *PublicTransactionPoolAPI) GetTransactionsCount(ctx context.Context, address, txType string) (uint64, error) {
- var err error
- if !strings.HasPrefix(address, "one1") {
- addr := internal_common.ParseAddr(address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return 0, err
- }
- }
- return s.hmy.GetTransactionsCount(address, txType)
-}
-
-// GetStakingTransactionsCount returns the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
-func (s *PublicTransactionPoolAPI) GetStakingTransactionsCount(ctx context.Context, address, txType string) (uint64, error) {
- var err error
- if !strings.HasPrefix(address, "one1") {
- addr := internal_common.ParseAddr(address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return 0, err
- }
- }
- return s.hmy.GetStakingTransactionsCount(address, txType)
-}
-
-// SendRawStakingTransaction will add the signed transaction to the transaction pool.
-// The sender is responsible for signing the transaction and using the correct nonce.
-func (s *PublicTransactionPoolAPI) SendRawStakingTransaction(
- ctx context.Context, encodedTx hexutil.Bytes,
-) (common.Hash, error) {
- if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
- err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
- return common.Hash{}, err
- }
- tx := new(staking.StakingTransaction)
- if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
- return common.Hash{}, err
- }
- 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.hmy, tx)
-}
-
-// SendRawTransaction will add the signed transaction to the transaction pool.
-// The sender is responsible for signing the transaction and using the correct nonce.
-func (s *PublicTransactionPoolAPI) SendRawTransaction(
- ctx context.Context, encodedTx hexutil.Bytes,
-) (common.Hash, error) {
- if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
- err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
- return common.Hash{}, err
- }
- tx := new(types.Transaction)
- if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
- return common.Hash{}, err
- }
- 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.hmy, tx)
-}
-
-func (s *PublicTransactionPoolAPI) fillTransactionFields(tx *types.Transaction, fields map[string]interface{}) error {
- var err error
- fields["shardID"] = tx.ShardID()
- var signer types.Signer = types.FrontierSigner{}
- if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainID())
- }
- from, _ := types.Sender(signer, tx)
- fields["from"] = from
- fields["to"] = ""
- if tx.To() != nil {
- fields["to"], err = internal_common.AddressToBech32(*tx.To())
- if err != nil {
- return err
- }
- fields["from"], err = internal_common.AddressToBech32(from)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (s *PublicTransactionPoolAPI) fillStakingTransactionFields(stx *staking.StakingTransaction, fields map[string]interface{}) error {
- from, err := stx.SenderAddress()
- if err != nil {
- return err
- }
- fields["sender"], err = internal_common.AddressToBech32(from)
- if err != nil {
- return err
- }
- fields["type"] = stx.StakingType()
- return nil
-}
-
-// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
-func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
- var tx *types.Transaction
- var stx *staking.StakingTransaction
- var blockHash common.Hash
- var blockNumber, index uint64
- tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.hmy.ChainDb(), hash)
- if tx == nil {
- stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash)
- if stx == nil {
- return nil, nil
- }
- }
- receipts, err := s.hmy.GetReceipts(ctx, blockHash)
- if err != nil {
- return nil, err
- }
- if len(receipts) <= int(index) {
- return nil, nil
- }
- receipt := receipts[index]
- fields := map[string]interface{}{
- "blockHash": blockHash,
- "blockNumber": hexutil.Uint64(blockNumber),
- "transactionHash": hash,
- "transactionIndex": hexutil.Uint64(index),
- "gasUsed": hexutil.Uint64(receipt.GasUsed),
- "cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
- "contractAddress": nil,
- "logs": receipt.Logs,
- "logsBloom": receipt.Bloom,
- }
- if tx != nil {
- if err = s.fillTransactionFields(tx, fields); err != nil {
- return nil, err
- }
- } else { // stx not nil
- if err = s.fillStakingTransactionFields(stx, fields); err != nil {
- return nil, err
- }
- }
- // Assign receipt status or post state.
- if len(receipt.PostState) > 0 {
- fields["root"] = hexutil.Bytes(receipt.PostState)
- } else {
- fields["status"] = hexutil.Uint(receipt.Status)
- }
- if receipt.Logs == nil {
- fields["logs"] = [][]*types.Log{}
- }
- // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
- if receipt.ContractAddress != (common.Address{}) {
- fields["contractAddress"] = receipt.ContractAddress
- }
- return fields, nil
-}
-
-// GetPoolStats returns stats for the tx-pool
-func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} {
- pendingCount, queuedCount := s.hmy.GetPoolStats()
- return map[string]interface{}{
- "executable-count": pendingCount,
- "non-executable-count": queuedCount,
- }
-}
-
-// PendingTransactions returns the plain transactions that are in the transaction pool
-func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) {
- pending, err := s.hmy.GetPoolTransactions()
- if err != nil {
- return nil, err
- }
- transactions := make([]*RPCTransaction, 0)
- for i := range pending {
- if plainTx, ok := pending[i].(*types.Transaction); ok {
- if tx := newRPCTransaction(plainTx, common.Hash{}, 0, 0, 0); tx != nil {
- transactions = append(transactions, tx)
- }
- } else if _, ok := pending[i].(*staking.StakingTransaction); ok {
- continue // Do not return staking transactions here.
- } else {
- return nil, types.ErrUnknownPoolTxType
- }
- }
- return transactions, nil
-}
-
-// PendingStakingTransactions returns the staking transactions that are in the transaction pool
-func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTransaction, error) {
- pending, err := s.hmy.GetPoolTransactions()
- if err != nil {
- return nil, err
- }
- transactions := make([]*RPCStakingTransaction, 0)
- for i := range pending {
- if _, ok := pending[i].(*types.Transaction); ok {
- continue // Do not return plain transactions here
- } else if stakingTx, ok := pending[i].(*staking.StakingTransaction); ok {
- if tx := newRPCStakingTransaction(stakingTx, common.Hash{}, 0, 0, 0); tx != nil {
- transactions = append(transactions, tx)
- }
- } else {
- return nil, types.ErrUnknownPoolTxType
- }
- }
- return transactions, nil
-}
-
-// GetCurrentTransactionErrorSink ..
-func (s *PublicTransactionPoolAPI) GetCurrentTransactionErrorSink() types.TransactionErrorReports {
- return s.hmy.GetCurrentTransactionErrorSink()
-}
-
-// GetCurrentStakingErrorSink ..
-func (s *PublicTransactionPoolAPI) GetCurrentStakingErrorSink() types.TransactionErrorReports {
- 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.hmy.ChainDb(), hash); cx != nil {
- return newRPCCXReceipt(cx, blockHash, blockNumber)
- }
- return nil
-}
-
-// GetPendingCXReceipts ..
-func (s *PublicTransactionPoolAPI) GetPendingCXReceipts(ctx context.Context) []*types.CXReceiptsProof {
- return s.hmy.GetPendingCXReceipts()
-}
diff --git a/internal/hmyapi/apiv1/types.go b/internal/hmyapi/apiv1/types.go
deleted file mode 100644
index f16adc7c9..000000000
--- a/internal/hmyapi/apiv1/types.go
+++ /dev/null
@@ -1,538 +0,0 @@
-package apiv1
-
-import (
- "encoding/hex"
- "math/big"
- "strings"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- ethtypes "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/harmony-one/harmony/block"
- "github.com/harmony-one/harmony/core/types"
- internal_common "github.com/harmony-one/harmony/internal/common"
- "github.com/harmony-one/harmony/numeric"
- "github.com/harmony-one/harmony/shard"
- staking "github.com/harmony-one/harmony/staking/types"
-)
-
-// RPCBlock represents a block that will serialize to the RPC representation of a block
-type RPCBlock struct {
- Number *hexutil.Big `json:"number"`
- ViewID *hexutil.Big `json:"viewID"`
- Epoch *hexutil.Big `json:"epoch"`
- Hash common.Hash `json:"hash"`
- ParentHash common.Hash `json:"parentHash"`
- Nonce types.BlockNonce `json:"nonce"`
- MixHash common.Hash `json:"mixHash"`
- LogsBloom ethtypes.Bloom `json:"logsBloom"`
- StateRoot common.Hash `json:"stateRoot"`
- Miner common.Address `json:"miner"`
- Difficulty *hexutil.Big `json:"difficulty"`
- ExtraData []byte `json:"extraData"`
- Size hexutil.Uint64 `json:"size"`
- GasLimit hexutil.Uint64 `json:"gasLimit"`
- GasUsed hexutil.Uint64 `json:"gasUsed"`
- Timestamp *big.Int `json:"timestamp"`
- TransactionsRoot common.Hash `json:"transactionsRoot"`
- ReceiptsRoot common.Hash `json:"receiptsRoot"`
- Transactions []interface{} `json:"transactions"`
- StakingTxs []interface{} `json:"stakingTxs"`
- Uncles []common.Hash `json:"uncles"`
- TotalDifficulty *big.Int `json:"totalDifficulty"`
- Signers []string `json:"signers"`
-}
-
-// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
-type RPCTransaction struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *hexutil.Big `json:"blockNumber"`
- From string `json:"from"`
- Timestamp hexutil.Uint64 `json:"timestamp"`
- Gas hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Hash common.Hash `json:"hash"`
- Input hexutil.Bytes `json:"input"`
- Nonce hexutil.Uint64 `json:"nonce"`
- To string `json:"to"`
- TransactionIndex hexutil.Uint `json:"transactionIndex"`
- Value *hexutil.Big `json:"value"`
- ShardID uint32 `json:"shardID"`
- ToShardID uint32 `json:"toShardID"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
-}
-
-// RPCStakingTransaction represents a transaction that will serialize to the RPC representation of a staking transaction
-type RPCStakingTransaction struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *hexutil.Big `json:"blockNumber"`
- From string `json:"from"`
- Timestamp hexutil.Uint64 `json:"timestamp"`
- Gas hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Hash common.Hash `json:"hash"`
- Nonce hexutil.Uint64 `json:"nonce"`
- TransactionIndex hexutil.Uint `json:"transactionIndex"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
- Type string `json:"type"`
- Msg map[string]interface{} `json:"msg"`
-}
-
-// RPCCXReceipt represents a CXReceipt that will serialize to the RPC representation of a CXReceipt
-type RPCCXReceipt struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *hexutil.Big `json:"blockNumber"`
- TxHash common.Hash `json:"hash"`
- From string `json:"from"`
- To string `json:"to"`
- ShardID uint32 `json:"shardID"`
- ToShardID uint32 `json:"toShardID"`
- Amount *hexutil.Big `json:"value"`
-}
-
-// HeaderInformation represents the latest consensus information
-type HeaderInformation struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber uint64 `json:"blockNumber"`
- ShardID uint32 `json:"shardID"`
- Leader string `json:"leader"`
- ViewID uint64 `json:"viewID"`
- Epoch uint64 `json:"epoch"`
- Timestamp string `json:"timestamp"`
- UnixTime uint64 `json:"unixtime"`
- LastCommitSig string `json:"lastCommitSig"`
- LastCommitBitmap string `json:"lastCommitBitmap"`
- CrossLinks *types.CrossLinks `json:"crossLinks,omitempty"`
-}
-
-// RPCDelegation represents a particular delegation to a validator
-type RPCDelegation struct {
- ValidatorAddress string `json:"validator_address"`
- DelegatorAddress string `json:"delegator_address"`
- Amount *big.Int `json:"amount"`
- Reward *big.Int `json:"reward"`
- Undelegations []RPCUndelegation `json:"Undelegations"`
-}
-
-// RPCUndelegation represents one undelegation entry
-type RPCUndelegation struct {
- Amount *big.Int
- Epoch *big.Int
-}
-
-// CallArgs represents the arguments for a call.
-type CallArgs struct {
- From *common.Address `json:"from"`
- To *common.Address `json:"to"`
- Gas *hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Value *hexutil.Big `json:"value"`
- Data *hexutil.Bytes `json:"data"`
-}
-
-// StakingNetworkInfo returns global staking info.
-type StakingNetworkInfo struct {
- TotalSupply numeric.Dec `json:"total-supply"`
- CirculatingSupply numeric.Dec `json:"circulating-supply"`
- EpochLastBlock uint64 `json:"epoch-last-block"`
- TotalStaking *big.Int `json:"total-staking"`
- MedianRawStake numeric.Dec `json:"median-raw-stake"`
-}
-
-func newHeaderInformation(header *block.Header, leader string) *HeaderInformation {
- if header == nil {
- return nil
- }
-
- result := &HeaderInformation{
- BlockHash: header.Hash(),
- BlockNumber: header.Number().Uint64(),
- ShardID: header.ShardID(),
- Leader: leader,
- ViewID: header.ViewID().Uint64(),
- Epoch: header.Epoch().Uint64(),
- UnixTime: header.Time().Uint64(),
- Timestamp: time.Unix(header.Time().Int64(), 0).UTC().String(),
- LastCommitBitmap: hex.EncodeToString(header.LastCommitBitmap()),
- }
-
- sig := header.LastCommitSignature()
- result.LastCommitSig = hex.EncodeToString(sig[:])
-
- if header.ShardID() == shard.BeaconChainShardID {
- decodedCrossLinks := &types.CrossLinks{}
- err := rlp.DecodeBytes(header.CrossLinks(), decodedCrossLinks)
- if err != nil {
- result.CrossLinks = &types.CrossLinks{}
- } else {
- result.CrossLinks = decodedCrossLinks
- }
- }
-
- return result
-}
-
-// newRPCCXReceipt returns a CXReceipt that will serialize to the RPC representation
-func newRPCCXReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uint64) *RPCCXReceipt {
- result := &RPCCXReceipt{
- BlockHash: blockHash,
- TxHash: cx.TxHash,
- Amount: (*hexutil.Big)(cx.Amount),
- ShardID: cx.ShardID,
- ToShardID: cx.ToShardID,
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
- }
-
- fromAddr, err := internal_common.AddressToBech32(cx.From)
- if err != nil {
- return nil
- }
- toAddr := ""
- if cx.To != nil {
- if toAddr, err = internal_common.AddressToBech32(*cx.To); err != nil {
- return nil
- }
- }
- result.From = fromAddr
- result.To = toAddr
-
- return result
-}
-
-// newRPCTransaction returns a transaction that will serialize to the RPC
-// representation, with the given location metadata set (if available).
-// Note that all txs on Harmony are replay protected (post EIP155 epoch).
-func newRPCTransaction(
- tx *types.Transaction, blockHash common.Hash,
- blockNumber uint64, timestamp uint64, index uint64,
-) *RPCTransaction {
- from, err := tx.SenderAddress()
- if err != nil {
- return nil
- }
- v, r, s := tx.RawSignatureValues()
-
- result := &RPCTransaction{
- Gas: hexutil.Uint64(tx.Gas()),
- GasPrice: (*hexutil.Big)(tx.GasPrice()),
- Hash: tx.Hash(),
- Input: hexutil.Bytes(tx.Data()),
- Nonce: hexutil.Uint64(tx.Nonce()),
- Value: (*hexutil.Big)(tx.Value()),
- ShardID: tx.ShardID(),
- ToShardID: tx.ToShardID(),
- Timestamp: hexutil.Uint64(timestamp),
- V: (*hexutil.Big)(v),
- R: (*hexutil.Big)(r),
- S: (*hexutil.Big)(s),
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
- result.TransactionIndex = hexutil.Uint(index)
- }
-
- fromAddr, err := internal_common.AddressToBech32(from)
- if err != nil {
- return nil
- }
- toAddr := ""
-
- if tx.To() != nil {
- if toAddr, err = internal_common.AddressToBech32(*tx.To()); err != nil {
- return nil
- }
- result.From = fromAddr
- } else {
- result.From = strings.ToLower(from.Hex())
- }
- result.To = toAddr
-
- return result
-}
-
-// newRPCStakingTransaction returns a staking transaction that will serialize to the RPC
-// representation, with the given location metadata set (if available).
-func newRPCStakingTransaction(tx *staking.StakingTransaction, blockHash common.Hash,
- blockNumber uint64, timestamp uint64, index uint64,
-) *RPCStakingTransaction {
- from, err := tx.SenderAddress()
- if err != nil {
- return nil
- }
- v, r, s := tx.RawSignatureValues()
-
- fields := map[string]interface{}{}
-
- switch tx.StakingType() {
- case staking.DirectiveCreateValidator:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCreateValidator)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.CreateValidator)
- if !ok {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "validatorAddress": validatorAddress,
- "commissionRate": (*hexutil.Big)(msg.CommissionRates.Rate.Int),
- "maxCommissionRate": (*hexutil.Big)(msg.CommissionRates.MaxRate.Int),
- "maxChangeRate": (*hexutil.Big)(msg.CommissionRates.MaxChangeRate.Int),
- "minSelfDelegation": (*hexutil.Big)(msg.MinSelfDelegation),
- "maxTotalDelegation": (*hexutil.Big)(msg.MaxTotalDelegation),
- "amount": (*hexutil.Big)(msg.Amount),
- "name": msg.Description.Name,
- "website": msg.Description.Website,
- "identity": msg.Description.Identity,
- "securityContact": msg.Description.SecurityContact,
- "details": msg.Description.Details,
- "slotPubKeys": msg.SlotPubKeys,
- }
- case staking.DirectiveEditValidator:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.EditValidator)
- if !ok {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- // Edit validators txs need not have commission rates to edit
- commissionRate := &hexutil.Big{}
- if msg.CommissionRate != nil {
- commissionRate = (*hexutil.Big)(msg.CommissionRate.Int)
- }
- fields = map[string]interface{}{
- "validatorAddress": validatorAddress,
- "commisionRate": commissionRate,
- "minSelfDelegation": (*hexutil.Big)(msg.MinSelfDelegation),
- "maxTotalDelegation": (*hexutil.Big)(msg.MaxTotalDelegation),
- "name": msg.Description.Name,
- "website": msg.Description.Website,
- "identity": msg.Description.Identity,
- "securityContact": msg.Description.SecurityContact,
- "details": msg.Description.Details,
- "slotPubKeyToAdd": msg.SlotKeyToAdd,
- "slotPubKeyToRemove": msg.SlotKeyToRemove,
- }
- case staking.DirectiveCollectRewards:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCollectRewards)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.CollectRewards)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- }
- case staking.DirectiveDelegate:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveDelegate)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.Delegate)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- "validatorAddress": validatorAddress,
- "amount": (*hexutil.Big)(msg.Amount),
- }
- case staking.DirectiveUndelegate:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.Undelegate)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- "validatorAddress": validatorAddress,
- "amount": (*hexutil.Big)(msg.Amount),
- }
- }
-
- result := &RPCStakingTransaction{
- Gas: hexutil.Uint64(tx.Gas()),
- GasPrice: (*hexutil.Big)(tx.GasPrice()),
- Hash: tx.Hash(),
- Nonce: hexutil.Uint64(tx.Nonce()),
- Timestamp: hexutil.Uint64(timestamp),
- V: (*hexutil.Big)(v),
- R: (*hexutil.Big)(r),
- S: (*hexutil.Big)(s),
- Type: tx.StakingType().String(),
- Msg: fields,
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
- result.TransactionIndex = hexutil.Uint(index)
- }
-
- fromAddr, err := internal_common.AddressToBech32(from)
- if err != nil {
- return nil
- }
- result.From = fromAddr
-
- return result
-}
-
-// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
-// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
-// transaction hashes.
-func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs, leader string) (map[string]interface{}, error) {
- head := b.Header() // copies the header once
- fields := map[string]interface{}{
- "number": (*hexutil.Big)(head.Number()),
- "viewID": (*hexutil.Big)(head.ViewID()),
- "epoch": (*hexutil.Big)(head.Epoch()),
- "hash": b.Hash(),
- "parentHash": head.ParentHash(),
- "nonce": 0, // Remove this because we don't have it in our header
- "mixHash": head.MixDigest(),
- "logsBloom": head.Bloom(),
- "stateRoot": head.Root(),
- "miner": leader,
- "difficulty": 0, // Remove this because we don't have it in our header
- "extraData": hexutil.Bytes(head.Extra()),
- "size": hexutil.Uint64(b.Size()),
- "gasLimit": hexutil.Uint64(head.GasLimit()),
- "gasUsed": hexutil.Uint64(head.GasUsed()),
- "timestamp": hexutil.Uint64(head.Time().Uint64()),
- "transactionsRoot": head.TxHash(),
- "receiptsRoot": head.ReceiptHash(),
- }
-
- if blockArgs.InclTx {
- formatTx := func(tx *types.Transaction) (interface{}, error) {
- return tx.Hash(), nil
- }
- if blockArgs.FullTx {
- formatTx = func(tx *types.Transaction) (interface{}, error) {
- return newRPCTransactionFromBlockHash(b, tx.Hash()), nil
- }
- }
- txs := b.Transactions()
- transactions := make([]interface{}, len(txs))
- var err error
- for i, tx := range txs {
- if transactions[i], err = formatTx(tx); err != nil {
- return nil, err
- }
- }
- fields["transactions"] = transactions
-
- if blockArgs.InclStaking {
- formatStakingTx := func(tx *staking.StakingTransaction) (interface{}, error) {
- return tx.Hash(), nil
- }
- if blockArgs.FullTx {
- formatStakingTx = func(tx *staking.StakingTransaction) (interface{}, error) {
- return newRPCStakingTransactionFromBlockHash(b, tx.Hash()), nil
- }
- }
- stakingTxs := b.StakingTransactions()
- stakingTransactions := make([]interface{}, len(stakingTxs))
- for i, tx := range stakingTxs {
- if stakingTransactions[i], err = formatStakingTx(tx); err != nil {
- return nil, err
- }
- }
- fields["stakingTransactions"] = stakingTransactions
- }
- }
-
- uncles := b.Uncles()
- uncleHashes := make([]common.Hash, len(uncles))
- for i, uncle := range uncles {
- uncleHashes[i] = uncle.Hash()
- }
- fields["uncles"] = uncleHashes
- if blockArgs.WithSigners {
- fields["signers"] = blockArgs.Signers
- }
- return fields, nil
-}
-
-// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
- for idx, tx := range b.Transactions() {
- if tx.Hash() == hash {
- return newRPCTransactionFromBlockIndex(b, uint64(idx))
- }
- }
- return nil
-}
-
-// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
- txs := b.Transactions()
- if index >= uint64(len(txs)) {
- return nil
- }
- return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
-}
-
-// newRPCStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
-func newRPCStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCStakingTransaction {
- for idx, tx := range b.StakingTransactions() {
- if tx.Hash() == hash {
- return newRPCStakingTransactionFromBlockIndex(b, uint64(idx))
- }
- }
- return nil
-}
-
-// newRPCStakingTransactionFromBlockIndex returns a staking transaction that will serialize to the RPC representation.
-func newRPCStakingTransactionFromBlockIndex(b *types.Block, index uint64) *RPCStakingTransaction {
- txs := b.StakingTransactions()
- if index >= uint64(len(txs)) {
- return nil
- }
- return newRPCStakingTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
-}
diff --git a/internal/hmyapi/apiv1/util.go b/internal/hmyapi/apiv1/util.go
deleted file mode 100644
index 4e545f6e6..000000000
--- a/internal/hmyapi/apiv1/util.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package apiv1
-
-import (
- "context"
-
- "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"
-)
-
-// defaultPageSize is to have default pagination.
-const (
- defaultPageSize = uint32(1000)
-)
-
-// ReturnWithPagination returns result with pagination (offset, page in TxHistoryArgs).
-func ReturnWithPagination(hashes []common.Hash, pageIndex uint32, pageSize uint32) []common.Hash {
- size := defaultPageSize
- if pageSize > 0 {
- size = pageSize
- }
- if uint64(size)*uint64(pageIndex) >= uint64(len(hashes)) {
- return make([]common.Hash, 0)
- }
- if uint64(size)*uint64(pageIndex)+uint64(size) > uint64(len(hashes)) {
- return hashes[size*pageIndex:]
- }
- return hashes[size*pageIndex : size*pageIndex+size]
-}
-
-// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
-func SubmitTransaction(
- ctx context.Context, hmy *hmy.Harmony, tx *types.Transaction,
-) (common.Hash, error) {
- 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(hmy.ChainConfig(), hmy.CurrentBlock().Epoch())
- from, err := types.Sender(signer, tx)
- if err != nil {
- return common.Hash{}, err
- }
- addr := crypto.CreateAddress(from, tx.Nonce())
- utils.Logger().Info().
- Str("fullhash", tx.Hash().Hex()).
- Str("contract", common2.MustAddressToBech32(addr)).
- Msg("Submitted contract creation")
- } else {
- utils.Logger().Info().
- Str("fullhash", tx.Hash().Hex()).
- Str("recipient", tx.To().Hex()).
- Msg("Submitted transaction")
- }
- return tx.Hash(), nil
-}
-
-// SubmitStakingTransaction is a helper function that submits tx to txPool and logs a message.
-func SubmitStakingTransaction(
- ctx context.Context, hmy *hmy.Harmony, tx *staking.StakingTransaction,
-) (common.Hash, error) {
- if err := hmy.SendStakingTx(ctx, tx); err != nil {
- utils.Logger().Warn().Err(err).Msg("Could not submit staking transaction")
- return tx.Hash(), err
- }
- utils.Logger().Info().Str("fullhash", tx.Hash().Hex()).Msg("Submitted Staking transaction")
- return tx.Hash(), nil
-}
diff --git a/internal/hmyapi/apiv2/addrlock.go b/internal/hmyapi/apiv2/addrlock.go
deleted file mode 100644
index 468333b87..000000000
--- a/internal/hmyapi/apiv2/addrlock.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package apiv2
-
-import (
- "sync"
-
- "github.com/ethereum/go-ethereum/common"
-)
-
-// AddrLocker ...
-type AddrLocker struct {
- mu sync.Mutex
- locks map[common.Address]*sync.Mutex
-}
-
-// lock returns the lock of the given address.
-func (l *AddrLocker) lock(address common.Address) *sync.Mutex {
- l.mu.Lock()
- defer l.mu.Unlock()
- if l.locks == nil {
- l.locks = make(map[common.Address]*sync.Mutex)
- }
- if _, ok := l.locks[address]; !ok {
- l.locks[address] = new(sync.Mutex)
- }
- return l.locks[address]
-}
-
-// LockAddr locks an account's mutex. This is used to prevent another tx getting the
-// same nonce until the lock is released. The mutex prevents the (an identical nonce) from
-// being read again during the time that the first transaction is being signed.
-func (l *AddrLocker) LockAddr(address common.Address) {
- l.lock(address).Lock()
-}
-
-// UnlockAddr unlocks the mutex of the given account.
-func (l *AddrLocker) UnlockAddr(address common.Address) {
- l.lock(address).Unlock()
-}
diff --git a/internal/hmyapi/apiv2/blockchain.go b/internal/hmyapi/apiv2/blockchain.go
deleted file mode 100644
index e579a9ad3..000000000
--- a/internal/hmyapi/apiv2/blockchain.go
+++ /dev/null
@@ -1,987 +0,0 @@
-package apiv2
-
-import (
- "context"
- "fmt"
- "math/big"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/harmony-one/harmony/block"
- "github.com/harmony-one/harmony/common/denominations"
- "github.com/harmony-one/harmony/consensus/quorum"
- "github.com/harmony-one/harmony/consensus/reward"
- "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"
- "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/network"
- staking "github.com/harmony-one/harmony/staking/types"
- "github.com/pkg/errors"
-)
-
-const (
- defaultGasPrice = denominations.Nano
- defaultFromAddress = "0x0000000000000000000000000000000000000000"
- defaultBlocksPeriod = 15000
- validatorsPageSize = 100
- initSupply = int64(12600000000)
-)
-
-// 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 {
- hmy *hmy.Harmony
-}
-
-// NewPublicBlockChainAPI creates a new Harmony blockchain API.
-func NewPublicBlockChainAPI(hmy *hmy.Harmony) *PublicBlockChainAPI {
- return &PublicBlockChainAPI{hmy}
-}
-
-// BlockArgs is struct to include optional block formatting params.
-type BlockArgs struct {
- WithSigners bool `json:"withSigners"`
- InclTx bool `json:"inclTx"`
- FullTx bool `json:"fullTx"`
- Signers []string `json:"signers"`
- InclStaking bool `json:"inclStaking"`
-}
-
-func (s *PublicBlockChainAPI) isBeaconShard() error {
- if s.hmy.ShardID != shard.BeaconChainShardID {
- return ErrNotBeaconShard
- }
- return nil
-}
-
-func (s *PublicBlockChainAPI) isBlockGreaterThanLatest(blockNum uint64) error {
- if blockNum > s.hmy.CurrentBlock().NumberU64() {
- return ErrRequestedBlockTooHigh
- }
- return nil
-}
-
-// 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, blockNum uint64, blockArgs BlockArgs) (map[string]interface{}, error) {
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
- blockArgs.InclTx = true
- if blk != nil {
- if blockArgs.WithSigners {
- blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNum)
- if err != nil {
- return nil, err
- }
- }
- 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
- }
- }
- return response, err
- }
- return nil, err
-}
-
-// GetBlockByHash 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) GetBlockByHash(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) {
- blk, err := s.hmy.GetBlock(ctx, blockHash)
- blockArgs.InclTx = true
- if blk != nil {
- if blockArgs.WithSigners {
- blockArgs.Signers, err = s.GetBlockSigners(ctx, blk.NumberU64())
- if err != nil {
- return nil, err
- }
- }
- leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
- return RPCMarshalBlock(blk, blockArgs, leader)
- }
- return nil, err
-}
-
-// GetBlocks method returns blocks in range blockStart, blockEnd just like GetBlockByNumber but all at once.
-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++ {
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(i))
- blockArgs.InclTx = true
- if blockArgs.WithSigners {
- blockArgs.Signers, err = s.GetBlockSigners(ctx, i)
- if err != nil {
- return nil, err
- }
- }
- 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"} {
- rpcBlock[field] = nil
- }
- }
- result = append(result, rpcBlock)
- }
- }
- return result, nil
-}
-
-// GetValidators returns validators list for a particular epoch.
-func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) {
- cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
- if err != nil {
- return nil, err
- }
- balanceQueryBlock := shard.Schedule.EpochLastBlock(uint64(epoch))
- if balanceQueryBlock > s.hmy.CurrentBlock().NumberU64() {
- balanceQueryBlock = s.hmy.CurrentBlock().NumberU64()
- }
- validators := make([]map[string]interface{}, 0)
- 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.hmy.GetBalance(ctx, addr, rpc.BlockNumber(balanceQueryBlock))
- if err != nil {
- return nil, err
- }
- validatorsFields := map[string]interface{}{
- "address": oneAddress,
- "balance": validatorBalance,
- }
- validators = append(validators, validatorsFields)
- }
- result := map[string]interface{}{
- "shardID": cmt.ShardID,
- "validators": validators,
- }
- return result, nil
-}
-
-// 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) {
- cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
- if err != nil {
- return nil, err
- }
-
- validators := make([]string, len(cmt.Slots))
- for i, v := range cmt.Slots {
- validators[i] = v.BLSPublicKey.Hex()
- }
- return validators, nil
-}
-
-// IsLastBlock checks if block is last epoch block.
-func (s *PublicBlockChainAPI) IsLastBlock(blockNum uint64) (bool, error) {
- if err := s.isBeaconShard(); err != nil {
- return false, err
- }
- return shard.Schedule.IsLastBlock(blockNum), nil
-}
-
-// EpochLastBlock returns epoch last block.
-func (s *PublicBlockChainAPI) EpochLastBlock(epoch uint64) (uint64, error) {
- if err := s.isBeaconShard(); err != nil {
- return 0, err
- }
- return shard.Schedule.EpochLastBlock(epoch), nil
-}
-
-// GetBlockSigners returns signers for a particular block.
-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(blockNum); err != nil {
- return nil, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum))
- if err != nil {
- return nil, err
- }
- signers := []string{}
- for _, validator := range slots {
- oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
- if err != nil {
- return nil, err
- }
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- signers = append(signers, oneAddress)
- }
- }
- return signers, nil
-}
-
-// GetBlockSignerKeys returns bls public keys that signed the block.
-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(blockNum); err != nil {
- return nil, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum))
- if err != nil {
- return nil, err
- }
- signers := []string{}
- for _, validator := range slots {
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- signers = append(signers, validator.BLSPublicKey.Hex())
- }
- }
- return signers, nil
-}
-
-// 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(blockNum); err != nil {
- return false, err
- }
- slots, mask, err := s.hmy.GetBlockSigners(ctx, rpc.BlockNumber(blockNum))
- if err != nil {
- return false, err
- }
- for _, validator := range slots {
- oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
- if err != nil {
- return false, err
- }
- if oneAddress != address {
- continue
- }
- if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
- return true, nil
- }
- }
- return false, nil
-}
-
-// GetSignedBlocks returns how many blocks a particular validator signed for last defaultBlocksPeriod (3 hours ~ 1500 blocks).
-func (s *PublicBlockChainAPI) GetSignedBlocks(ctx context.Context, address string) uint64 {
- totalSigned := uint64(0)
- lastBlock := uint64(0)
- blockHeight := uint64(s.BlockNumber())
- if blockHeight >= defaultBlocksPeriod {
- lastBlock = blockHeight - defaultBlocksPeriod + 1
- }
- for i := lastBlock; i <= blockHeight; i++ {
- signed, err := s.IsBlockSigner(ctx, i, address)
- if err == nil && signed {
- totalSigned++
- }
- }
- return totalSigned
-}
-
-// GetEpoch returns current epoch.
-func (s *PublicBlockChainAPI) GetEpoch(ctx context.Context) uint64 {
- return s.LatestHeader(ctx).Epoch
-}
-
-// GetLeader returns current shard leader.
-func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string {
- return s.LatestHeader(ctx).Leader
-}
-
-// GetValidatorSelfDelegation returns validator stake.
-func (s *PublicBlockChainAPI) GetValidatorSelfDelegation(ctx context.Context, address string) (*big.Int, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetValidatorSelfDelegation(internal_common.ParseAddr(address)), nil
-}
-
-// GetValidatorTotalDelegation returns total balace stacking for validator with delegation.
-func (s *PublicBlockChainAPI) GetValidatorTotalDelegation(ctx context.Context, address string) (*big.Int, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- delegations := s.hmy.GetDelegationsByValidator(internal_common.ParseAddr(address))
- totalStake := big.NewInt(0)
- for _, delegation := range delegations {
- totalStake.Add(totalStake, delegation.Amount)
- }
- return totalStake, nil
-}
-
-// GetShardingStructure returns an array of sharding structures.
-func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[string]interface{}, error) {
- // Get header and number of shards.
- epoch := s.GetEpoch(ctx)
- numShard := shard.Schedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards()
-
- // Return shareding structure for each case.
- 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.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, blockNum uint64) (hexutil.Bytes, error) {
- address := internal_common.ParseAddr(addr)
- state, _, err := s.hmy.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNum))
- if state == nil || err != nil {
- return nil, err
- }
- code := state.GetCode(address)
- return code, state.Error()
-}
-
-// 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, blockNum uint64) (hexutil.Bytes, error) {
- address := internal_common.ParseAddr(addr)
- state, _, err := s.hmy.StateAndHeaderByNumber(ctx, rpc.BlockNumber(blockNum))
- if state == nil || err != nil {
- return nil, err
- }
- res := state.GetState(address, common.HexToHash(key))
- return res[:], state.Error()
-}
-
-// GetBalanceByBlockNumber returns balance by block number.
-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.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.hmy.GetAccountNonce(ctx, addr, blockNr)
-}
-
-// GetBalance returns the amount of Atto for the given address in the state of the
-// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
-// block numbers are also allowed.
-func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address string) (*big.Int, error) {
- addr := internal_common.ParseAddr(address)
- 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.hmy.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available
- return header.Number().Uint64()
-}
-
-// ResendCx requests that the egress receipt for the given cross-shard
-// transaction be sent to the destination shard for credit. This is used for
-// unblocking a half-complete cross-shard transaction whose fund has been
-// 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.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.hmy, args, rpc.BlockNumber(blockNr), vm.Config{}, 5*time.Second, s.hmy.RPCGasCap)
- return (hexutil.Bytes)(result), err
-}
-
-// LatestHeader returns the latest header information
-func (s *PublicBlockChainAPI) LatestHeader(ctx context.Context) *HeaderInformation {
- 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)
-}
-
-// GetHeaderByNumber returns block header at given number
-func (s *PublicBlockChainAPI) GetHeaderByNumber(ctx context.Context, blockNum uint64) (*HeaderInformation, error) {
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- header, err := s.hmy.HeaderByNumber(context.Background(), rpc.BlockNumber(blockNum))
- if err != nil {
- return nil, err
- }
- leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch())
- return newHeaderInformation(header, leader), nil
-}
-
-// GetTotalStaking returns total staking by validators, only meant to be called on beaconchain
-// explorer node
-func (s *PublicBlockChainAPI) GetTotalStaking() (*big.Int, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetTotalStakingSnapshot(), nil
-}
-
-// GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain
-// explorer node
-func (s *PublicBlockChainAPI) GetMedianRawStakeSnapshot() (
- *committee.CompletedEPoSRound, error,
-) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetMedianRawStakeSnapshot()
-}
-
-// GetAllValidatorAddresses returns all validator addresses.
-func (s *PublicBlockChainAPI) GetAllValidatorAddresses() ([]string, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- validatorAddresses := s.hmy.GetAllValidatorAddresses()
- addresses := make([]string, len(validatorAddresses))
- for i, addr := range validatorAddresses {
- oneAddr, _ := internal_common.AddressToBech32(addr)
- addresses[i] = oneAddr
- }
- return addresses, nil
-}
-
-// GetElectedValidatorAddresses returns elected validator addresses.
-func (s *PublicBlockChainAPI) GetElectedValidatorAddresses() ([]string, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- electedAddresses := s.hmy.GetElectedValidatorAddresses()
- addresses := make([]string, len(electedAddresses))
- for i, addr := range electedAddresses {
- oneAddr, _ := internal_common.AddressToBech32(addr)
- addresses[i] = oneAddr
- }
- return addresses, nil
-}
-
-// GetValidatorInformation ..
-func (s *PublicBlockChainAPI) GetValidatorInformation(
- ctx context.Context, address string,
-) (*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(rpc.LatestBlockNumber))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the latest blk information")
- }
- return s.hmy.GetValidatorInformation(
- internal_common.ParseAddr(address), blk,
- )
-}
-
-// GetValidatorInformationByBlockNumber ..
-func (s *PublicBlockChainAPI) GetValidatorInformationByBlockNumber(
- ctx context.Context, address string, blockNr uint64,
-) (*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNr); err != nil {
- return nil, err
- }
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNr)
- }
- return s.hmy.GetValidatorInformation(
- internal_common.ParseAddr(address), blk,
- )
-}
-
-func (s *PublicBlockChainAPI) getAllValidatorInformation(
- ctx context.Context, page int, blockNr rpc.BlockNumber,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if page < -1 {
- return nil, errors.Errorf("page given %d cannot be less than -1", page)
- }
- addresses := s.hmy.GetAllValidatorAddresses()
- if page != -1 && len(addresses) <= page*validatorsPageSize {
- return make([]*staking.ValidatorRPCEnhanced, 0), nil
- }
- validatorsNum := len(addresses)
- start := 0
- if page != -1 {
- validatorsNum = validatorsPageSize
- start = page * validatorsPageSize
- if len(addresses)-start < validatorsPageSize {
- validatorsNum = len(addresses) - start
- }
- }
- validators := []*staking.ValidatorRPCEnhanced{}
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNr))
- if err != nil {
- 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.hmy.GetValidatorInformation(addresses[i], blk)
- if err == nil {
- validators = append(validators, information)
- }
- }
- return validators, nil
-}
-
-// GetAllValidatorInformation returns information about all validators.
-// If page is -1, return all else return the pagination.
-func (s *PublicBlockChainAPI) GetAllValidatorInformation(
- ctx context.Context, page int,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
-
- blockNr := s.hmy.CurrentBlock().NumberU64()
-
- // delete cache for previous block
- prevKey := fmt.Sprintf("all-info-%d", blockNr-1)
- s.hmy.SingleFlightForgetKey(prevKey)
-
- key := fmt.Sprintf("all-info-%d", blockNr)
- res, err := s.hmy.SingleFlightRequest(
- key,
- func() (interface{}, error) {
- return s.getAllValidatorInformation(ctx, page, rpc.LatestBlockNumber)
- },
- )
- if err != nil {
- return nil, err
- }
- return res.([]*staking.ValidatorRPCEnhanced), nil
-
-}
-
-// GetAllValidatorInformationByBlockNumber returns information about all validators.
-// If page is -1, return all else return the pagination.
-func (s *PublicBlockChainAPI) GetAllValidatorInformationByBlockNumber(
- ctx context.Context, page int, blockNr uint64,
-) ([]*staking.ValidatorRPCEnhanced, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNr); err != nil {
- return nil, err
- }
- return s.getAllValidatorInformation(ctx, page, rpc.BlockNumber(blockNr))
-}
-
-// GetAllDelegationInformation returns delegation information about `validatorsPageSize` validators,
-// starting at `page*validatorsPageSize`.
-// If page is -1, return all instead of `validatorsPageSize` elements.
-func (s *PublicBlockChainAPI) GetAllDelegationInformation(ctx context.Context, page int) ([][]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
-
- if page < -1 {
- return make([][]*RPCDelegation, 0), nil
- }
- addresses := s.hmy.GetAllValidatorAddresses()
- if page != -1 && len(addresses) <= page*validatorsPageSize {
- return make([][]*RPCDelegation, 0), nil
- }
- validatorsNum := len(addresses)
- start := 0
- if page != -1 {
- validatorsNum = validatorsPageSize
- start = page * validatorsPageSize
- if len(addresses)-start < validatorsPageSize {
- validatorsNum = len(addresses) - start
- }
- }
- validators := make([][]*RPCDelegation, validatorsNum)
- var err error
- for i := start; i < start+validatorsNum; i++ {
- validators[i-start], err = s.GetDelegationsByValidator(ctx, addresses[i].String())
- if err != nil {
- return nil, err
- }
- }
- return validators, nil
-}
-
-// GetDelegationsByDelegator returns list of delegations for a delegator address.
-func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, address string) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
- result := []*RPCDelegation{}
- for i := range delegations {
- delegation := delegations[i]
-
- undelegations := make([]RPCUndelegation, len(delegation.Undelegations))
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validators[i])
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- result = append(result, &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- })
- }
- return result, nil
-}
-
-// GetDelegationsByDelegatorByBlockNumber returns list of delegations for a delegator address at given block number
-func (s *PublicBlockChainAPI) GetDelegationsByDelegatorByBlockNumber(
- ctx context.Context, address string, blockNum uint64,
-) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- if err := s.isBlockGreaterThanLatest(blockNum); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- blk, err := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
- if err != nil {
- return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum)
- }
- validators, delegations := s.hmy.GetDelegationsByDelegatorByBlock(delegatorAddress, blk)
- result := make([]*RPCDelegation, len(delegations))
- for i := range delegations {
- delegation := delegations[i]
-
- undelegations := make([]RPCUndelegation, len(delegation.Undelegations))
-
- for j := range delegation.Undelegations {
- undelegations[j] = RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- }
- }
- valAddr, _ := internal_common.AddressToBech32(validators[i])
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- result[i] = &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- }
- }
- return result, nil
-}
-
-// GetDelegationsByValidator returns list of delegations for a validator address.
-func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, address string) ([]*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- validatorAddress := internal_common.ParseAddr(address)
- delegations := s.hmy.GetDelegationsByValidator(validatorAddress)
- result := make([]*RPCDelegation, 0)
- for _, delegation := range delegations {
-
- undelegations := []RPCUndelegation{}
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validatorAddress)
- delAddr, _ := internal_common.AddressToBech32(delegation.DelegatorAddress)
- result = append(result, &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- })
- }
- return result, nil
-}
-
-// GetDelegationByDelegatorAndValidator returns a delegation for delegator and validator.
-func (s *PublicBlockChainAPI) GetDelegationByDelegatorAndValidator(ctx context.Context, address string, validator string) (*RPCDelegation, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- delegatorAddress := internal_common.ParseAddr(address)
- validatorAddress := internal_common.ParseAddr(validator)
- validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
- for i := range delegations {
- if validators[i] != validatorAddress {
- continue
- }
- delegation := delegations[i]
-
- undelegations := []RPCUndelegation{}
-
- for j := range delegation.Undelegations {
- undelegations = append(undelegations, RPCUndelegation{
- delegation.Undelegations[j].Amount,
- delegation.Undelegations[j].Epoch,
- })
- }
- valAddr, _ := internal_common.AddressToBech32(validatorAddress)
- delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
- return &RPCDelegation{
- valAddr,
- delAddr,
- delegation.Amount,
- delegation.Reward,
- undelegations,
- }, nil
- }
- return nil, 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.hmy, args, nil)
-}
-
-// GetCurrentUtilityMetrics ..
-func (s *PublicBlockChainAPI) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetCurrentUtilityMetrics()
-}
-
-// GetSuperCommittees ..
-func (s *PublicBlockChainAPI) GetSuperCommittees() (*quorum.Transition, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetSuperCommittees()
-}
-
-// GetCurrentBadBlocks ..
-func (s *PublicBlockChainAPI) GetCurrentBadBlocks() []core.BadBlock {
- return s.hmy.GetCurrentBadBlocks()
-}
-
-// GetTotalSupply ..
-func (s *PublicBlockChainAPI) GetTotalSupply() (numeric.Dec, error) {
- return numeric.NewDec(initSupply), nil
-}
-
-// GetCirculatingSupply ..
-func (s *PublicBlockChainAPI) GetCirculatingSupply() (numeric.Dec, error) {
- timestamp := time.Now()
- return numeric.NewDec(initSupply).Mul(reward.PercentageForTimeStamp(timestamp.Unix())), nil
-}
-
-// GetStakingNetworkInfo ..
-func (s *PublicBlockChainAPI) GetStakingNetworkInfo(
- ctx context.Context,
-) (*StakingNetworkInfo, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- totalStaking, _ := s.GetTotalStaking()
- round, _ := s.GetMedianRawStakeSnapshot()
- epoch := s.GetEpoch(ctx)
- epochLastBlock, _ := s.EpochLastBlock(epoch)
- totalSupply, _ := s.GetTotalSupply()
- circulatingSupply, _ := s.GetCirculatingSupply()
- return &StakingNetworkInfo{
- TotalSupply: totalSupply,
- CirculatingSupply: circulatingSupply,
- EpochLastBlock: epochLastBlock,
- TotalStaking: totalStaking,
- MedianRawStake: round.MedianStake,
- }, nil
-}
-
-// GetLastCrossLinks ..
-func (s *PublicBlockChainAPI) GetLastCrossLinks() ([]*types.CrossLink, error) {
- if err := s.isBeaconShard(); err != nil {
- return nil, err
- }
- return s.hmy.GetLastCrossLinks()
-}
-
-// GetLatestChainHeaders ..
-func (s *PublicBlockChainAPI) GetLatestChainHeaders() *block.HeaderPair {
- return s.hmy.GetLatestChainHeaders()
-}
-
-// docall executes an EVM call
-func doCall(ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) ([]byte, uint64, bool, error) {
- defer func(start time.Time) {
- utils.Logger().Debug().
- Dur("runtime", time.Since(start)).
- Msg("Executing EVM call finished")
- }(time.Now())
-
- state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNr)
- if state == nil || err != nil {
- return nil, 0, false, err
- }
- // Set sender address or use a default if none specified
- var addr common.Address
- if args.From == nil {
- // Any address does not affect the logic of this call.
- addr = common.HexToAddress(defaultFromAddress)
- } else {
- addr = *args.From
- }
- // Set default gas & gas price if none were set
- gas := uint64(math.MaxUint64 / 2)
- if args.Gas != nil {
- gas = uint64(*args.Gas)
- }
- if globalGasCap != nil && globalGasCap.Uint64() < gas {
- utils.Logger().Warn().
- Uint64("requested", gas).
- Uint64("cap", globalGasCap.Uint64()).
- Msg("Caller gas above allowance, capping")
- gas = globalGasCap.Uint64()
- }
- gasPrice := new(big.Int).SetUint64(defaultGasPrice)
- if args.GasPrice != nil {
- gasPrice = args.GasPrice.ToInt()
- }
-
- value := new(big.Int)
- if args.Value != nil {
- value = args.Value.ToInt()
- }
-
- var data []byte
- if args.Data != nil {
- data = []byte(*args.Data)
- }
-
- // Create new call message
- msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false)
-
- // Setup context so it may be cancelled the call has completed
- // or, in case of unmetered gas, setup a context with a timeout.
- var cancel context.CancelFunc
- if timeout > 0 {
- ctx, cancel = context.WithTimeout(ctx, timeout)
- } else {
- ctx, cancel = context.WithCancel(ctx)
- }
- // Make sure the context is cancelled when the call has completed
- // this makes sure resources are cleaned up.
- defer cancel()
-
- // Get a new instance of the EVM.
- evm, vmError, err := hmy.GetEVM(ctx, msg, state, header)
- if err != nil {
- return nil, 0, false, err
- }
- // Wait for the context to be done and cancel the evm. Even if the
- // EVM has finished, cancelling may be done (repeatedly)
- go func() {
- <-ctx.Done()
- evm.Cancel()
- }()
-
- // Setup the gas pool (also for unmetered requests)
- // and apply the message.
- gp := new(core.GasPool).AddGas(math.MaxUint64)
- res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
- if err := vmError(); err != nil {
- return nil, 0, false, err
- }
- // If the timer caused an abort, return an appropriate error message
- if evm.Cancelled() {
- return nil, 0, false, fmt.Errorf("execution aborted (timeout = %v)", timeout)
- }
- return res, gas, failed, err
-}
-
-// doEstimateGas ..
-func doEstimateGas(ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int) (hexutil.Uint64, error) {
- // Binary search the gas requirement, as it may be higher than the amount used
- var (
- lo = params.TxGas - 1
- hi uint64
- max uint64
- )
- blockNum := rpc.LatestBlockNumber
- if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
- hi = uint64(*args.Gas)
- } else {
- // Retrieve the blk to act as the gas ceiling
- blk, err := hmy.BlockByNumber(ctx, blockNum)
- if err != nil {
- return 0, err
- }
- hi = blk.GasLimit()
- }
- if gasCap != nil && hi > gasCap.Uint64() {
- // log.Warn("Caller gas above allowance, capping", "requested", hi, "max", gasCap)
- hi = gasCap.Uint64()
- }
- max = hi
-
- // Use zero-address if none other is available
- if args.From == nil {
- args.From = &common.Address{}
- }
- // Create a helper to check if a gas allowance results in an executable transaction
- executable := func(gas uint64) bool {
- args.Gas = (*hexutil.Uint64)(&gas)
-
- _, _, failed, err := doCall(ctx, hmy, args, blockNum, vm.Config{}, 0, big.NewInt(int64(max)))
- if err != nil || failed {
- return false
- }
- return true
- }
- // Execute the binary search and hone in on an executable gas limit
- for lo+1 < hi {
- mid := (hi + lo) / 2
- if !executable(mid) {
- lo = mid
- } else {
- hi = mid
- }
- }
- // Reject the transaction as invalid if it still fails at the highest allowance
- if hi == max {
- if !executable(hi) {
- return 0, fmt.Errorf("gas required exceeds allowance (%d) or always failing transaction", max)
- }
- }
- return hexutil.Uint64(hi), nil
-}
diff --git a/internal/hmyapi/apiv2/debug.go b/internal/hmyapi/apiv2/debug.go
deleted file mode 100644
index 5eb37effc..000000000
--- a/internal/hmyapi/apiv2/debug.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package apiv2
-
-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 {
- hmy *hmy.Harmony
-}
-
-// NewDebugAPI Creates a new DebugAPI instance
-func NewDebugAPI(hmy *hmy.Harmony) *DebugAPI {
- return &DebugAPI{hmy}
-}
-
-// SetLogVerbosity Sets log verbosity on runtime
-// Example usage:
-// curl -H "Content-Type: application/json" -d '{"method":"debug_setLogVerbosity","params":[0],"id":1}' http://localhost:9123
-func (*DebugAPI) SetLogVerbosity(ctx context.Context, level int) (map[string]interface{}, error) {
- if level < int(log.LvlCrit) || level > int(log.LvlTrace) {
- return nil, ErrInvalidLogLevel
- }
-
- verbosity := log.Lvl(level)
- utils.SetLogVerbosity(verbosity)
- return map[string]interface{}{"verbosity": verbosity.String()}, nil
-}
diff --git a/internal/hmyapi/apiv2/harmony.go b/internal/hmyapi/apiv2/harmony.go
deleted file mode 100644
index 276f3121a..000000000
--- a/internal/hmyapi/apiv2/harmony.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package apiv2
-
-import (
- "context"
- "math/big"
-
- "github.com/harmony-one/harmony/hmy"
- commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common"
- "github.com/harmony-one/harmony/internal/params"
-)
-
-// 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 {
- hmy *hmy.Harmony
-}
-
-// NewPublicHarmonyAPI ...
-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 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
-// yet received the latest block headers from its pears. In case it is synchronizing:
-// - startingBlock: block number this node started to synchronise from
-// - currentBlock: block number this node is currently importing
-// - highestBlock: block number of the highest block header this node has received from peers
-// - 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(dm): find our Downloader module for syncing blocks
- return false, nil
-}
-
-// GasPrice returns a suggestion for a gas price.
-func (s *PublicHarmonyAPI) GasPrice(ctx context.Context) (*big.Int, error) {
- // TODO(ricl): add SuggestPrice API
- return big.NewInt(1), nil
-}
-
-// NodeMetadata captures select metadata of the RPC answering node
-type NodeMetadata struct {
- BLSPublicKey []string `json:"blskey"`
- Version string `json:"version"`
- NetworkType string `json:"network"`
- ChainConfig params.ChainConfig `json:"chain-config"`
- IsLeader bool `json:"is-leader"`
- ShardID uint32 `json:"shard-id"`
- CurrentEpoch uint64 `json:"current-epoch"`
- BlocksPerEpoch *uint64 `json:"blocks-per-epoch,omitempty"`
- Role string `json:"role"`
- DNSZone string `json:"dns-zone"`
- Archival bool `json:"is-archival"`
-}
-
-// GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node
-func (s *PublicHarmonyAPI) GetNodeMetadata() commonRPC.NodeMetadata {
- return s.hmy.GetNodeMetadata()
-}
-
-// GetPeerInfo produces a NodePeerInfo record, containing peer info of the node
-func (s *PublicHarmonyAPI) GetPeerInfo() commonRPC.NodePeerInfo {
- return s.hmy.GetPeerInfo()
-}
diff --git a/internal/hmyapi/apiv2/net.go b/internal/hmyapi/apiv2/net.go
deleted file mode 100644
index 8fc979621..000000000
--- a/internal/hmyapi/apiv2/net.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package apiv2
-
-import (
- "fmt"
-
- "github.com/harmony-one/harmony/p2p"
-)
-
-// PublicNetAPI offers network related RPC methods
-type PublicNetAPI struct {
- net p2p.Host
- chainID uint64
-}
-
-// NewPublicNetAPI creates a new net API instance.
-func NewPublicNetAPI(net p2p.Host, chainID uint64) *PublicNetAPI {
- return &PublicNetAPI{net, chainID}
-}
-
-// PeerCount returns the number of connected peers
-func (s *PublicNetAPI) PeerCount() int {
- return s.net.GetPeerCount()
-}
-
-// Version returns the network version, i.e. ChainID identifying which network we are using
-func (s *PublicNetAPI) Version() string {
- return fmt.Sprintf("%d", s.chainID)
-}
diff --git a/internal/hmyapi/apiv2/transactionpool.go b/internal/hmyapi/apiv2/transactionpool.go
deleted file mode 100644
index 51347bd80..000000000
--- a/internal/hmyapi/apiv2/transactionpool.go
+++ /dev/null
@@ -1,432 +0,0 @@
-package apiv2
-
-import (
- "context"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/rlp"
- "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"
- "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"
-)
-
-// TxHistoryArgs is struct to make GetTransactionsHistory request
-type TxHistoryArgs struct {
- Address string `json:"address"`
- PageIndex uint32 `json:"pageIndex"`
- PageSize uint32 `json:"pageSize"`
- FullTx bool `json:"fullTx"`
- TxType string `json:"txType"`
- Order string `json:"order"`
-}
-
-// PublicTransactionPoolAPI exposes methods for the RPC interface
-type PublicTransactionPoolAPI struct {
- hmy *hmy.Harmony
- nonceLock *AddrLocker
-}
-
-// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool.
-func NewPublicTransactionPoolAPI(hmy *hmy.Harmony, nonceLock *AddrLocker) *PublicTransactionPoolAPI {
- return &PublicTransactionPoolAPI{hmy, nonceLock}
-}
-
-// GetTransactionsHistory returns the list of transactions hashes that involve a particular address.
-func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, args TxHistoryArgs) (map[string]interface{}, error) {
- var address string
- var result []common.Hash
- var err error
- if strings.HasPrefix(args.Address, "one1") {
- address = args.Address
- } else {
- addr := internal_common.ParseAddr(args.Address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return nil, err
- }
- }
- hashes, err := s.hmy.GetTransactionsHistory(address, args.TxType, args.Order)
- if err != nil {
- return nil, err
- }
- result = ReturnWithPagination(hashes, args.PageIndex, args.PageSize)
- if !args.FullTx {
- return map[string]interface{}{"transactions": result}, nil
- }
- txs := []*RPCTransaction{}
- for _, hash := range result {
- tx := s.GetTransactionByHash(ctx, hash)
- if tx != nil {
- txs = append(txs, tx)
- }
- }
- return map[string]interface{}{"transactions": txs}, nil
-}
-
-// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
-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
-}
-
-// 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.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, blockNum uint64, index uint64) *RPCTransaction {
- if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil {
- return newRPCTransactionFromBlockIndex(block, index)
- }
- return nil
-}
-
-// 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.hmy.GetBlock(ctx, blockHash); block != nil {
- return newRPCTransactionFromBlockIndex(block, index)
- }
- return nil
-}
-
-// 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.hmy.ChainDb(), hash)
- block, _ := s.hmy.GetBlock(ctx, blockHash)
- if block == nil {
- return nil
- }
- if tx != nil {
- return newRPCTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index)
- }
- // Transaction unknown, return as such
- return nil
-}
-
-// GetStakingTransactionsHistory returns the list of transactions hashes that involve a particular address.
-func (s *PublicTransactionPoolAPI) GetStakingTransactionsHistory(ctx context.Context, args TxHistoryArgs) (map[string]interface{}, error) {
- var address string
- var result []common.Hash
- var err error
- if strings.HasPrefix(args.Address, "one1") {
- address = args.Address
- } else {
- addr := internal_common.ParseAddr(args.Address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return nil, err
- }
- }
- hashes, err := s.hmy.GetStakingTransactionsHistory(address, args.TxType, args.Order)
- if err != nil {
- return nil, err
- }
- result = ReturnWithPagination(hashes, args.PageIndex, args.PageSize)
- if !args.FullTx {
- return map[string]interface{}{"staking_transactions": result}, nil
- }
- txs := []*RPCStakingTransaction{}
- for _, hash := range result {
- tx := s.GetStakingTransactionByHash(ctx, hash)
- if tx != nil {
- txs = append(txs, tx)
- }
- }
- return map[string]interface{}{"staking_transactions": txs}, nil
-}
-
-// GetBlockStakingTransactionCountByNumber returns the number of staking transactions in the block with the given block number.
-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
-}
-
-// 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.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, blockNum uint64, index uint64) *RPCStakingTransaction {
- if block, _ := s.hmy.BlockByNumber(ctx, rpc.BlockNumber(blockNum)); block != nil {
- return newRPCStakingTransactionFromBlockIndex(block, index)
- }
- return nil
-}
-
-// 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.hmy.GetBlock(ctx, blockHash); block != nil {
- return newRPCStakingTransactionFromBlockIndex(block, index)
- }
- return nil
-}
-
-// 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.hmy.ChainDb(), hash)
- block, _ := s.hmy.GetBlock(ctx, blockHash)
- if block == nil {
- return nil
- }
- if stx != nil {
- return newRPCStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index)
- }
- // Transaction unknown, return as such
- return nil
-}
-
-// GetTransactionsCount returns the number of regular transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
-func (s *PublicTransactionPoolAPI) GetTransactionsCount(ctx context.Context, address, txType string) (uint64, error) {
- var err error
- if !strings.HasPrefix(address, "one1") {
- addr := internal_common.ParseAddr(address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return 0, err
- }
- }
- return s.hmy.GetTransactionsCount(address, txType)
-}
-
-// GetStakingTransactionsCount returns the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
-func (s *PublicTransactionPoolAPI) GetStakingTransactionsCount(ctx context.Context, address, txType string) (uint64, error) {
- var err error
- if !strings.HasPrefix(address, "one1") {
- addr := internal_common.ParseAddr(address)
- address, err = internal_common.AddressToBech32(addr)
- if err != nil {
- return 0, err
- }
- }
- return s.hmy.GetStakingTransactionsCount(address, txType)
-}
-
-// SendRawStakingTransaction will add the signed transaction to the transaction pool.
-// The sender is responsible for signing the transaction and using the correct nonce.
-func (s *PublicTransactionPoolAPI) SendRawStakingTransaction(
- ctx context.Context, encodedTx hexutil.Bytes,
-) (common.Hash, error) {
- if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
- err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
- return common.Hash{}, err
- }
- tx := new(staking.StakingTransaction)
- if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
- return common.Hash{}, err
- }
- 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.hmy, tx)
-}
-
-// SendRawTransaction will add the signed transaction to the transaction pool.
-// The sender is responsible for signing the transaction and using the correct nonce.
-func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx hexutil.Bytes) (common.Hash, error) {
- if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
- err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
- return common.Hash{}, err
- }
- tx := new(types.Transaction)
- if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
- return common.Hash{}, err
- }
- 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.hmy, tx)
-}
-
-func (s *PublicTransactionPoolAPI) fillTransactionFields(tx *types.Transaction, fields map[string]interface{}) error {
- var err error
- fields["shardID"] = tx.ShardID()
- var signer types.Signer = types.FrontierSigner{}
- if tx.Protected() {
- signer = types.NewEIP155Signer(tx.ChainID())
- }
- from, _ := types.Sender(signer, tx)
- fields["from"] = from
- fields["to"] = ""
- if tx.To() != nil {
- fields["to"], err = internal_common.AddressToBech32(*tx.To())
- if err != nil {
- return err
- }
- fields["from"], err = internal_common.AddressToBech32(from)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (s *PublicTransactionPoolAPI) fillStakingTransactionFields(stx *staking.StakingTransaction, fields map[string]interface{}) error {
- from, err := stx.SenderAddress()
- if err != nil {
- return err
- }
- fields["sender"], err = internal_common.AddressToBech32(from)
- if err != nil {
- return err
- }
- fields["type"] = stx.StakingType()
- return nil
-}
-
-// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
-func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
- var tx *types.Transaction
- var stx *staking.StakingTransaction
- var blockHash common.Hash
- var blockNumber, index uint64
- tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.hmy.ChainDb(), hash)
- if tx == nil {
- stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash)
- if stx == nil {
- return nil, nil
- }
- }
- receipts, err := s.hmy.GetReceipts(ctx, blockHash)
- if err != nil {
- return nil, err
- }
- if len(receipts) <= int(index) {
- return nil, nil
- }
- receipt := receipts[index]
- fields := map[string]interface{}{
- "blockHash": blockHash,
- "blockNumber": blockNumber,
- "transactionHash": hash,
- "transactionIndex": index,
- "gasUsed": receipt.GasUsed,
- "cumulativeGasUsed": receipt.CumulativeGasUsed,
- "contractAddress": nil,
- "logs": receipt.Logs,
- "logsBloom": receipt.Bloom,
- }
- if tx != nil {
- if err = s.fillTransactionFields(tx, fields); err != nil {
- return nil, err
- }
- } else { // stx not nil
- if err = s.fillStakingTransactionFields(stx, fields); err != nil {
- return nil, err
- }
- }
- // Assign receipt status or post state.
- if len(receipt.PostState) > 0 {
- fields["root"] = hexutil.Bytes(receipt.PostState)
- } else {
- fields["status"] = receipt.Status
- }
- if receipt.Logs == nil {
- fields["logs"] = [][]*types.Log{}
- }
- // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
- if receipt.ContractAddress != (common.Address{}) {
- fields["contractAddress"] = receipt.ContractAddress
- }
- return fields, nil
-}
-
-// GetPoolStats returns stats for the tx-pool
-func (s *PublicTransactionPoolAPI) GetPoolStats() map[string]interface{} {
- pendingCount, queuedCount := s.hmy.GetPoolStats()
- return map[string]interface{}{
- "executable-count": pendingCount,
- "non-executable-count": queuedCount,
- }
-}
-
-// PendingTransactions returns the plain transactions that are in the transaction pool
-func (s *PublicTransactionPoolAPI) PendingTransactions() ([]*RPCTransaction, error) {
- pending, err := s.hmy.GetPoolTransactions()
- if err != nil {
- return nil, err
- }
- transactions := make([]*RPCTransaction, 0)
- for i := range pending {
- if plainTx, ok := pending[i].(*types.Transaction); ok {
- if tx := newRPCTransaction(plainTx, common.Hash{}, 0, 0, 0); tx != nil {
- transactions = append(transactions, tx)
- }
- } else if _, ok := pending[i].(*staking.StakingTransaction); ok {
- continue // Do not return staking transactions here.
- } else {
- return nil, types.ErrUnknownPoolTxType
- }
- }
- return transactions, nil
-}
-
-// PendingStakingTransactions returns the staking transactions that are in the transaction pool
-func (s *PublicTransactionPoolAPI) PendingStakingTransactions() ([]*RPCStakingTransaction, error) {
- pending, err := s.hmy.GetPoolTransactions()
- if err != nil {
- return nil, err
- }
- transactions := make([]*RPCStakingTransaction, 0)
- for i := range pending {
- if _, ok := pending[i].(*types.Transaction); ok {
- continue // Do not return plain transactions here
- } else if stakingTx, ok := pending[i].(*staking.StakingTransaction); ok {
- if tx := newRPCStakingTransaction(stakingTx, common.Hash{}, 0, 0, 0); tx != nil {
- transactions = append(transactions, tx)
- }
- } else {
- return nil, types.ErrUnknownPoolTxType
- }
- }
- return transactions, nil
-}
-
-// GetCurrentTransactionErrorSink ..
-func (s *PublicTransactionPoolAPI) GetCurrentTransactionErrorSink() types.TransactionErrorReports {
- return s.hmy.GetCurrentTransactionErrorSink()
-}
-
-// GetCurrentStakingErrorSink ..
-func (s *PublicTransactionPoolAPI) GetCurrentStakingErrorSink() types.TransactionErrorReports {
- 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.hmy.ChainDb(), hash); cx != nil {
- return newRPCCXReceipt(cx, blockHash, blockNumber)
- }
- return nil
-}
-
-// GetPendingCXReceipts ..
-func (s *PublicTransactionPoolAPI) GetPendingCXReceipts(ctx context.Context) []*types.CXReceiptsProof {
- return s.hmy.GetPendingCXReceipts()
-}
diff --git a/internal/hmyapi/apiv2/types.go b/internal/hmyapi/apiv2/types.go
deleted file mode 100644
index a6f67e10f..000000000
--- a/internal/hmyapi/apiv2/types.go
+++ /dev/null
@@ -1,539 +0,0 @@
-package apiv2
-
-import (
- "encoding/hex"
- "math/big"
- "strings"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- ethtypes "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/harmony-one/harmony/block"
- "github.com/harmony-one/harmony/core/types"
- internal_common "github.com/harmony-one/harmony/internal/common"
- "github.com/harmony-one/harmony/numeric"
- "github.com/harmony-one/harmony/shard"
- staking "github.com/harmony-one/harmony/staking/types"
-)
-
-// RPCBlock represents a block that will serialize to the RPC representation of a block
-type RPCBlock struct {
- Number *big.Int `json:"number"`
- ViewID *big.Int `json:"viewID"`
- Epoch *big.Int `json:"epoch"`
- Hash common.Hash `json:"hash"`
- ParentHash common.Hash `json:"parentHash"`
- Nonce types.BlockNonce `json:"nonce"`
- MixHash common.Hash `json:"mixHash"`
- LogsBloom ethtypes.Bloom `json:"logsBloom"`
- StateRoot common.Hash `json:"stateRoot"`
- Miner string `json:"miner"`
- Difficulty *big.Int `json:"difficulty"`
- ExtraData []byte `json:"extraData"`
- Size uint64 `json:"size"`
- GasLimit uint64 `json:"gasLimit"`
- GasUsed uint64 `json:"gasUsed"`
- Timestamp *big.Int `json:"timestamp"`
- TransactionsRoot common.Hash `json:"transactionsRoot"`
- ReceiptsRoot common.Hash `json:"receiptsRoot"`
- Transactions []interface{} `json:"transactions"`
- StakingTxs []interface{} `json:"stakingTxs"`
- Uncles []common.Hash `json:"uncles"`
- TotalDifficulty *big.Int `json:"totalDifficulty"`
- Signers []string `json:"signers"`
-}
-
-// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction
-type RPCTransaction struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *big.Int `json:"blockNumber"`
- From string `json:"from"`
- Timestamp uint64 `json:"timestamp"`
- Gas uint64 `json:"gas"`
- GasPrice *big.Int `json:"gasPrice"`
- Hash common.Hash `json:"hash"`
- Input hexutil.Bytes `json:"input"`
- Nonce uint64 `json:"nonce"`
- To string `json:"to"`
- TransactionIndex uint64 `json:"transactionIndex"`
- Value *big.Int `json:"value"`
- ShardID uint32 `json:"shardID"`
- ToShardID uint32 `json:"toShardID"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
-}
-
-// RPCStakingTransaction represents a transaction that will serialize to the RPC representation of a staking transaction
-type RPCStakingTransaction struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *big.Int `json:"blockNumber"`
- From string `json:"from"`
- Timestamp uint64 `json:"timestamp"`
- Gas uint64 `json:"gas"`
- GasPrice *big.Int `json:"gasPrice"`
- Hash common.Hash `json:"hash"`
- Nonce uint64 `json:"nonce"`
- TransactionIndex uint64 `json:"transactionIndex"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
- Type string `json:"type"`
- Msg map[string]interface{} `json:"msg"`
-}
-
-// RPCCXReceipt represents a CXReceipt that will serialize to the RPC representation of a CXReceipt
-type RPCCXReceipt struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber *big.Int `json:"blockNumber"`
- TxHash common.Hash `json:"hash"`
- From string `json:"from"`
- To string `json:"to"`
- ShardID uint32 `json:"shardID"`
- ToShardID uint32 `json:"toShardID"`
- Amount *big.Int `json:"value"`
-}
-
-// HeaderInformation represents the latest consensus information
-type HeaderInformation struct {
- BlockHash common.Hash `json:"blockHash"`
- BlockNumber uint64 `json:"blockNumber"`
- ShardID uint32 `json:"shardID"`
- Leader string `json:"leader"`
- ViewID uint64 `json:"viewID"`
- Epoch uint64 `json:"epoch"`
- Timestamp string `json:"timestamp"`
- UnixTime uint64 `json:"unixtime"`
- LastCommitSig string `json:"lastCommitSig"`
- LastCommitBitmap string `json:"lastCommitBitmap"`
- CrossLinks *types.CrossLinks `json:"crossLinks,omitempty"`
-}
-
-// RPCDelegation represents a particular delegation to a validator
-type RPCDelegation struct {
- ValidatorAddress string `json:"validator_address"`
- DelegatorAddress string `json:"delegator_address"`
- Amount *big.Int `json:"amount"`
- Reward *big.Int `json:"reward"`
- Undelegations []RPCUndelegation `json:"Undelegations"`
-}
-
-// RPCUndelegation represents one undelegation entry
-type RPCUndelegation struct {
- Amount *big.Int
- Epoch *big.Int
-}
-
-// CallArgs represents the arguments for a call.
-type CallArgs struct {
- From *common.Address `json:"from"`
- To *common.Address `json:"to"`
- Gas *hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- Value *hexutil.Big `json:"value"`
- Data *hexutil.Bytes `json:"data"`
-}
-
-// StakingNetworkInfo returns global staking info.
-type StakingNetworkInfo struct {
- TotalSupply numeric.Dec `json:"total-supply"`
- CirculatingSupply numeric.Dec `json:"circulating-supply"`
- EpochLastBlock uint64 `json:"epoch-last-block"`
- TotalStaking *big.Int `json:"total-staking"`
- MedianRawStake numeric.Dec `json:"median-raw-stake"`
-}
-
-func newHeaderInformation(header *block.Header, leader string) *HeaderInformation {
- if header == nil {
- return nil
- }
-
- result := &HeaderInformation{
- BlockHash: header.Hash(),
- BlockNumber: header.Number().Uint64(),
- ShardID: header.ShardID(),
- Leader: leader,
- ViewID: header.ViewID().Uint64(),
- Epoch: header.Epoch().Uint64(),
- UnixTime: header.Time().Uint64(),
- Timestamp: time.Unix(header.Time().Int64(), 0).UTC().String(),
- LastCommitBitmap: hex.EncodeToString(header.LastCommitBitmap()),
- }
-
- sig := header.LastCommitSignature()
- result.LastCommitSig = hex.EncodeToString(sig[:])
-
- if header.ShardID() == shard.BeaconChainShardID {
- decodedCrossLinks := &types.CrossLinks{}
- err := rlp.DecodeBytes(header.CrossLinks(), decodedCrossLinks)
- if err != nil {
- result.CrossLinks = &types.CrossLinks{}
- } else {
- result.CrossLinks = decodedCrossLinks
- }
- }
-
- return result
-}
-
-// newRPCCXReceipt returns a CXReceipt that will serialize to the RPC representation
-func newRPCCXReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uint64) *RPCCXReceipt {
- result := &RPCCXReceipt{
- BlockHash: blockHash,
- TxHash: cx.TxHash,
- Amount: cx.Amount,
- ShardID: cx.ShardID,
- ToShardID: cx.ToShardID,
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = (*big.Int)(new(big.Int).SetUint64(blockNumber))
- }
-
- fromAddr, err := internal_common.AddressToBech32(cx.From)
- if err != nil {
- return nil
- }
- toAddr := ""
- if cx.To != nil {
- if toAddr, err = internal_common.AddressToBech32(*cx.To); err != nil {
- return nil
- }
- }
- result.From = fromAddr
- result.To = toAddr
-
- return result
-}
-
-// newRPCTransaction returns a transaction that will serialize to the RPC
-// representation, with the given location metadata set (if available).
-// Note that all txs on Harmony are replay protected (post EIP155 epoch).
-func newRPCTransaction(
- tx *types.Transaction, blockHash common.Hash,
- blockNumber uint64, timestamp uint64, index uint64,
-) *RPCTransaction {
- from, err := tx.SenderAddress()
- if err != nil {
- return nil
- }
- v, r, s := tx.RawSignatureValues()
-
- result := &RPCTransaction{
- Gas: tx.Gas(),
- GasPrice: tx.GasPrice(),
- Hash: tx.Hash(),
- Input: hexutil.Bytes(tx.Data()),
- Nonce: tx.Nonce(),
- Value: tx.Value(),
- ShardID: tx.ShardID(),
- ToShardID: tx.ToShardID(),
- Timestamp: timestamp,
- V: (*hexutil.Big)(v),
- R: (*hexutil.Big)(r),
- S: (*hexutil.Big)(s),
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = (*big.Int)(new(big.Int).SetUint64(blockNumber))
- result.TransactionIndex = index
- }
-
- fromAddr, err := internal_common.AddressToBech32(from)
- if err != nil {
- return nil
- }
- toAddr := ""
-
- if tx.To() != nil {
- if toAddr, err = internal_common.AddressToBech32(*tx.To()); err != nil {
- return nil
- }
- result.From = fromAddr
- } else {
- result.From = strings.ToLower(from.Hex())
- }
- result.To = toAddr
-
- return result
-}
-
-// newRPCStakingTransaction returns a staking transaction that will serialize to the RPC
-// representation, with the given location metadata set (if available).
-func newRPCStakingTransaction(
- tx *staking.StakingTransaction, blockHash common.Hash,
- blockNumber uint64, timestamp uint64, index uint64,
-) *RPCStakingTransaction {
- from, err := tx.SenderAddress()
- if err != nil {
- return nil
- }
- v, r, s := tx.RawSignatureValues()
-
- fields := make(map[string]interface{})
-
- switch tx.StakingType() {
- case staking.DirectiveCreateValidator:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCreateValidator)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.CreateValidator)
- if !ok {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "validatorAddress": validatorAddress,
- "name": msg.Description.Name,
- "commissionRate": msg.CommissionRates.Rate.Int,
- "maxCommissionRate": msg.CommissionRates.MaxRate.Int,
- "maxChangeRate": msg.CommissionRates.MaxChangeRate.Int,
- "minSelfDelegation": msg.MinSelfDelegation,
- "maxTotalDelegation": msg.MaxTotalDelegation,
- "amount": msg.Amount,
- "website": msg.Description.Website,
- "identity": msg.Description.Identity,
- "securityContact": msg.Description.SecurityContact,
- "details": msg.Description.Details,
- "slotPubKeys": msg.SlotPubKeys,
- }
- case staking.DirectiveEditValidator:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.EditValidator)
- if !ok {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- // Edit validators txs need not have commission rates to edit
- commissionRate := &big.Int{}
- if msg.CommissionRate != nil {
- commissionRate = msg.CommissionRate.Int
- }
- fields = map[string]interface{}{
- "validatorAddress": validatorAddress,
- "commisionRate": commissionRate,
- "name": msg.Description.Name,
- "minSelfDelegation": msg.MinSelfDelegation,
- "maxTotalDelegation": msg.MaxTotalDelegation,
- "website": msg.Description.Website,
- "identity": msg.Description.Identity,
- "securityContact": msg.Description.SecurityContact,
- "details": msg.Description.Details,
- "slotPubKeyToAdd": msg.SlotKeyToAdd,
- "slotPubKeyToRemove": msg.SlotKeyToRemove,
- }
- case staking.DirectiveCollectRewards:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCollectRewards)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.CollectRewards)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- }
- case staking.DirectiveDelegate:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveDelegate)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.Delegate)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- "validatorAddress": validatorAddress,
- "amount": msg.Amount,
- }
- case staking.DirectiveUndelegate:
- rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate)
- if err != nil {
- return nil
- }
- msg, ok := rawMsg.(*staking.Undelegate)
- if !ok {
- return nil
- }
- delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
- if err != nil {
- return nil
- }
- validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
- if err != nil {
- return nil
- }
- fields = map[string]interface{}{
- "delegatorAddress": delegatorAddress,
- "validatorAddress": validatorAddress,
- "amount": msg.Amount,
- }
- }
-
- result := &RPCStakingTransaction{
- Gas: tx.Gas(),
- GasPrice: tx.GasPrice(),
- Hash: tx.Hash(),
- Nonce: tx.Nonce(),
- Timestamp: timestamp,
- V: (*hexutil.Big)(v),
- R: (*hexutil.Big)(r),
- S: (*hexutil.Big)(s),
- Type: tx.StakingType().String(),
- Msg: fields,
- }
- if blockHash != (common.Hash{}) {
- result.BlockHash = blockHash
- result.BlockNumber = new(big.Int).SetUint64(blockNumber)
- result.TransactionIndex = index
- }
-
- fromAddr, err := internal_common.AddressToBech32(from)
- if err != nil {
- return nil
- }
- result.From = fromAddr
-
- return result
-}
-
-// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
-// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
-// transaction hashes.
-func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs, leader string) (map[string]interface{}, error) {
- head := b.Header() // copies the header once
- fields := map[string]interface{}{
- "number": (*big.Int)(head.Number()),
- "viewID": (*big.Int)(head.ViewID()),
- "epoch": (*big.Int)(head.Epoch()),
- "hash": b.Hash(),
- "parentHash": head.ParentHash(),
- "nonce": 0, // Remove this because we don't have it in our header
- "mixHash": head.MixDigest(),
- "logsBloom": head.Bloom(),
- "stateRoot": head.Root(),
- "miner": leader,
- "difficulty": 0, // Remove this because we don't have it in our header
- "extraData": hexutil.Bytes(head.Extra()),
- "size": uint64(b.Size()),
- "gasLimit": head.GasLimit(),
- "gasUsed": head.GasUsed(),
- "timestamp": head.Time().Uint64(),
- "transactionsRoot": head.TxHash(),
- "receiptsRoot": head.ReceiptHash(),
- }
-
- if blockArgs.InclTx {
- formatTx := func(tx *types.Transaction) (interface{}, error) {
- return tx.Hash(), nil
- }
- if blockArgs.FullTx {
- formatTx = func(tx *types.Transaction) (interface{}, error) {
- return newRPCTransactionFromBlockHash(b, tx.Hash()), nil
- }
- }
- txs := b.Transactions()
- transactions := make([]interface{}, len(txs))
- var err error
- for i, tx := range txs {
- if transactions[i], err = formatTx(tx); err != nil {
- return nil, err
- }
- }
- fields["transactions"] = transactions
-
- if blockArgs.InclStaking {
- formatStakingTx := func(tx *staking.StakingTransaction) (interface{}, error) {
- return tx.Hash(), nil
- }
- if blockArgs.FullTx {
- formatStakingTx = func(tx *staking.StakingTransaction) (interface{}, error) {
- return newRPCStakingTransactionFromBlockHash(b, tx.Hash()), nil
- }
- }
- stakingTxs := b.StakingTransactions()
- stakingTransactions := make([]interface{}, len(stakingTxs))
- for i, tx := range stakingTxs {
- if stakingTransactions[i], err = formatStakingTx(tx); err != nil {
- return nil, err
- }
- }
- fields["stakingTransactions"] = stakingTransactions
- }
- }
-
- uncles := b.Uncles()
- uncleHashes := make([]common.Hash, len(uncles))
- for i, uncle := range uncles {
- uncleHashes[i] = uncle.Hash()
- }
- fields["uncles"] = uncleHashes
- if blockArgs.WithSigners {
- fields["signers"] = blockArgs.Signers
- }
- return fields, nil
-}
-
-// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
- for idx, tx := range b.Transactions() {
- if tx.Hash() == hash {
- return newRPCTransactionFromBlockIndex(b, uint64(idx))
- }
- }
- return nil
-}
-
-// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
- txs := b.Transactions()
- if index >= uint64(len(txs)) {
- return nil
- }
- return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
-}
-
-// newRPCStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
-func newRPCStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCStakingTransaction {
- for idx, tx := range b.StakingTransactions() {
- if tx.Hash() == hash {
- return newRPCStakingTransactionFromBlockIndex(b, uint64(idx))
- }
- }
- return nil
-}
-
-// newRPCStakingTransactionFromBlockIndex returns a staking transaction that will serialize to the RPC representation.
-func newRPCStakingTransactionFromBlockIndex(b *types.Block, index uint64) *RPCStakingTransaction {
- txs := b.StakingTransactions()
- if index >= uint64(len(txs)) {
- return nil
- }
- return newRPCStakingTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
-}
diff --git a/internal/hmyapi/apiv2/util.go b/internal/hmyapi/apiv2/util.go
deleted file mode 100644
index 60418dec7..000000000
--- a/internal/hmyapi/apiv2/util.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package apiv2
-
-import (
- "context"
-
- "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"
-)
-
-// defaultPageSize is to have default pagination.
-const (
- defaultPageSize = uint32(1000)
-)
-
-// ReturnWithPagination returns result with pagination (offset, page in TxHistoryArgs).
-func ReturnWithPagination(hashes []common.Hash, pageIndex uint32, pageSize uint32) []common.Hash {
- size := defaultPageSize
- if pageSize > 0 {
- size = pageSize
- }
- if uint64(size)*uint64(pageIndex) >= uint64(len(hashes)) {
- return make([]common.Hash, 0)
- }
- if uint64(size)*uint64(pageIndex)+uint64(size) > uint64(len(hashes)) {
- return hashes[size*pageIndex:]
- }
- return hashes[size*pageIndex : size*pageIndex+size]
-}
-
-// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
-func SubmitTransaction(
- ctx context.Context, hmy *hmy.Harmony, tx *types.Transaction,
-) (common.Hash, error) {
- 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(hmy.ChainConfig(), hmy.CurrentBlock().Epoch())
- from, err := types.Sender(signer, tx)
- if err != nil {
- return common.Hash{}, err
- }
- addr := crypto.CreateAddress(from, tx.Nonce())
- utils.Logger().Info().
- Str("fullhash", tx.Hash().Hex()).
- Str("contract", common2.MustAddressToBech32(addr)).
- Msg("Submitted contract creation")
- } else {
- utils.Logger().Info().
- Str("fullhash", tx.Hash().Hex()).
- Str("recipient", tx.To().Hex()).
- Msg("Submitted transaction")
- }
- return tx.Hash(), nil
-}
-
-// SubmitStakingTransaction is a helper function that submits tx to txPool and logs a message.
-func SubmitStakingTransaction(
- ctx context.Context, hmy *hmy.Harmony, tx *staking.StakingTransaction,
-) (common.Hash, error) {
- 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
- }
- utils.Logger().Info().Str("fullhash", tx.Hash().Hex()).Msg("Submitted Staking transaction")
- return tx.Hash(), nil
-}
diff --git a/internal/hmyapi/backend.go b/internal/hmyapi/backend.go
deleted file mode 100644
index ffb31ea52..000000000
--- a/internal/hmyapi/backend.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package hmyapi
-
-import (
- "github.com/ethereum/go-ethereum/rpc"
- "github.com/harmony-one/harmony/hmy"
- "github.com/harmony-one/harmony/internal/hmyapi/apiv1"
- "github.com/harmony-one/harmony/internal/hmyapi/apiv2"
-)
-
-// GetAPIs returns all the APIs.
-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(hmy),
- Public: true,
- },
- {
- Namespace: "hmy",
- Version: "1.0",
- Service: apiv1.NewPublicBlockChainAPI(hmy),
- Public: true,
- },
- {
- Namespace: "hmy",
- Version: "1.0",
- Service: apiv1.NewPublicTransactionPoolAPI(hmy, nonceLock),
- Public: true,
- },
- {
- Namespace: "hmy",
- Version: "1.0",
- Service: apiv1.NewDebugAPI(hmy),
- Public: false,
- },
- {
- Namespace: "hmyv2",
- Version: "1.0",
- Service: apiv2.NewPublicHarmonyAPI(hmy),
- Public: true,
- },
- {
- Namespace: "hmyv2",
- Version: "1.0",
- Service: apiv2.NewPublicBlockChainAPI(hmy),
- Public: true,
- },
- {
- Namespace: "hmyv2",
- Version: "1.0",
- Service: apiv2.NewPublicTransactionPoolAPI(hmy, nonceLockV2),
- Public: true,
- },
- {
- Namespace: "hmyv2",
- Version: "1.0",
- Service: apiv2.NewDebugAPI(hmy),
- Public: false,
- },
- }
-}
diff --git a/node/rpc.go b/node/rpc.go
index 49697dbed..ff1aa78ad 100644
--- a/node/rpc.go
+++ b/node/rpc.go
@@ -1,43 +1,16 @@
package node
import (
- "fmt"
- "net"
"strconv"
- "strings"
"github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/hmy"
- nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
- "github.com/harmony-one/harmony/internal/hmyapi"
- "github.com/harmony-one/harmony/internal/hmyapi/apiv1"
- "github.com/harmony-one/harmony/internal/hmyapi/apiv2"
- "github.com/harmony-one/harmony/internal/hmyapi/filters"
- "github.com/harmony-one/harmony/internal/utils"
+ hmy_rpc "github.com/harmony-one/harmony/rpc"
+ "github.com/harmony-one/harmony/rpc/filters"
"github.com/libp2p/go-libp2p-core/peer"
)
-const (
- rpcHTTPPortOffset = 500
- rpcWSPortOffset = 800
-)
-
-var (
- // HTTP RPC
- httpListener net.Listener
- httpHandler *rpc.Server
- httpEndpoint = ""
- wsEndpoint = ""
- httpModules = []string{"hmy", "hmyv2", "net", "netv2", "explorer"}
- httpVirtualHosts = []string{"*"}
- httpTimeouts = rpc.DefaultHTTPTimeouts
- httpOrigins = []string{"*"}
- wsModules = []string{"hmy", "hmyv2", "net", "netv2", "web3"}
- wsOrigins = []string{"*"}
- harmony *hmy.Harmony
-)
-
// IsCurrentlyLeader exposes if node is currently the leader node
func (node *Node) IsCurrentlyLeader() bool {
return node.Consensus.IsLeader()
@@ -91,8 +64,10 @@ func (node *Node) ReportPlainErrorSink() types.TransactionErrorReports {
// StartRPC start RPC service
func (node *Node) StartRPC(nodePort string) error {
+ harmony := hmy.New(node, node.TxPool, node.CxPool, node.Consensus.ShardID)
+
// Gather all the possible APIs to surface
- apis := node.APIs()
+ apis := node.APIs(harmony)
for _, service := range node.serviceManager.GetServices() {
apis = append(apis, service.APIs()...)
@@ -100,105 +75,26 @@ func (node *Node) StartRPC(nodePort string) error {
port, _ := strconv.Atoi(nodePort)
- ip := ""
- if !nodeconfig.GetPublicRPC() {
- ip = "127.0.0.1"
- }
- httpEndpoint = fmt.Sprintf("%v:%v", ip, port+rpcHTTPPortOffset)
-
- if err := node.startHTTP(httpEndpoint, apis, httpModules, httpOrigins, httpVirtualHosts, httpTimeouts); err != nil {
- return err
- }
- wsEndpoint = fmt.Sprintf("%v:%v", ip, port+rpcWSPortOffset)
- if err := node.startWS(wsEndpoint, apis, wsModules, wsOrigins, true); err != nil {
- node.stopHTTP()
- return err
- }
-
- return nil
+ return hmy_rpc.StartServers(harmony, port, apis)
}
-// startHTTP initializes and starts the HTTP RPC endpoint.
-func (node *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error {
- // Short circuit if the HTTP endpoint isn't being exposed
- if endpoint == "" {
- return nil
- }
-
- listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts)
- if err != nil {
- return err
- }
-
- utils.Logger().Info().
- Str("url", fmt.Sprintf("http://%s", endpoint)).
- Str("cors", strings.Join(cors, ",")).
- Str("vhosts", strings.Join(vhosts, ",")).
- Msg("HTTP endpoint opened")
- // All listeners booted successfully
- httpListener = listener
- httpHandler = handler
- return nil
+// StopRPC stop RPC service
+func (node *Node) StopRPC() error {
+ return hmy_rpc.StopServers()
}
-// stopHTTP terminates the HTTP RPC endpoint.
-func (node *Node) stopHTTP() {
- if httpListener != nil {
- httpListener.Close()
- httpListener = nil
- utils.Logger().Info().Str("url", fmt.Sprintf("http://%s", httpEndpoint)).Msg("HTTP endpoint closed")
- }
- if httpHandler != nil {
- httpHandler.Stop()
- httpHandler = nil
- }
-}
-
-// startWS initializes and starts the websocket RPC endpoint.
-func (node *Node) startWS(
- endpoint string, apis []rpc.API, modules []string, wsOrigins []string, exposeAll bool,
-) error {
- // Short circuit if the WS endpoint isn't being exposed
- if endpoint == "" {
- return nil
- }
- listener, _, err := rpc.StartWSEndpoint(endpoint, apis, modules, wsOrigins, exposeAll)
- if err != nil {
- return err
- }
- utils.Logger().Info().
- Str("url", fmt.Sprintf("ws://%s", listener.Addr())).
- Msg("WebSocket endpoint opened")
- return nil
-}
-
-// APIs return the collection of RPC services the ethereum package offers.
+// APIs return the collection of local RPC services.
// 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)
+func (node *Node) APIs(harmony *hmy.Harmony) []rpc.API {
// Append all the local APIs and return
- return append(apis, []rpc.API{
+ return []rpc.API{
+ hmy_rpc.NewPublicNetAPI(node.host, harmony.ChainID, hmy_rpc.V1),
+ hmy_rpc.NewPublicNetAPI(node.host, harmony.ChainID, hmy_rpc.V2),
{
Namespace: "hmy",
- Version: "1.0",
+ Version: hmy_rpc.APIVersion,
Service: filters.NewPublicFilterAPI(harmony, false),
Public: true,
},
- {
- Namespace: "net",
- Version: "1.0",
- Service: apiv1.NewPublicNetAPI(node.host, harmony.ChainID),
- Public: true,
- },
- {
- Namespace: "netv2",
- Version: "1.0",
- Service: apiv2.NewPublicNetAPI(node.host, harmony.ChainID),
- Public: true,
- },
- }...)
+ }
}
diff --git a/rpc/blockchain.go b/rpc/blockchain.go
new file mode 100644
index 000000000..660463044
--- /dev/null
+++ b/rpc/blockchain.go
@@ -0,0 +1,668 @@
+package rpc
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/consensus/reward"
+ "github.com/harmony-one/harmony/hmy"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+ "github.com/harmony-one/harmony/internal/utils"
+ "github.com/harmony-one/harmony/numeric"
+ rpc_common "github.com/harmony-one/harmony/rpc/common"
+ v1 "github.com/harmony-one/harmony/rpc/v1"
+ v2 "github.com/harmony-one/harmony/rpc/v2"
+ "github.com/harmony-one/harmony/shard"
+)
+
+const (
+ initSupply = int64(12600000000)
+)
+
+var (
+ blocksPeriod = shard.Schedule.BlocksPerEpoch()
+)
+
+// PublicBlockchainService provides an API to access the Harmony blockchain.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicBlockchainService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicBlockchainAPI creates a new API for the RPC interface
+func NewPublicBlockchainAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicBlockchainService{hmy, version},
+ Public: true,
+ }
+}
+
+// getBlockOptions is a helper to get block args given an interface option from RPC params.
+func (s *PublicBlockchainService) getBlockOptions(opts interface{}) (*rpc_common.BlockArgs, error) {
+ switch s.version {
+ case V1:
+ fullTx, ok := opts.(bool)
+ if !ok {
+ return nil, fmt.Errorf("invalid type for block arguments")
+ }
+ return &rpc_common.BlockArgs{
+ WithSigners: false,
+ FullTx: fullTx,
+ InclStaking: true,
+ }, nil
+ case V2:
+ parsedBlockArgs := rpc_common.BlockArgs{}
+ if err := parsedBlockArgs.UnmarshalFromInterface(opts); err != nil {
+ return nil, err
+ }
+ return &parsedBlockArgs, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// getBalanceByBlockNumber returns balance by block number at given eth blockNum without checks
+func (s *PublicBlockchainService) getBalanceByBlockNumber(
+ ctx context.Context, address string, blockNum rpc.BlockNumber,
+) (*big.Int, error) {
+ addr := internal_common.ParseAddr(address)
+ balance, err := s.hmy.GetBalance(ctx, addr, blockNum)
+ if err != nil {
+ return nil, err
+ }
+ return balance, nil
+}
+
+// BlockNumber returns the block number of the chain head.
+func (s *PublicBlockchainService) BlockNumber(ctx context.Context) (interface{}, error) {
+ // Fetch latest header
+ header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return nil, err
+ }
+
+ // Format return base on version
+ switch s.version {
+ case V1:
+ return hexutil.Uint64(header.Number().Uint64()), nil
+ case V2:
+ return header.Number().Uint64(), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// 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.
+// When withSigners in BlocksArgs is true it shows block signers for this block in list of one addresses.
+func (s *PublicBlockchainService) GetBlockByNumber(
+ ctx context.Context, blockNumber BlockNumber, opts interface{},
+) (response StructuredResponse, err error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+ var blockArgs *rpc_common.BlockArgs
+ blockArgs, ok := opts.(*rpc_common.BlockArgs)
+ if !ok {
+ blockArgs, err = s.getBlockOptions(opts)
+ if err != nil {
+ return nil, err
+ }
+ }
+ blockArgs.InclTx = true
+
+ // Fetch the block
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+ blk, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, err
+ }
+ if blockArgs.WithSigners {
+ blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNumber)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Format the response according to version
+ leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
+ var rpcBlock interface{}
+ switch s.version {
+ case V1:
+ rpcBlock, err = v1.NewBlock(blk, blockArgs, leader)
+ case V2:
+ rpcBlock, err = v2.NewBlock(blk, blockArgs, leader)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ if err != nil {
+ return nil, err
+ }
+ response, err = NewStructuredResponse(rpcBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ // Pending blocks need to nil out a few fields
+ if blockNum == rpc.PendingBlockNumber {
+ for _, field := range []string{"hash", "nonce", "miner"} {
+ response[field] = nil
+ }
+ }
+ return response, err
+}
+
+// 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. When withSigners in BlocksArgs is true
+// it shows block signers for this block in list of one addresses.
+func (s *PublicBlockchainService) GetBlockByHash(
+ ctx context.Context, blockHash common.Hash, opts interface{},
+) (response StructuredResponse, err error) {
+ // Process arguments based on version
+ var blockArgs *rpc_common.BlockArgs
+ blockArgs, ok := opts.(*rpc_common.BlockArgs)
+ if !ok {
+ blockArgs, err = s.getBlockOptions(opts)
+ if err != nil {
+ return nil, err
+ }
+ }
+ blockArgs.InclTx = true
+
+ // Fetch the block
+ blk, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ return nil, err
+ }
+ if blockArgs.WithSigners {
+ blockArgs.Signers, err = s.GetBlockSigners(ctx, BlockNumber(blk.NumberU64()))
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Format the response according to version
+ leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
+ var rpcBlock interface{}
+ switch s.version {
+ case V1:
+ rpcBlock, err = v1.NewBlock(blk, blockArgs, leader)
+ case V2:
+ rpcBlock, err = v2.NewBlock(blk, blockArgs, leader)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(rpcBlock)
+}
+
+// GetBlockByNumberNew is an alias for GetBlockByNumber using rpc_common.BlockArgs
+func (s *PublicBlockchainService) GetBlockByNumberNew(
+ ctx context.Context, blockNum BlockNumber, blockArgs *rpc_common.BlockArgs,
+) (StructuredResponse, error) {
+ return s.GetBlockByNumber(ctx, blockNum, blockArgs)
+}
+
+// GetBlockByHashNew is an alias for GetBlocksByHash using rpc_common.BlockArgs
+func (s *PublicBlockchainService) GetBlockByHashNew(
+ ctx context.Context, blockHash common.Hash, blockArgs *rpc_common.BlockArgs,
+) (StructuredResponse, error) {
+ return s.GetBlockByHash(ctx, blockHash, blockArgs)
+}
+
+// GetBlocks method returns blocks in range blockStart, blockEnd just like GetBlockByNumber but all at once.
+func (s *PublicBlockchainService) GetBlocks(
+ ctx context.Context, blockNumberStart BlockNumber,
+ blockNumberEnd BlockNumber, blockArgs *rpc_common.BlockArgs,
+) ([]StructuredResponse, error) {
+ blockStart := blockNumberStart.Int64()
+ blockEnd := blockNumberEnd.Int64()
+
+ // Fetch blocks within given range
+ result := []StructuredResponse{}
+ for i := blockStart; i <= blockEnd; i++ {
+ blockNum := BlockNumber(i)
+ if blockNum.Int64() > s.hmy.CurrentBlock().Number().Int64() {
+ break
+ }
+ // rpcBlock is already formatted according to version
+ rpcBlock, err := s.GetBlockByNumber(ctx, blockNum, blockArgs)
+ if err != nil {
+ utils.Logger().Warn().Err(err).Msg("RPC Get Blocks Error")
+ }
+ if rpcBlock != nil {
+ result = append(result, rpcBlock)
+ }
+ }
+ return result, nil
+}
+
+// IsLastBlock checks if block is last epoch block.
+func (s *PublicBlockchainService) IsLastBlock(ctx context.Context, blockNum uint64) (bool, error) {
+ if !isBeaconShard(s.hmy) {
+ return false, ErrNotBeaconShard
+ }
+ return shard.Schedule.IsLastBlock(blockNum), nil
+}
+
+// EpochLastBlock returns epoch last block.
+func (s *PublicBlockchainService) EpochLastBlock(ctx context.Context, epoch uint64) (uint64, error) {
+ if !isBeaconShard(s.hmy) {
+ return 0, ErrNotBeaconShard
+ }
+ return shard.Schedule.EpochLastBlock(epoch), nil
+}
+
+// GetBlockSigners returns signers for a particular block.
+func (s *PublicBlockchainService) GetBlockSigners(
+ ctx context.Context, blockNumber BlockNumber,
+) ([]string, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Ensure correct block
+ if blockNum.Int64() == 0 || blockNum.Int64() >= s.hmy.CurrentBlock().Number().Int64() {
+ return []string{}, nil
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+
+ // Fetch signers
+ slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ signers := []string{}
+ for _, validator := range slots {
+ oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
+ if err != nil {
+ return nil, err
+ }
+ if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
+ signers = append(signers, oneAddress)
+ }
+ }
+ return signers, nil
+}
+
+// GetBlockSignerKeys returns bls public keys that signed the block.
+func (s *PublicBlockchainService) GetBlockSignerKeys(
+ ctx context.Context, blockNumber BlockNumber,
+) ([]string, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Ensure correct block
+ if blockNum.Int64() == 0 || blockNum.Int64() >= s.hmy.CurrentBlock().Number().Int64() {
+ return []string{}, nil
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+
+ // Fetch signers
+ slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ signers := []string{}
+ for _, validator := range slots {
+ if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
+ signers = append(signers, validator.BLSPublicKey.Hex())
+ }
+ }
+ return signers, nil
+}
+
+// IsBlockSigner returns true if validator with address signed blockNum block.
+func (s *PublicBlockchainService) IsBlockSigner(
+ ctx context.Context, blockNumber BlockNumber, address string,
+) (bool, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Ensure correct block
+ if blockNum.Int64() == 0 {
+ return false, nil
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return false, ErrRequestedBlockTooHigh
+ }
+
+ // Fetch signers
+ slots, mask, err := s.hmy.GetBlockSigners(ctx, blockNum)
+ if err != nil {
+ return false, err
+ }
+
+ // Check if given address is in slots (response output is the same for all versions)
+ for _, validator := range slots {
+ oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
+ if err != nil {
+ return false, err
+ }
+ if oneAddress != address {
+ continue
+ }
+ if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+// GetSignedBlocks returns how many blocks a particular validator signed for
+// last blocksPeriod (1 epoch's worth of blocks).
+func (s *PublicBlockchainService) GetSignedBlocks(
+ ctx context.Context, address string,
+) (interface{}, error) {
+ // Fetch the number of signed blocks within default period
+ totalSigned := uint64(0)
+ lastBlock := uint64(0)
+ blockHeight := s.hmy.CurrentBlock().Number().Uint64()
+ if blockHeight >= blocksPeriod {
+ lastBlock = blockHeight - blocksPeriod + 1
+ }
+ for i := lastBlock; i <= blockHeight; i++ {
+ signed, err := s.IsBlockSigner(ctx, BlockNumber(i), address)
+ if err == nil && signed {
+ totalSigned++
+ }
+ }
+
+ // Format the response according to the version
+ switch s.version {
+ case V1:
+ return hexutil.Uint64(totalSigned), nil
+ case V2:
+ return totalSigned, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetEpoch returns current epoch.
+func (s *PublicBlockchainService) GetEpoch(ctx context.Context) (interface{}, error) {
+ // Fetch Header
+ header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return "", err
+ }
+ epoch := header.Epoch().Uint64()
+
+ // Format the response according to the version
+ switch s.version {
+ case V1:
+ return hexutil.Uint64(epoch), nil
+ case V2:
+ return epoch, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetLeader returns current shard leader.
+func (s *PublicBlockchainService) GetLeader(ctx context.Context) (string, error) {
+ // Fetch Header
+ header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return "", err
+ }
+
+ // Response output is the same for all versions
+ leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch())
+ return leader, nil
+}
+
+// GetShardingStructure returns an array of sharding structures.
+func (s *PublicBlockchainService) GetShardingStructure(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // Get header and number of shards.
+ epoch := s.hmy.CurrentBlock().Epoch()
+ numShard := shard.Schedule.InstanceForEpoch(epoch).NumShards()
+
+ // Return sharding structure for each case (response output is the same for all versions)
+ return shard.Schedule.GetShardingStructure(int(numShard), int(s.hmy.ShardID)), nil
+}
+
+// GetShardID returns shard ID of the requested node.
+func (s *PublicBlockchainService) GetShardID(ctx context.Context) (int, error) {
+ // Response output is the same for all versions
+ return int(s.hmy.ShardID), nil
+}
+
+// GetBalanceByBlockNumber returns balance by block number
+func (s *PublicBlockchainService) GetBalanceByBlockNumber(
+ ctx context.Context, address string, blockNumber BlockNumber,
+) (interface{}, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch balance
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+ balance, err := s.getBalanceByBlockNumber(ctx, address, blockNum)
+ if err != nil {
+ return nil, err
+ }
+
+ // Format return base on version
+ switch s.version {
+ case V1:
+ return (*hexutil.Big)(balance), nil
+ case V2:
+ return balance, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// LatestHeader returns the latest header information
+func (s *PublicBlockchainService) LatestHeader(ctx context.Context) (StructuredResponse, error) {
+ // Fetch Header
+ header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch())
+ return NewStructuredResponse(NewHeaderInformation(header, leader))
+}
+
+// GetLatestChainHeaders ..
+func (s *PublicBlockchainService) GetLatestChainHeaders(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ // Response output is the same for all versions
+ return NewStructuredResponse(s.hmy.GetLatestChainHeaders())
+}
+
+// GetLastCrossLinks ..
+func (s *PublicBlockchainService) GetLastCrossLinks(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch crosslinks
+ crossLinks, err := s.hmy.GetLastCrossLinks()
+ if err != nil {
+ return nil, err
+ }
+
+ // Format response, all output is the same for all versions
+ responseSlice := []StructuredResponse{}
+ for _, el := range crossLinks {
+ response, err := NewStructuredResponse(el)
+ if err != nil {
+ return nil, err
+ }
+ responseSlice = append(responseSlice, response)
+ }
+ return responseSlice, nil
+}
+
+// GetHeaderByNumber returns block header at given number
+func (s *PublicBlockchainService) GetHeaderByNumber(
+ ctx context.Context, blockNumber BlockNumber,
+) (StructuredResponse, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Ensure valid block number
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+
+ // Fetch Header
+ header, err := s.hmy.HeaderByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ leader := s.hmy.GetLeaderAddress(header.Coinbase(), header.Epoch())
+ return NewStructuredResponse(NewHeaderInformation(header, leader))
+}
+
+// GetCurrentUtilityMetrics ..
+func (s *PublicBlockchainService) GetCurrentUtilityMetrics(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch metrics
+ metrics, err := s.hmy.GetCurrentUtilityMetrics()
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(metrics)
+}
+
+// GetSuperCommittees ..
+func (s *PublicBlockchainService) GetSuperCommittees(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch super committees
+ cmt, err := s.hmy.GetSuperCommittees()
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(cmt)
+}
+
+// GetCurrentBadBlocks ..
+func (s *PublicBlockchainService) GetCurrentBadBlocks(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // Fetch bad blocks and format
+ badBlocks := []StructuredResponse{}
+ for _, blk := range s.hmy.GetCurrentBadBlocks() {
+ // Response output is the same for all versions
+ fmtBadBlock, err := NewStructuredResponse(blk)
+ if err != nil {
+ return nil, err
+ }
+ badBlocks = append(badBlocks, fmtBadBlock)
+ }
+
+ return badBlocks, nil
+}
+
+// GetTotalSupply ..
+func (s *PublicBlockchainService) GetTotalSupply(
+ ctx context.Context,
+) (numeric.Dec, error) {
+ // Response output is the same for all versions
+ return numeric.NewDec(initSupply), nil
+}
+
+// GetCirculatingSupply ..
+func (s *PublicBlockchainService) GetCirculatingSupply(
+ ctx context.Context,
+) (numeric.Dec, error) {
+ timestamp := time.Now()
+
+ // Response output is the same for all versions
+ return numeric.NewDec(initSupply).Mul(reward.PercentageForTimeStamp(timestamp.Unix())), nil
+}
+
+// GetStakingNetworkInfo ..
+func (s *PublicBlockchainService) GetStakingNetworkInfo(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+ totalStaking := s.hmy.GetTotalStakingSnapshot()
+ header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return nil, err
+ }
+ medianSnapshot, err := s.hmy.GetMedianRawStakeSnapshot()
+ if err != nil {
+ return nil, err
+ }
+ epochLastBlock, err := s.EpochLastBlock(ctx, header.Epoch().Uint64())
+ if err != nil {
+ return nil, err
+ }
+ totalSupply, err := s.GetTotalSupply(ctx)
+ if err != nil {
+ return nil, err
+ }
+ circulatingSupply, err := s.GetCirculatingSupply(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(StakingNetworkInfo{
+ TotalSupply: totalSupply,
+ CirculatingSupply: circulatingSupply,
+ EpochLastBlock: epochLastBlock,
+ TotalStaking: totalStaking,
+ MedianRawStake: medianSnapshot.MedianStake,
+ })
+}
+
+func isBlockGreaterThanLatest(hmy *hmy.Harmony, blockNum rpc.BlockNumber) bool {
+ // rpc.BlockNumber is int64 (latest = -1. pending = -2) and currentBlockNum is uint64.
+ if blockNum == rpc.PendingBlockNumber {
+ return true
+ }
+ if blockNum == rpc.LatestBlockNumber {
+ return false
+ }
+ return uint64(blockNum) > hmy.CurrentBlock().NumberU64()
+}
diff --git a/internal/hmyapi/common/hacks.go b/rpc/common/hacks.go
similarity index 100%
rename from internal/hmyapi/common/hacks.go
rename to rpc/common/hacks.go
diff --git a/internal/hmyapi/common/types.go b/rpc/common/types.go
similarity index 71%
rename from internal/hmyapi/common/types.go
rename to rpc/common/types.go
index 5da9f11a4..9c91aa176 100644
--- a/internal/hmyapi/common/types.go
+++ b/rpc/common/types.go
@@ -1,10 +1,35 @@
package common
import (
+ "encoding/json"
+
"github.com/harmony-one/harmony/internal/params"
"github.com/libp2p/go-libp2p-core/peer"
)
+// BlockArgs is struct to include optional block formatting params.
+type BlockArgs struct {
+ WithSigners bool `json:"withSigners"`
+ InclTx bool `json:"inclTx"`
+ FullTx bool `json:"fullTx"`
+ Signers []string `json:"signers"`
+ InclStaking bool `json:"inclStaking"`
+}
+
+// UnmarshalFromInterface ..
+func (ba *BlockArgs) UnmarshalFromInterface(blockArgs interface{}) error {
+ var args BlockArgs
+ dat, err := json.Marshal(blockArgs)
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal(dat, &args); err != nil {
+ return err
+ }
+ *ba = args
+ return nil
+}
+
// C ..
type C struct {
TotalKnownPeers int `json:"total-known-peers"`
diff --git a/rpc/contract.go b/rpc/contract.go
new file mode 100644
index 000000000..1fea9a121
--- /dev/null
+++ b/rpc/contract.go
@@ -0,0 +1,196 @@
+package rpc
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/common/denominations"
+ "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/utils"
+)
+
+const (
+ defaultGasPrice = denominations.Nano
+ defaultFromAddress = "0x0000000000000000000000000000000000000000"
+)
+
+// PublicContractService provides an API to access Harmony's contract services.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicContractService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicContractAPI creates a new API for the RPC interface
+func NewPublicContractAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicContractService{hmy, version},
+ Public: true,
+ }
+}
+
+// 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 *PublicContractService) Call(
+ ctx context.Context, args CallArgs, blockNumber BlockNumber,
+) (hexutil.Bytes, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Execute call
+ result, _, _, err := doCall(ctx, s.hmy, args, blockNum, vm.Config{}, CallTimeout, s.hmy.RPCGasCap)
+
+ // Response output is the same for all versions
+ return result, err
+}
+
+// GetCode returns the code stored at the given address in the state for the given block number.
+func (s *PublicContractService) GetCode(
+ ctx context.Context, addr string, blockNumber BlockNumber,
+) (hexutil.Bytes, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch state
+ address := internal_common.ParseAddr(addr)
+ state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum)
+ if state == nil || err != nil {
+ return nil, err
+ }
+ code := state.GetCode(address)
+
+ // Response output is the same for all versions
+ return code, state.Error()
+}
+
+// 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 *PublicContractService) GetStorageAt(
+ ctx context.Context, addr string, key string, blockNumber BlockNumber,
+) (hexutil.Bytes, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch state
+ state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum)
+ if state == nil || err != nil {
+ return nil, err
+ }
+ address := internal_common.ParseAddr(addr)
+ res := state.GetState(address, common.HexToHash(key))
+
+ // Response output is the same for all versions
+ return res[:], state.Error()
+}
+
+// docall executes an EVM call
+func doCall(
+ ctx context.Context, hmy *hmy.Harmony, args CallArgs, blockNum 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())
+
+ // Fetch state
+ state, header, err := hmy.StateAndHeaderByNumber(ctx, blockNum)
+ 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()
+ }
+
+ // Set value & data
+ value := new(big.Int)
+ if args.Value != nil {
+ value = args.Value.ToInt()
+ }
+ var data []byte
+ if args.Data != nil {
+ data = *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)
+ }
+
+ // Response output is the same for all versions
+ return res, gas, failed, err
+}
diff --git a/rpc/debug.go b/rpc/debug.go
new file mode 100644
index 000000000..090e68f14
--- /dev/null
+++ b/rpc/debug.go
@@ -0,0 +1,40 @@
+package rpc
+
+import (
+ "context"
+
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+ "github.com/harmony-one/harmony/internal/utils"
+)
+
+// PrivateDebugService Internal JSON RPC for debugging purpose
+type PrivateDebugService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPrivateDebugAPI creates a new API for the RPC interface
+// TODO(dm): expose public via config
+func NewPrivateDebugAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PrivateDebugService{hmy, version},
+ Public: false,
+ }
+}
+
+// SetLogVerbosity Sets log verbosity on runtime
+// Example usage:
+// curl -H "Content-Type: application/json" -d '{"method":"debug_setLogVerbosity","params":[0],"id":1}' http://localhost:9123
+func (*PrivateDebugService) SetLogVerbosity(ctx context.Context, level int) (map[string]interface{}, error) {
+ if level < int(log.LvlCrit) || level > int(log.LvlTrace) {
+ return nil, ErrInvalidLogLevel
+ }
+
+ verbosity := log.Lvl(level)
+ utils.SetLogVerbosity(verbosity)
+ return map[string]interface{}{"verbosity": verbosity.String()}, nil
+}
diff --git a/internal/hmyapi/apiv2/error.go b/rpc/error.go
similarity index 69%
rename from internal/hmyapi/apiv2/error.go
rename to rpc/error.go
index b80d49da3..cfe653592 100644
--- a/internal/hmyapi/apiv2/error.go
+++ b/rpc/error.go
@@ -1,4 +1,4 @@
-package apiv2
+package rpc
import (
"errors"
@@ -15,4 +15,8 @@ var (
ErrNotBeaconShard = errors.New("cannot call this rpc on non beaconchain node")
// ErrRequestedBlockTooHigh when given block is greater than latest block number
ErrRequestedBlockTooHigh = errors.New("requested block number greater than current block number")
+ // ErrUnknownRPCVersion when rpc method has an unknown or unhandled version
+ ErrUnknownRPCVersion = errors.New("API service has an unknown version")
+ // ErrTransactionNotFound when attempting to get a transaction that does exist or has not been finalized
+ ErrTransactionNotFound = errors.New("transaction not found")
)
diff --git a/internal/hmyapi/filters/api.go b/rpc/filters/api.go
similarity index 100%
rename from internal/hmyapi/filters/api.go
rename to rpc/filters/api.go
diff --git a/internal/hmyapi/filters/filter.go b/rpc/filters/filter.go
similarity index 100%
rename from internal/hmyapi/filters/filter.go
rename to rpc/filters/filter.go
diff --git a/internal/hmyapi/filters/filter_criteria.go b/rpc/filters/filter_criteria.go
similarity index 100%
rename from internal/hmyapi/filters/filter_criteria.go
rename to rpc/filters/filter_criteria.go
diff --git a/internal/hmyapi/filters/filter_system.go b/rpc/filters/filter_system.go
similarity index 100%
rename from internal/hmyapi/filters/filter_system.go
rename to rpc/filters/filter_system.go
diff --git a/rpc/harmony.go b/rpc/harmony.go
new file mode 100644
index 000000000..278e4ab18
--- /dev/null
+++ b/rpc/harmony.go
@@ -0,0 +1,88 @@
+package rpc
+
+import (
+ "context"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+)
+
+// PublicHarmonyService provides an API to access Harmony related information.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicHarmonyService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicHarmonyAPI creates a new API for the RPC interface
+func NewPublicHarmonyAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicHarmonyService{hmy, version},
+ Public: true,
+ }
+}
+
+// ProtocolVersion returns the current Harmony protocol version this node supports
+// Note that the return type is an interface to account for the different versions
+func (s *PublicHarmonyService) ProtocolVersion(
+ ctx context.Context,
+) (interface{}, error) {
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(s.hmy.ProtocolVersion()), nil
+ case V2:
+ return s.hmy.ProtocolVersion(), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
+// yet received the latest block headers from its pears. In case it is synchronizing:
+// - startingBlock: block number this node started to synchronise from
+// - currentBlock: block number this node is currently importing
+// - highestBlock: block number of the highest block header this node has received from peers
+// - pulledStates: number of state entries processed until now
+// - knownStates: number of known state entries that still need to be pulled
+func (s *PublicHarmonyService) Syncing(
+ ctx context.Context,
+) (interface{}, error) {
+ // TODO(dm): find our Downloader module for syncing blocks
+ return false, nil
+}
+
+// GasPrice returns a suggestion for a gas price.
+// Note that the return type is an interface to account for the different versions
+func (s *PublicHarmonyService) GasPrice(ctx context.Context) (interface{}, error) {
+ // TODO(dm): add SuggestPrice API
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return (*hexutil.Big)(big.NewInt(1)), nil
+ case V2:
+ return 1, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetNodeMetadata produces a NodeMetadata record, data is from the answering RPC node
+func (s *PublicHarmonyService) GetNodeMetadata(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ // Response output is the same for all versions
+ return NewStructuredResponse(s.hmy.GetNodeMetadata())
+}
+
+// GetPeerInfo produces a NodePeerInfo record
+func (s *PublicHarmonyService) GetPeerInfo(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ // Response output is the same for all versions
+ return NewStructuredResponse(s.hmy.GetPeerInfo())
+}
diff --git a/rpc/net.go b/rpc/net.go
new file mode 100644
index 000000000..3a13f5354
--- /dev/null
+++ b/rpc/net.go
@@ -0,0 +1,59 @@
+package rpc
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/internal/utils"
+ "github.com/harmony-one/harmony/p2p"
+)
+
+// PublicNetService offers network related RPC methods
+type PublicNetService struct {
+ net p2p.Host
+ chainID uint64
+ version Version
+}
+
+// NewPublicNetAPI creates a new net API instance.
+func NewPublicNetAPI(net p2p.Host, chainID uint64, version Version) rpc.API {
+ // manually set different namespace to preserve legacy behavior
+ var namespace string
+ switch version {
+ case V1:
+ namespace = netV1Namespace
+ case V2:
+ namespace = netV2Namespace
+ default:
+ utils.Logger().Error().Msgf("Unknown version %v, ignoring API.", version)
+ return rpc.API{}
+ }
+
+ return rpc.API{
+ Namespace: namespace,
+ Version: APIVersion,
+ Service: &PublicNetService{net, chainID, version},
+ Public: true,
+ }
+}
+
+// PeerCount returns the number of connected peers
+// Note that the return type is an interface to account for the different versions
+func (s *PublicNetService) PeerCount(ctx context.Context) (interface{}, error) {
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(s.net.GetPeerCount()), nil
+ case V2:
+ return s.net.GetPeerCount(), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// Version returns the network version, i.e. ChainID identifying which network we are using
+func (s *PublicNetService) Version(ctx context.Context) string {
+ return fmt.Sprintf("%d", s.chainID)
+}
diff --git a/rpc/pool.go b/rpc/pool.go
new file mode 100644
index 000000000..39a99d005
--- /dev/null
+++ b/rpc/pool.go
@@ -0,0 +1,295 @@
+package rpc
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/core"
+ "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"
+ v1 "github.com/harmony-one/harmony/rpc/v1"
+ v2 "github.com/harmony-one/harmony/rpc/v2"
+ staking "github.com/harmony-one/harmony/staking/types"
+)
+
+// PublicPoolService provides an API to access the Harmony node's transaction pool.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicPoolService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicPoolAPI creates a new API for the RPC interface
+func NewPublicPoolAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicPoolService{hmy, version},
+ Public: true,
+ }
+}
+
+// SendRawTransaction will add the signed transaction to the transaction pool.
+// The sender is responsible for signing the transaction and using the correct nonce.
+func (s *PublicPoolService) SendRawTransaction(
+ ctx context.Context, encodedTx hexutil.Bytes,
+) (common.Hash, error) {
+ // DOS prevention
+ if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
+ err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
+ return common.Hash{}, err
+ }
+
+ // Verify transaction type & chain
+ tx := new(types.Transaction)
+ if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
+ return common.Hash{}, err
+ }
+ 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(),
+ )
+ }
+
+ // Submit transaction
+ if err := s.hmy.SendTx(ctx, tx); err != nil {
+ utils.Logger().Warn().Err(err).Msg("Could not submit transaction")
+ return tx.Hash(), err
+ }
+
+ // Log submission
+ if tx.To() == nil {
+ signer := types.MakeSigner(s.hmy.ChainConfig(), s.hmy.CurrentBlock().Epoch())
+ from, err := types.Sender(signer, tx)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ addr := crypto.CreateAddress(from, tx.Nonce())
+ utils.Logger().Info().
+ Str("fullhash", tx.Hash().Hex()).
+ Str("contract", common2.MustAddressToBech32(addr)).
+ Msg("Submitted contract creation")
+ } else {
+ utils.Logger().Info().
+ Str("fullhash", tx.Hash().Hex()).
+ Str("recipient", tx.To().Hex()).
+ Msg("Submitted transaction")
+ }
+
+ // Response output is the same for all versions
+ return tx.Hash(), nil
+}
+
+// SendRawStakingTransaction will add the signed transaction to the transaction pool.
+// The sender is responsible for signing the transaction and using the correct nonce.
+func (s *PublicPoolService) SendRawStakingTransaction(
+ ctx context.Context, encodedTx hexutil.Bytes,
+) (common.Hash, error) {
+ // DOS prevention
+ if len(encodedTx) >= types.MaxEncodedPoolTransactionSize {
+ err := errors.Wrapf(core.ErrOversizedData, "encoded tx size: %d", len(encodedTx))
+ return common.Hash{}, err
+ }
+
+ // Verify staking transaction type & chain
+ tx := new(staking.StakingTransaction)
+ if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
+ return common.Hash{}, err
+ }
+ 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(),
+ )
+ }
+
+ // Submit transaction
+ if err := s.hmy.SendStakingTx(ctx, tx); err != nil {
+ utils.Logger().Warn().Err(err).Msg("Could not submit staking transaction")
+ return tx.Hash(), err
+ }
+
+ // Log submission
+ utils.Logger().Info().
+ Str("fullhash", tx.Hash().Hex()).
+ Msg("Submitted Staking transaction")
+
+ // Response output is the same for all versions
+ return tx.Hash(), nil
+}
+
+// GetPoolStats returns stats for the tx-pool
+func (s *PublicPoolService) GetPoolStats(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ pendingCount, queuedCount := s.hmy.GetPoolStats()
+
+ // Response output is the same for all versions
+ return StructuredResponse{
+ "executable-count": pendingCount,
+ "non-executable-count": queuedCount,
+ }, nil
+}
+
+// PendingTransactions returns the plain transactions that are in the transaction pool
+func (s *PublicPoolService) PendingTransactions(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // Fetch all pending transactions (stx & plain tx)
+ pending, err := s.hmy.GetPoolTransactions()
+ if err != nil {
+ return nil, err
+ }
+
+ // Only format and return plain transactions according to the version
+ transactions := []StructuredResponse{}
+ for i := range pending {
+ if plainTx, ok := pending[i].(*types.Transaction); ok {
+ var tx interface{}
+ switch s.version {
+ case V1:
+ tx, err = v1.NewTransaction(plainTx, common.Hash{}, 0, 0, 0)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingTransactions")
+ continue // Legacy behavior is to not return error here
+ }
+ case V2:
+ tx, err = v2.NewTransaction(plainTx, common.Hash{}, 0, 0, 0)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingTransactions")
+ continue // Legacy behavior is to not return error here
+ }
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ rpcTx, err := NewStructuredResponse(tx)
+ if err == nil {
+ transactions = append(transactions, rpcTx)
+ } else {
+ // Legacy behavior is to not return error here
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingTransactions")
+ }
+ } else if _, ok := pending[i].(*staking.StakingTransaction); ok {
+ continue // Do not return staking transactions here.
+ } else {
+ return nil, types.ErrUnknownPoolTxType
+ }
+ }
+ return transactions, nil
+}
+
+// PendingStakingTransactions returns the staking transactions that are in the transaction pool
+func (s *PublicPoolService) PendingStakingTransactions(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // Fetch all pending transactions (stx & plain tx)
+ pending, err := s.hmy.GetPoolTransactions()
+ if err != nil {
+ return nil, err
+ }
+
+ // Only format and return staking transactions according to the version
+ transactions := []StructuredResponse{}
+ for i := range pending {
+ if _, ok := pending[i].(*types.Transaction); ok {
+ continue // Do not return plain transactions here
+ } else if stakingTx, ok := pending[i].(*staking.StakingTransaction); ok {
+ var tx interface{}
+ switch s.version {
+ case V1:
+ tx, err = v1.NewStakingTransaction(stakingTx, common.Hash{}, 0, 0, 0)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingStakingTransactions")
+ continue // Legacy behavior is to not return error here
+ }
+ case V2:
+ tx, err = v2.NewStakingTransaction(stakingTx, common.Hash{}, 0, 0, 0)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingStakingTransactions")
+ continue // Legacy behavior is to not return error here
+ }
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ rpcTx, err := NewStructuredResponse(tx)
+ if err == nil {
+ transactions = append(transactions, rpcTx)
+ } else {
+ // Legacy behavior is to not return error here
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "PendingStakingTransactions")
+ }
+ } else {
+ return nil, types.ErrUnknownPoolTxType
+ }
+ }
+ return transactions, nil
+}
+
+// GetCurrentTransactionErrorSink ..
+func (s *PublicPoolService) GetCurrentTransactionErrorSink(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // For each transaction error in the error sink, format the response (same format for all versions)
+ formattedErrors := []StructuredResponse{}
+ for _, txError := range s.hmy.GetCurrentTransactionErrorSink() {
+ formattedErr, err := NewStructuredResponse(txError)
+ if err != nil {
+ return nil, err
+ }
+ formattedErrors = append(formattedErrors, formattedErr)
+ }
+ return formattedErrors, nil
+}
+
+// GetCurrentStakingErrorSink ..
+func (s *PublicPoolService) GetCurrentStakingErrorSink(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // For each staking tx error in the error sink, format the response (same format for all versions)
+ formattedErrors := []StructuredResponse{}
+ for _, txErr := range s.hmy.GetCurrentStakingErrorSink() {
+ formattedErr, err := NewStructuredResponse(txErr)
+ if err != nil {
+ return nil, err
+ }
+ formattedErrors = append(formattedErrors, formattedErr)
+ }
+ return formattedErrors, nil
+}
+
+// GetPendingCXReceipts ..
+func (s *PublicPoolService) GetPendingCXReceipts(
+ ctx context.Context,
+) ([]StructuredResponse, error) {
+ // For each cx receipt, format the response (same format for all versions)
+ formattedReceipts := []StructuredResponse{}
+ for _, receipts := range s.hmy.GetPendingCXReceipts() {
+ formattedReceipt, err := NewStructuredResponse(receipts)
+ if err != nil {
+ return nil, err
+ }
+ formattedReceipts = append(formattedReceipts, formattedReceipt)
+ }
+ return formattedReceipts, nil
+}
diff --git a/rpc/rpc.go b/rpc/rpc.go
new file mode 100644
index 000000000..5bb3f7c7f
--- /dev/null
+++ b/rpc/rpc.go
@@ -0,0 +1,167 @@
+package rpc
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+ nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
+ "github.com/harmony-one/harmony/internal/utils"
+ v1 "github.com/harmony-one/harmony/rpc/v1"
+ v2 "github.com/harmony-one/harmony/rpc/v2"
+)
+
+// Version enum
+const (
+ V1 Version = iota
+ V2
+)
+
+const (
+ // APIVersion used for DApp's, bumped after RPC refactor (7/2020)
+ APIVersion = "1.1"
+ // CallTimeout is the timeout given to all contract calls
+ CallTimeout = 5 * time.Second
+ // LogTag is the tag found in the log for all RPC logs
+ LogTag = "[RPC]"
+ // HTTPPortOffset ..
+ HTTPPortOffset = 500
+ // WSPortOffset ..
+ WSPortOffset = 800
+
+ netV1Namespace = "net"
+ netV2Namespace = "netv2"
+)
+
+var (
+ // HTTPModules ..
+ HTTPModules = []string{"hmy", "hmyv2", netV1Namespace, netV2Namespace, "explorer"}
+ // WSModules ..
+ WSModules = []string{"hmy", "hmyv2", netV1Namespace, netV2Namespace, "web3"}
+
+ httpListener net.Listener
+ httpHandler *rpc.Server
+ wsListener net.Listener
+ wsHandler *rpc.Server
+ httpEndpoint = ""
+ wsEndpoint = ""
+ httpVirtualHosts = []string{"*"}
+ httpTimeouts = rpc.DefaultHTTPTimeouts
+ httpOrigins = []string{"*"}
+ wsOrigins = []string{"*"}
+)
+
+// Version of the RPC
+type Version int
+
+// Namespace of the RPC version
+func (n Version) Namespace() string {
+ return HTTPModules[n]
+}
+
+// StartServers starts the http & ws servers
+func StartServers(hmy *hmy.Harmony, port int, apis []rpc.API) error {
+ ip := ""
+ if !nodeconfig.GetPublicRPC() {
+ ip = "127.0.0.1"
+ }
+
+ apis = append(apis, getAPIs(hmy)...)
+
+ httpEndpoint = fmt.Sprintf("%v:%v", ip, port+HTTPPortOffset)
+ if err := startHTTP(apis); err != nil {
+ return err
+ }
+ wsEndpoint = fmt.Sprintf("%v:%v", ip, port+WSPortOffset)
+ if err := startWS(apis); err != nil {
+ return err
+ }
+ return nil
+}
+
+// StopServers stops the http & ws servers
+func StopServers() error {
+ if httpListener != nil {
+ if err := httpListener.Close(); err != nil {
+ return err
+ }
+ httpListener = nil
+ utils.Logger().Info().
+ Str("url", fmt.Sprintf("http://%s", httpEndpoint)).
+ Msg("HTTP endpoint closed")
+ }
+ if httpHandler != nil {
+ httpHandler.Stop()
+ httpHandler = nil
+ }
+ if wsListener != nil {
+ if err := wsListener.Close(); err != nil {
+ return err
+ }
+ wsListener = nil
+ utils.Logger().Info().
+ Str("url", fmt.Sprintf("http://%s", wsEndpoint)).
+ Msg("WS endpoint closed")
+ }
+ if wsHandler != nil {
+ wsHandler.Stop()
+ wsHandler = nil
+ }
+ return nil
+}
+
+// getAPIs returns all the API methods for the RPC interface
+func getAPIs(hmy *hmy.Harmony) []rpc.API {
+ return []rpc.API{
+ // Public methods
+ NewPublicHarmonyAPI(hmy, V1),
+ NewPublicHarmonyAPI(hmy, V2),
+ NewPublicBlockchainAPI(hmy, V1),
+ NewPublicBlockchainAPI(hmy, V2),
+ NewPublicContractAPI(hmy, V1),
+ NewPublicContractAPI(hmy, V2),
+ NewPublicTransactionAPI(hmy, V1),
+ NewPublicTransactionAPI(hmy, V2),
+ NewPublicPoolAPI(hmy, V1),
+ NewPublicPoolAPI(hmy, V2),
+ NewPublicStakingAPI(hmy, V1),
+ NewPublicStakingAPI(hmy, V2),
+ // Private methods
+ NewPrivateDebugAPI(hmy, V1),
+ NewPrivateDebugAPI(hmy, V2),
+ // Legacy methods (subject to removal)
+ v1.NewPublicLegacyAPI(hmy),
+ v2.NewPublicLegacyAPI(hmy),
+ }
+}
+
+func startHTTP(apis []rpc.API) (err error) {
+ httpListener, httpHandler, err = rpc.StartHTTPEndpoint(
+ httpEndpoint, apis, HTTPModules, httpOrigins, httpVirtualHosts, httpTimeouts,
+ )
+ if err != nil {
+ return err
+ }
+
+ utils.Logger().Info().
+ Str("url", fmt.Sprintf("http://%s", httpEndpoint)).
+ Str("cors", strings.Join(httpOrigins, ",")).
+ Str("vhosts", strings.Join(httpVirtualHosts, ",")).
+ Msg("HTTP endpoint opened")
+ return nil
+}
+
+func startWS(apis []rpc.API) (err error) {
+ wsListener, wsHandler, err = rpc.StartWSEndpoint(wsEndpoint, apis, WSModules, wsOrigins, true)
+ if err != nil {
+ return err
+ }
+
+ utils.Logger().Info().
+ Str("url", fmt.Sprintf("ws://%s", wsListener.Addr())).
+ Msg("WebSocket endpoint opened")
+ return nil
+}
diff --git a/rpc/staking.go b/rpc/staking.go
new file mode 100644
index 000000000..c4f172016
--- /dev/null
+++ b/rpc/staking.go
@@ -0,0 +1,621 @@
+package rpc
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+
+ "github.com/pkg/errors"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+ "github.com/harmony-one/harmony/shard"
+)
+
+const (
+ validatorsPageSize = 100
+)
+
+// PublicStakingService provides an API to access Harmony's staking services.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicStakingService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicStakingAPI creates a new API for the RPC interface
+func NewPublicStakingAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicStakingService{hmy, version},
+ Public: true,
+ }
+}
+
+// getBalanceByBlockNumber returns balance by block number at given eth blockNum without checks
+func (s *PublicStakingService) getBalanceByBlockNumber(
+ ctx context.Context, address string, blockNum rpc.BlockNumber,
+) (*big.Int, error) {
+ addr := internal_common.ParseAddr(address)
+ balance, err := s.hmy.GetBalance(ctx, addr, blockNum)
+ if err != nil {
+ return nil, err
+ }
+ return balance, nil
+}
+
+// getAllValidatorInformation helper function to get all validator information for a given eth block number
+func (s *PublicStakingService) getAllValidatorInformation(
+ ctx context.Context, page int, blockNum rpc.BlockNumber,
+) ([]StructuredResponse, error) {
+ if page < -1 {
+ return nil, errors.Errorf("page given %d cannot be less than -1", page)
+ }
+
+ // Get all validators
+ addresses := s.hmy.GetAllValidatorAddresses()
+ if page != -1 && len(addresses) <= page*validatorsPageSize {
+ return []StructuredResponse{}, nil
+ }
+
+ // Set page start
+ validatorsNum := len(addresses)
+ start := 0
+ if page != -1 {
+ validatorsNum = validatorsPageSize
+ start = page * validatorsPageSize
+ if len(addresses)-start < validatorsPageSize {
+ validatorsNum = len(addresses) - start
+ }
+ }
+
+ // Fetch block
+ blk, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum)
+ }
+
+ // Fetch validator information for block
+ validators := []StructuredResponse{}
+ for i := start; i < start+validatorsNum; i++ {
+ validatorInfo, err := s.hmy.GetValidatorInformation(addresses[i], blk)
+ if err == nil {
+ // Response output is the same for all versions
+ information, err := NewStructuredResponse(validatorInfo)
+ if err != nil {
+ return nil, err
+ }
+ validators = append(validators, information)
+ }
+ }
+ return validators, nil
+}
+
+// GetTotalStaking returns total staking by validators, only meant to be called on beaconchain
+// explorer node
+func (s *PublicStakingService) GetTotalStaking(
+ ctx context.Context,
+) (*big.Int, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Response output is the same for all versions
+ return s.hmy.GetTotalStakingSnapshot(), nil
+}
+
+// GetMedianRawStakeSnapshot returns the raw median stake, only meant to be called on beaconchain
+// explorer node
+func (s *PublicStakingService) GetMedianRawStakeSnapshot(
+ ctx context.Context,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch snapshot
+ snapshot, err := s.hmy.GetMedianRawStakeSnapshot()
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(snapshot)
+}
+
+// GetElectedValidatorAddresses returns elected validator addresses.
+func (s *PublicStakingService) GetElectedValidatorAddresses(
+ ctx context.Context,
+) ([]string, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch elected validators
+ electedAddresses := s.hmy.GetElectedValidatorAddresses()
+ addresses := make([]string, len(electedAddresses))
+ for i, addr := range electedAddresses {
+ oneAddr, _ := internal_common.AddressToBech32(addr)
+ // Response output is the same for all versions
+ addresses[i] = oneAddr
+ }
+ return addresses, nil
+}
+
+// GetValidators returns validators list for a particular epoch.
+func (s *PublicStakingService) GetValidators(
+ ctx context.Context, epoch int64,
+) (StructuredResponse, error) {
+ // Fetch the Committee
+ cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
+ if err != nil {
+ return nil, err
+ }
+ balanceQueryBlock := shard.Schedule.EpochLastBlock(uint64(epoch))
+ if balanceQueryBlock > s.hmy.CurrentBlock().NumberU64() {
+ balanceQueryBlock = s.hmy.CurrentBlock().NumberU64()
+ }
+
+ validators := []StructuredResponse{}
+ for _, validator := range cmt.Slots {
+ // Fetch the balance of the validator
+ oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
+ if err != nil {
+ return nil, err
+ }
+ validatorBalance, err := s.getBalanceByBlockNumber(ctx, oneAddress, rpc.BlockNumber(balanceQueryBlock))
+ if err != nil {
+ return nil, err
+ }
+
+ // Format the response according to the version
+ var validatorsFields StructuredResponse
+ switch s.version {
+ case V1:
+ validatorsFields = StructuredResponse{
+ "address": oneAddress,
+ "balance": (*hexutil.Big)(validatorBalance),
+ }
+ case V2:
+ validatorsFields = StructuredResponse{
+ "address": oneAddress,
+ "balance": validatorBalance,
+ }
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ validators = append(validators, validatorsFields)
+ }
+ result := StructuredResponse{
+ "shardID": cmt.ShardID,
+ "validators": validators,
+ }
+ return result, nil
+}
+
+// GetAllValidatorAddresses returns all validator addresses.
+func (s *PublicStakingService) GetAllValidatorAddresses(
+ ctx context.Context,
+) ([]string, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch all validator addresses
+ validatorAddresses := s.hmy.GetAllValidatorAddresses()
+ addresses := make([]string, len(validatorAddresses))
+ for i, addr := range validatorAddresses {
+ oneAddr, _ := internal_common.AddressToBech32(addr)
+ // Response output is the same for all versions
+ addresses[i] = oneAddr
+ }
+ return addresses, nil
+}
+
+// GetValidatorKeys returns list of bls public keys in the committee for a particular epoch.
+func (s *PublicStakingService) GetValidatorKeys(
+ ctx context.Context, epoch int64,
+) ([]string, error) {
+ // Fetch the Committee
+ cmt, err := s.hmy.GetValidators(big.NewInt(epoch))
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ validators := make([]string, len(cmt.Slots))
+ for i, v := range cmt.Slots {
+ validators[i] = v.BLSPublicKey.Hex()
+ }
+ return validators, nil
+}
+
+// GetAllValidatorInformation returns information about all validators.
+// If page is -1, return all instead of `validatorsPageSize` elements.
+func (s *PublicStakingService) GetAllValidatorInformation(
+ ctx context.Context, page int,
+) ([]StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // fetch current block number
+ blockNum := s.hmy.CurrentBlock().NumberU64()
+
+ // delete cache for previous block
+ prevKey := fmt.Sprintf("all-info-%d", blockNum-1)
+ s.hmy.SingleFlightForgetKey(prevKey)
+
+ // Fetch all validator information in a single flight request
+ key := fmt.Sprintf("all-info-%d", blockNum)
+ res, err := s.hmy.SingleFlightRequest(
+ key,
+ func() (interface{}, error) {
+ return s.getAllValidatorInformation(ctx, page, rpc.LatestBlockNumber)
+ },
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return res.([]StructuredResponse), nil
+}
+
+// GetAllValidatorInformationByBlockNumber returns information about all validators.
+// If page is -1, return all instead of `validatorsPageSize` elements.
+func (s *PublicStakingService) GetAllValidatorInformationByBlockNumber(
+ ctx context.Context, page int, blockNumber BlockNumber,
+) ([]StructuredResponse, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+
+ // Response output is the same for all versions
+ return s.getAllValidatorInformation(ctx, page, blockNum)
+}
+
+// GetValidatorInformation returns information about a validator.
+func (s *PublicStakingService) GetValidatorInformation(
+ ctx context.Context, address string,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch latest block
+ blk, err := s.hmy.BlockByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not retrieve the latest blk information")
+ }
+
+ // Fetch validator information
+ validatorInfo, err := s.hmy.GetValidatorInformation(internal_common.ParseAddr(address), blk)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(validatorInfo)
+}
+
+// GetValidatorInformationByBlockNumber returns information about a validator.
+func (s *PublicStakingService) GetValidatorInformationByBlockNumber(
+ ctx context.Context, address string, blockNumber BlockNumber,
+) (StructuredResponse, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch block
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+ blk, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum)
+ }
+
+ // Fetch validator info
+ validatorInfo, err := s.hmy.GetValidatorInformation(internal_common.ParseAddr(address), blk)
+ if err != nil {
+ return nil, err
+ }
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(validatorInfo)
+}
+
+// GetValidatorSelfDelegation returns validator stake.
+func (s *PublicStakingService) GetValidatorSelfDelegation(
+ ctx context.Context, address string,
+) (interface{}, error) {
+ // Ensure node is for beacon shard
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch self delegation
+ selfDelegation := s.hmy.GetValidatorSelfDelegation(internal_common.ParseAddr(address)).Uint64()
+
+ // Format the response according to the version
+ switch s.version {
+ case V1:
+ return hexutil.Uint64(selfDelegation), nil
+ case V2:
+ return selfDelegation, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetValidatorTotalDelegation returns total balance stacking for validator with delegation.
+func (s *PublicStakingService) GetValidatorTotalDelegation(
+ ctx context.Context, address string,
+) (interface{}, error) {
+ // Ensure node is for beacon shard
+ if s.hmy.ShardID != shard.BeaconChainShardID {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch delegations & sum
+ delegations := s.hmy.GetDelegationsByValidator(internal_common.ParseAddr(address))
+ totalStake := big.NewInt(0)
+ for _, delegation := range delegations {
+ totalStake.Add(totalStake, delegation.Amount)
+ }
+
+ // Format the response according to the version
+ switch s.version {
+ case V1:
+ return hexutil.Uint64(totalStake.Uint64()), nil
+ case V2:
+ return totalStake, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetAllDelegationInformation returns delegation information about `validatorsPageSize` validators,
+// starting at `page*validatorsPageSize`.
+// If page is -1, return all instead of `validatorsPageSize` elements.
+// TODO(dm): optimize with single flight
+func (s *PublicStakingService) GetAllDelegationInformation(
+ ctx context.Context, page int,
+) ([][]StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+ if page < -1 {
+ return make([][]StructuredResponse, 0), nil
+ }
+
+ // Get all validators
+ addresses := s.hmy.GetAllValidatorAddresses()
+
+ // Return nothing if no delegation on page
+ if page != -1 && len(addresses) <= page*validatorsPageSize {
+ return make([][]StructuredResponse, 0), nil
+ }
+
+ // Set page start
+ validatorsNum := len(addresses)
+ start := 0
+ if page != -1 {
+ validatorsNum = validatorsPageSize
+ start = page * validatorsPageSize
+ if len(addresses)-start < validatorsPageSize {
+ validatorsNum = len(addresses) - start
+ }
+ }
+
+ // Fetch all delegations
+ validators := make([][]StructuredResponse, validatorsNum)
+ var err error
+ for i := start; i < start+validatorsNum; i++ {
+ validators[i-start], err = s.GetDelegationsByValidator(ctx, addresses[i].String())
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Response output is the same for all versions
+ return validators, nil
+}
+
+// GetDelegationsByDelegator returns list of delegations for a delegator address.
+func (s *PublicStakingService) GetDelegationsByDelegator(
+ ctx context.Context, address string,
+) ([]StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch delegation
+ delegatorAddress := internal_common.ParseAddr(address)
+ validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
+
+ // Format response
+ result := []StructuredResponse{}
+ for i := range delegations {
+ delegation := delegations[i]
+ undelegations := make([]Undelegation, len(delegation.Undelegations))
+
+ for j := range delegation.Undelegations {
+ undelegations = append(undelegations, Undelegation{
+ Amount: delegation.Undelegations[j].Amount,
+ Epoch: delegation.Undelegations[j].Epoch,
+ })
+ }
+ valAddr, _ := internal_common.AddressToBech32(validators[i])
+ delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
+
+ // Response output is the same for all versions
+ del, err := NewStructuredResponse(Delegation{
+ ValidatorAddress: valAddr,
+ DelegatorAddress: delAddr,
+ Amount: delegation.Amount,
+ Reward: delegation.Reward,
+ Undelegations: undelegations,
+ })
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, del)
+ }
+ return result, nil
+}
+
+// GetDelegationsByDelegatorByBlockNumber returns list of delegations for a delegator address at given block number
+func (s *PublicStakingService) GetDelegationsByDelegatorByBlockNumber(
+ ctx context.Context, address string, blockNumber BlockNumber,
+) ([]StructuredResponse, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+ if isBlockGreaterThanLatest(s.hmy, blockNum) {
+ return nil, ErrRequestedBlockTooHigh
+ }
+
+ // Fetch delegation for block number
+ delegatorAddress := internal_common.ParseAddr(address)
+ blk, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, errors.Wrapf(err, "could not retrieve the blk information for blk number: %d", blockNum)
+ }
+ validators, delegations := s.hmy.GetDelegationsByDelegatorByBlock(delegatorAddress, blk)
+
+ // Format response
+ result := []StructuredResponse{}
+ for i := range delegations {
+ delegation := delegations[i]
+ undelegations := make([]Undelegation, len(delegation.Undelegations))
+
+ for j := range delegation.Undelegations {
+ undelegations[j] = Undelegation{
+ Amount: delegation.Undelegations[j].Amount,
+ Epoch: delegation.Undelegations[j].Epoch,
+ }
+ }
+ valAddr, _ := internal_common.AddressToBech32(validators[i])
+ delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
+
+ // Response output is the same for all versions
+ del, err := NewStructuredResponse(Delegation{
+ ValidatorAddress: valAddr,
+ DelegatorAddress: delAddr,
+ Amount: delegation.Amount,
+ Reward: delegation.Reward,
+ Undelegations: undelegations,
+ })
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, del)
+ }
+ return result, nil
+}
+
+// GetDelegationsByValidator returns list of delegations for a validator address.
+func (s *PublicStakingService) GetDelegationsByValidator(
+ ctx context.Context, address string,
+) ([]StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch delegations
+ validatorAddress := internal_common.ParseAddr(address)
+ delegations := s.hmy.GetDelegationsByValidator(validatorAddress)
+
+ // Format response
+ result := []StructuredResponse{}
+ for i := range delegations {
+ delegation := delegations[i]
+ undelegations := []Undelegation{}
+
+ for j := range delegation.Undelegations {
+ undelegations = append(undelegations, Undelegation{
+ Amount: delegation.Undelegations[j].Amount,
+ Epoch: delegation.Undelegations[j].Epoch,
+ })
+ }
+ valAddr, _ := internal_common.AddressToBech32(validatorAddress)
+ delAddr, _ := internal_common.AddressToBech32(delegation.DelegatorAddress)
+
+ // Response output is the same for all versions
+ del, err := NewStructuredResponse(Delegation{
+ ValidatorAddress: valAddr,
+ DelegatorAddress: delAddr,
+ Amount: delegation.Amount,
+ Reward: delegation.Reward,
+ Undelegations: undelegations,
+ })
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, del)
+ }
+ return result, nil
+}
+
+// GetDelegationByDelegatorAndValidator returns a delegation for delegator and validator.
+func (s *PublicStakingService) GetDelegationByDelegatorAndValidator(
+ ctx context.Context, address string, validator string,
+) (StructuredResponse, error) {
+ if !isBeaconShard(s.hmy) {
+ return nil, ErrNotBeaconShard
+ }
+
+ // Fetch delegations
+ delegatorAddress := internal_common.ParseAddr(address)
+ validatorAddress := internal_common.ParseAddr(validator)
+ validators, delegations := s.hmy.GetDelegationsByDelegator(delegatorAddress)
+
+ // Format response
+ for i := range delegations {
+ if validators[i] != validatorAddress {
+ continue
+ }
+ delegation := delegations[i]
+
+ undelegations := []Undelegation{}
+
+ for j := range delegation.Undelegations {
+ undelegations = append(undelegations, Undelegation{
+ Amount: delegation.Undelegations[j].Amount,
+ Epoch: delegation.Undelegations[j].Epoch,
+ })
+ }
+ valAddr, _ := internal_common.AddressToBech32(validatorAddress)
+ delAddr, _ := internal_common.AddressToBech32(delegatorAddress)
+
+ // Response output is the same for all versions
+ return NewStructuredResponse(Delegation{
+ ValidatorAddress: valAddr,
+ DelegatorAddress: delAddr,
+ Amount: delegation.Amount,
+ Reward: delegation.Reward,
+ Undelegations: undelegations,
+ })
+ }
+ return nil, nil
+}
+
+func isBeaconShard(hmy *hmy.Harmony) bool {
+ return hmy.ShardID == shard.BeaconChainShardID
+}
diff --git a/rpc/transaction.go b/rpc/transaction.go
new file mode 100644
index 000000000..59cf3234c
--- /dev/null
+++ b/rpc/transaction.go
@@ -0,0 +1,747 @@
+package rpc
+
+import (
+ "context"
+ "fmt"
+ "math/big"
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/core/rawdb"
+ "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"
+ v1 "github.com/harmony-one/harmony/rpc/v1"
+ v2 "github.com/harmony-one/harmony/rpc/v2"
+ staking "github.com/harmony-one/harmony/staking/types"
+)
+
+const (
+ defaultPageSize = uint32(100)
+)
+
+// PublicTransactionService provides an API to access Harmony's transaction service.
+// It offers only methods that operate on public data that is freely available to anyone.
+type PublicTransactionService struct {
+ hmy *hmy.Harmony
+ version Version
+}
+
+// NewPublicTransactionAPI creates a new API for the RPC interface
+func NewPublicTransactionAPI(hmy *hmy.Harmony, version Version) rpc.API {
+ return rpc.API{
+ Namespace: version.Namespace(),
+ Version: APIVersion,
+ Service: &PublicTransactionService{hmy, version},
+ Public: true,
+ }
+}
+
+// GetAccountNonce returns the nonce value of the given address for the given block number
+func (s *PublicTransactionService) GetAccountNonce(
+ ctx context.Context, address string, blockNumber BlockNumber,
+) (uint64, error) {
+ // Process number based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Response output is the same for all versions
+ addr := internal_common.ParseAddr(address)
+ return s.hmy.GetAccountNonce(ctx, addr, blockNum)
+}
+
+// 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
+// Note that the return type is an interface to account for the different versions
+func (s *PublicTransactionService) GetTransactionCount(
+ ctx context.Context, addr string, blockNumber BlockNumber,
+) (response interface{}, err error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+ address := internal_common.ParseAddr(addr)
+
+ // Fetch transaction count
+ var nonce uint64
+ if blockNum == rpc.PendingBlockNumber {
+ // Ask transaction pool for the nonce which includes pending transactions
+ nonce, err = s.hmy.GetPoolNonce(ctx, address)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ // Resolve block number and use its state to ask for the nonce
+ state, _, err := s.hmy.StateAndHeaderByNumber(ctx, blockNum)
+ if err != nil {
+ return nil, err
+ }
+ if state == nil {
+ return nil, fmt.Errorf("state not found")
+ }
+ if state.Error() != nil {
+ return nil, state.Error()
+ }
+ nonce = state.GetNonce(address)
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return (hexutil.Uint64)(nonce), nil
+ case V2:
+ return nonce, nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetTransactionsCount returns the number of regular transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
+func (s *PublicTransactionService) GetTransactionsCount(
+ ctx context.Context, address, txType string,
+) (count uint64, err error) {
+ if !strings.HasPrefix(address, "one1") {
+ // Handle hex address
+ addr := internal_common.ParseAddr(address)
+ address, err = internal_common.AddressToBech32(addr)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // Response output is the same for all versions
+ return s.hmy.GetTransactionsCount(address, txType)
+}
+
+// GetStakingTransactionsCount returns the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
+func (s *PublicTransactionService) GetStakingTransactionsCount(
+ ctx context.Context, address, txType string,
+) (count uint64, err error) {
+ if !strings.HasPrefix(address, "one1") {
+ // Handle hex address
+ addr := internal_common.ParseAddr(address)
+ address, err = internal_common.AddressToBech32(addr)
+ if err != nil {
+ return 0, err
+ }
+ }
+
+ // Response output is the same for all versions
+ return s.hmy.GetStakingTransactionsCount(address, txType)
+}
+
+// EstimateGas returns an estimate of the amount of gas needed to execute the
+// given transaction against the current pending block.
+func (s *PublicTransactionService) EstimateGas(
+ ctx context.Context, args CallArgs,
+) (hexutil.Uint64, error) {
+ gas, err := doEstimateGas(ctx, s.hmy, args, nil)
+ if err != nil {
+ return 0, err
+ }
+
+ // Response output is the same for all versions
+ return (hexutil.Uint64)(gas), nil
+}
+
+// GetTransactionByHash returns the plain transaction for the given hash
+func (s *PublicTransactionService) GetTransactionByHash(
+ ctx context.Context, hash common.Hash,
+) (StructuredResponse, error) {
+ // Try to return an already finalized transaction
+ tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.hmy.ChainDb(), hash)
+ if tx == nil {
+ utils.Logger().Debug().
+ Err(errors.Wrapf(ErrTransactionNotFound, "hash %v", hash.String())).
+ Msgf("%v error at %v", LogTag, "GetTransactionByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetTransactionByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format the response according to the version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetStakingTransactionByHash returns the staking transaction for the given hash
+func (s *PublicTransactionService) GetStakingTransactionByHash(
+ ctx context.Context, hash common.Hash,
+) (StructuredResponse, error) {
+ // Try to return an already finalized transaction
+ stx, blockHash, blockNumber, index := rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash)
+ if stx == nil {
+ utils.Logger().Debug().
+ Err(errors.Wrapf(ErrTransactionNotFound, "hash %v", hash.String())).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ switch s.version {
+ case V1:
+ tx, err := v1.NewStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetTransactionsHistory returns the list of transactions hashes that involve a particular address.
+func (s *PublicTransactionService) GetTransactionsHistory(
+ ctx context.Context, args TxHistoryArgs,
+) (StructuredResponse, error) {
+ // Fetch transaction history
+ var address string
+ var result []common.Hash
+ var err error
+ if strings.HasPrefix(args.Address, "one1") {
+ address = args.Address
+ } else {
+ addr := internal_common.ParseAddr(args.Address)
+ address, err = internal_common.AddressToBech32(addr)
+ if err != nil {
+ return nil, err
+ }
+ }
+ hashes, err := s.hmy.GetTransactionsHistory(address, args.TxType, args.Order)
+ if err != nil {
+ return nil, err
+ }
+
+ result = returnHashesWithPagination(hashes, args.PageIndex, args.PageSize)
+
+ // Just hashes have same response format for all versions
+ if !args.FullTx {
+ return StructuredResponse{"transactions": result}, nil
+ }
+
+ // Full transactions have different response format
+ txs := []StructuredResponse{}
+ for _, hash := range result {
+ tx, err := s.GetTransactionByHash(ctx, hash)
+ if err == nil {
+ txs = append(txs, tx)
+ } else {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetTransactionsHistory")
+ // Legacy behavior is to not return RPC errors
+ }
+ }
+ return StructuredResponse{"transactions": txs}, nil
+}
+
+// GetStakingTransactionsHistory returns the list of transactions hashes that involve a particular address.
+func (s *PublicTransactionService) GetStakingTransactionsHistory(
+ ctx context.Context, args TxHistoryArgs,
+) (StructuredResponse, error) {
+ // Fetch transaction history
+ var address string
+ var result []common.Hash
+ var err error
+ if strings.HasPrefix(args.Address, "one1") {
+ address = args.Address
+ } else {
+ addr := internal_common.ParseAddr(args.Address)
+ address, err = internal_common.AddressToBech32(addr)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionsHistory")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+ }
+ hashes, err := s.hmy.GetStakingTransactionsHistory(address, args.TxType, args.Order)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionsHistory")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ result = returnHashesWithPagination(hashes, args.PageIndex, args.PageSize)
+
+ // Just hashes have same response format for all versions
+ if !args.FullTx {
+ return StructuredResponse{"staking_transactions": result}, nil
+ }
+
+ // Full transactions have different response format
+ txs := []StructuredResponse{}
+ for _, hash := range result {
+ tx, err := s.GetStakingTransactionByHash(ctx, hash)
+ if err == nil {
+ txs = append(txs, tx)
+ } else {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionsHistory")
+ // Legacy behavior is to not return RPC errors
+ }
+ }
+ return StructuredResponse{"staking_transactions": txs}, nil
+}
+
+// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
+// Note that the return type is an interface to account for the different versions
+func (s *PublicTransactionService) GetBlockTransactionCountByNumber(
+ ctx context.Context, blockNumber BlockNumber,
+) (interface{}, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch block
+ block, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetBlockTransactionCountByNumber")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(len(block.Transactions())), nil
+ case V2:
+ return len(block.Transactions()), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
+// Note that the return type is an interface to account for the different versions
+func (s *PublicTransactionService) GetBlockTransactionCountByHash(
+ ctx context.Context, blockHash common.Hash,
+) (interface{}, error) {
+ // Fetch block
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetBlockTransactionCountByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(len(block.Transactions())), nil
+ case V2:
+ return len(block.Transactions()), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
+func (s *PublicTransactionService) GetTransactionByBlockNumberAndIndex(
+ ctx context.Context, blockNumber BlockNumber, index TransactionIndex,
+) (StructuredResponse, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch Block
+ block, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetTransactionByBlockNumberAndIndex")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
+func (s *PublicTransactionService) GetTransactionByBlockHashAndIndex(
+ ctx context.Context, blockHash common.Hash, index TransactionIndex,
+) (StructuredResponse, error) {
+ // Fetch Block
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetTransactionByBlockHashAndIndex")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetBlockStakingTransactionCountByNumber returns the number of staking transactions in the block with the given block number.
+// Note that the return type is an interface to account for the different versions
+func (s *PublicTransactionService) GetBlockStakingTransactionCountByNumber(
+ ctx context.Context, blockNumber BlockNumber,
+) (interface{}, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch block
+ block, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetBlockStakingTransactionCountByNumber")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(len(block.StakingTransactions())), nil
+ case V2:
+ return len(block.StakingTransactions()), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetBlockStakingTransactionCountByHash returns the number of staking transactions in the block with the given hash.
+// Note that the return type is an interface to account for the different versions
+func (s *PublicTransactionService) GetBlockStakingTransactionCountByHash(
+ ctx context.Context, blockHash common.Hash,
+) (interface{}, error) {
+ // Fetch block
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetBlockStakingTransactionCountByHash")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ return hexutil.Uint(len(block.StakingTransactions())), nil
+ case V2:
+ return len(block.StakingTransactions()), nil
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetStakingTransactionByBlockNumberAndIndex returns the staking transaction for the given block number and index.
+func (s *PublicTransactionService) GetStakingTransactionByBlockNumberAndIndex(
+ ctx context.Context, blockNumber BlockNumber, index TransactionIndex,
+) (StructuredResponse, error) {
+ // Process arguments based on version
+ blockNum := blockNumber.EthBlockNumber()
+
+ // Fetch Block
+ block, err := s.hmy.BlockByNumber(ctx, blockNum)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionByBlockNumberAndIndex")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewStakingTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewStakingTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetStakingTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
+func (s *PublicTransactionService) GetStakingTransactionByBlockHashAndIndex(
+ ctx context.Context, blockHash common.Hash, index TransactionIndex,
+) (StructuredResponse, error) {
+ // Fetch Block
+ block, err := s.hmy.GetBlock(ctx, blockHash)
+ if err != nil {
+ utils.Logger().Debug().
+ Err(err).
+ Msgf("%v error at %v", LogTag, "GetStakingTransactionByBlockHashAndIndex")
+ // Legacy behavior is to not return RPC errors
+ return nil, nil
+ }
+
+ // Format response according to version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewStakingTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewStakingTransactionFromBlockIndex(block, uint64(index))
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
+func (s *PublicTransactionService) GetTransactionReceipt(
+ ctx context.Context, hash common.Hash,
+) (StructuredResponse, error) {
+ // Fetch receipt for plain & staking transaction
+ var tx *types.Transaction
+ var stx *staking.StakingTransaction
+ var blockHash common.Hash
+ var blockNumber, index uint64
+ tx, blockHash, blockNumber, index = rawdb.ReadTransaction(s.hmy.ChainDb(), hash)
+ if tx == nil {
+ stx, blockHash, blockNumber, index = rawdb.ReadStakingTransaction(s.hmy.ChainDb(), hash)
+ if stx == nil {
+ return nil, nil
+ }
+ }
+ receipts, err := s.hmy.GetReceipts(ctx, blockHash)
+ if err != nil {
+ return nil, err
+ }
+ if len(receipts) <= int(index) {
+ return nil, nil
+ }
+ receipt := receipts[index]
+
+ // Format response according to version
+ var RPCReceipt interface{}
+ switch s.version {
+ case V1:
+ if tx == nil {
+ RPCReceipt, err = v1.NewReceipt(stx, blockHash, blockNumber, index, receipt)
+ } else {
+ RPCReceipt, err = v1.NewReceipt(tx, blockHash, blockNumber, index, receipt)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(RPCReceipt)
+ case V2:
+ if tx == nil {
+ RPCReceipt, err = v2.NewReceipt(stx, blockHash, blockNumber, index, receipt)
+ } else {
+ RPCReceipt, err = v2.NewReceipt(tx, blockHash, blockNumber, index, receipt)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(RPCReceipt)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+}
+
+// GetCXReceiptByHash returns the transaction for the given hash
+func (s *PublicTransactionService) GetCXReceiptByHash(
+ ctx context.Context, hash common.Hash,
+) (StructuredResponse, error) {
+ if cx, blockHash, blockNumber, _ := rawdb.ReadCXReceipt(s.hmy.ChainDb(), hash); cx != nil {
+ // Format response according to version
+ switch s.version {
+ case V1:
+ tx, err := v1.NewCxReceipt(cx, blockHash, blockNumber)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ case V2:
+ tx, err := v2.NewCxReceipt(cx, blockHash, blockNumber)
+ if err != nil {
+ return nil, err
+ }
+ return NewStructuredResponse(tx)
+ default:
+ return nil, ErrUnknownRPCVersion
+ }
+ }
+ utils.Logger().Debug().
+ Err(fmt.Errorf("unable to found CX receipt for tx %v", hash.String())).
+ Msgf("%v error at %v", LogTag, "GetCXReceiptByHash")
+ return nil, nil // Legacy behavior is to not return an error here
+}
+
+// ResendCx requests that the egress receipt for the given cross-shard
+// transaction be sent to the destination shard for credit. This is used for
+// unblocking a half-complete cross-shard transaction whose fund has been
+// withdrawn already from the source shard but not credited yet in the
+// destination account due to transient failures.
+func (s *PublicTransactionService) ResendCx(ctx context.Context, txID common.Hash) (bool, error) {
+ _, success := s.hmy.ResendCx(ctx, txID)
+
+ // Response output is the same for all versions
+ return success, nil
+}
+
+// returnHashesWithPagination returns result with pagination (offset, page in TxHistoryArgs).
+func returnHashesWithPagination(hashes []common.Hash, pageIndex uint32, pageSize uint32) []common.Hash {
+ size := defaultPageSize
+ if pageSize > 0 {
+ size = pageSize
+ }
+ if uint64(size)*uint64(pageIndex) >= uint64(len(hashes)) {
+ return make([]common.Hash, 0)
+ }
+ if uint64(size)*uint64(pageIndex)+uint64(size) > uint64(len(hashes)) {
+ return hashes[size*pageIndex:]
+ }
+ return hashes[size*pageIndex : size*pageIndex+size]
+}
+
+// doEstimateGas ..
+func doEstimateGas(
+ ctx context.Context, hmy *hmy.Harmony, args CallArgs, gasCap *big.Int,
+) (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 hi, nil
+}
diff --git a/rpc/types.go b/rpc/types.go
new file mode 100644
index 000000000..f4a75aec9
--- /dev/null
+++ b/rpc/types.go
@@ -0,0 +1,200 @@
+package rpc
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "math/big"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/block"
+ "github.com/harmony-one/harmony/core/types"
+ "github.com/harmony-one/harmony/numeric"
+ "github.com/harmony-one/harmony/shard"
+)
+
+// CallArgs represents the arguments for a call.
+type CallArgs struct {
+ From *common.Address `json:"from"`
+ To *common.Address `json:"to"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Value *hexutil.Big `json:"value"`
+ Data *hexutil.Bytes `json:"data"`
+}
+
+// StakingNetworkInfo returns global staking info.
+type StakingNetworkInfo struct {
+ TotalSupply numeric.Dec `json:"total-supply"`
+ CirculatingSupply numeric.Dec `json:"circulating-supply"`
+ EpochLastBlock uint64 `json:"epoch-last-block"`
+ TotalStaking *big.Int `json:"total-staking"`
+ MedianRawStake numeric.Dec `json:"median-raw-stake"`
+}
+
+// Delegation represents a particular delegation to a validator
+type Delegation struct {
+ ValidatorAddress string `json:"validator_address"`
+ DelegatorAddress string `json:"delegator_address"`
+ Amount *big.Int `json:"amount"`
+ Reward *big.Int `json:"reward"`
+ Undelegations []Undelegation `json:"Undelegations"`
+}
+
+// Undelegation represents one undelegation entry
+type Undelegation struct {
+ Amount *big.Int
+ Epoch *big.Int
+}
+
+// StructuredResponse type of RPCs
+type StructuredResponse = map[string]interface{}
+
+// NewStructuredResponse creates a structured response from the given input
+func NewStructuredResponse(input interface{}) (StructuredResponse, error) {
+ var objMap StructuredResponse
+ dat, err := json.Marshal(input)
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal(dat, &objMap); err != nil {
+ return nil, err
+ }
+ return objMap, nil
+}
+
+// BlockNumber ..
+type BlockNumber rpc.BlockNumber
+
+// UnmarshalJSON converts a hex string or integer to a block number
+func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
+ baseBn := rpc.BlockNumber(0)
+ baseErr := baseBn.UnmarshalJSON(data)
+ if baseErr != nil {
+ input := strings.TrimSpace(string(data))
+ if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
+ input = input[1 : len(input)-1]
+ }
+ input = strings.TrimPrefix(input, "0x")
+ num, err := strconv.ParseInt(input, 10, 64)
+ if err != nil {
+ return err
+ }
+ *bn = BlockNumber(num)
+ return nil
+ }
+ *bn = BlockNumber(baseBn)
+ return nil
+}
+
+// Int64 ..
+func (bn BlockNumber) Int64() int64 {
+ return (int64)(bn)
+}
+
+// EthBlockNumber ..
+func (bn BlockNumber) EthBlockNumber() rpc.BlockNumber {
+ return (rpc.BlockNumber)(bn)
+}
+
+// TransactionIndex ..
+type TransactionIndex uint64
+
+// UnmarshalJSON converts a hex string or integer to a Transaction index
+func (i *TransactionIndex) UnmarshalJSON(data []byte) (err error) {
+ input := strings.TrimSpace(string(data))
+ if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
+ input = input[1 : len(input)-1]
+ }
+
+ var num int64
+ if strings.HasPrefix(input, "0x") {
+ num, err = strconv.ParseInt(strings.TrimPrefix(input, "0x"), 16, 64)
+ } else {
+ num, err = strconv.ParseInt(input, 10, 64)
+ }
+ if err != nil {
+ return err
+ }
+
+ *i = TransactionIndex(num)
+ return nil
+}
+
+// TxHistoryArgs is struct to include optional transaction formatting params.
+type TxHistoryArgs struct {
+ Address string `json:"address"`
+ PageIndex uint32 `json:"pageIndex"`
+ PageSize uint32 `json:"pageSize"`
+ FullTx bool `json:"fullTx"`
+ TxType string `json:"txType"`
+ Order string `json:"order"`
+}
+
+// UnmarshalFromInterface ..
+func (ta *TxHistoryArgs) UnmarshalFromInterface(blockArgs interface{}) error {
+ var args TxHistoryArgs
+ dat, err := json.Marshal(blockArgs)
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal(dat, &args); err != nil {
+ return err
+ }
+ *ta = args
+ return nil
+}
+
+// HeaderInformation represents the latest consensus information
+type HeaderInformation struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ ShardID uint32 `json:"shardID"`
+ Leader string `json:"leader"`
+ ViewID uint64 `json:"viewID"`
+ Epoch uint64 `json:"epoch"`
+ Timestamp string `json:"timestamp"`
+ UnixTime uint64 `json:"unixtime"`
+ LastCommitSig string `json:"lastCommitSig"`
+ LastCommitBitmap string `json:"lastCommitBitmap"`
+ CrossLinks *types.CrossLinks `json:"crossLinks,omitempty"`
+}
+
+// NewHeaderInformation returns the header information that will serialize to the RPC representation.
+func NewHeaderInformation(header *block.Header, leader string) *HeaderInformation {
+ if header == nil {
+ return nil
+ }
+
+ result := &HeaderInformation{
+ BlockHash: header.Hash(),
+ BlockNumber: header.Number().Uint64(),
+ ShardID: header.ShardID(),
+ Leader: leader,
+ ViewID: header.ViewID().Uint64(),
+ Epoch: header.Epoch().Uint64(),
+ UnixTime: header.Time().Uint64(),
+ Timestamp: time.Unix(header.Time().Int64(), 0).UTC().String(),
+ LastCommitBitmap: hex.EncodeToString(header.LastCommitBitmap()),
+ }
+
+ sig := header.LastCommitSignature()
+ result.LastCommitSig = hex.EncodeToString(sig[:])
+
+ if header.ShardID() == shard.BeaconChainShardID {
+ decodedCrossLinks := &types.CrossLinks{}
+ err := rlp.DecodeBytes(header.CrossLinks(), decodedCrossLinks)
+ if err != nil {
+ result.CrossLinks = &types.CrossLinks{}
+ } else {
+ result.CrossLinks = decodedCrossLinks
+ }
+ }
+
+ return result
+}
diff --git a/rpc/v1/legacy.go b/rpc/v1/legacy.go
new file mode 100644
index 000000000..4e0bf413a
--- /dev/null
+++ b/rpc/v1/legacy.go
@@ -0,0 +1,40 @@
+package v1
+
+import (
+ "context"
+
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+)
+
+// PublicLegacyService provides an API to access the Harmony blockchain.
+// Services here are legacy methods, specific to the V1 RPC that can be deprecated in the future.
+type PublicLegacyService struct {
+ hmy *hmy.Harmony
+}
+
+// NewPublicLegacyAPI creates a new API for the RPC interface
+func NewPublicLegacyAPI(hmy *hmy.Harmony) rpc.API {
+ return rpc.API{
+ Namespace: "hmy",
+ Version: "1.0",
+ Service: &PublicLegacyService{hmy},
+ Public: true,
+ }
+}
+
+// GetBalance returns the amount of Atto for the given address in the state of the
+// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta
+// block numbers are also allowed.
+func (s *PublicLegacyService) GetBalance(
+ ctx context.Context, address string, blockNr rpc.BlockNumber,
+) (*hexutil.Big, error) {
+ addr := internal_common.ParseAddr(address)
+ balance, err := s.hmy.GetBalance(ctx, addr, blockNr)
+ if err != nil {
+ return nil, err
+ }
+ return (*hexutil.Big)(balance), nil
+}
diff --git a/rpc/v1/types.go b/rpc/v1/types.go
new file mode 100644
index 000000000..f5304c7f4
--- /dev/null
+++ b/rpc/v1/types.go
@@ -0,0 +1,735 @@
+package v1
+
+import (
+ "fmt"
+ "math/big"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ ethtypes "github.com/ethereum/go-ethereum/core/types"
+ "github.com/harmony-one/harmony/core/types"
+ "github.com/harmony-one/harmony/crypto/bls"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+ rpc_common "github.com/harmony-one/harmony/rpc/common"
+ staking "github.com/harmony-one/harmony/staking/types"
+)
+
+// BlockWithTxHash represents a block that will serialize to the RPC representation of a block
+// having ONLY transaction hashes in the Transaction & Staking transaction fields.
+type BlockWithTxHash struct {
+ Number *hexutil.Big `json:"number"`
+ ViewID *hexutil.Big `json:"viewID"`
+ Epoch *hexutil.Big `json:"epoch"`
+ Hash common.Hash `json:"hash"`
+ ParentHash common.Hash `json:"parentHash"`
+ Nonce uint64 `json:"nonce"`
+ MixHash common.Hash `json:"mixHash"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ StateRoot common.Hash `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty uint64 `json:"difficulty"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ Size hexutil.Uint64 `json:"size"`
+ GasLimit hexutil.Uint64 `json:"gasLimit"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ Timestamp hexutil.Uint64 `json:"timestamp"`
+ TransactionsRoot common.Hash `json:"transactionsRoot"`
+ ReceiptsRoot common.Hash `json:"receiptsRoot"`
+ Uncles []common.Hash `json:"uncles"`
+ Transactions []common.Hash `json:"transactions"`
+ StakingTxs []common.Hash `json:"stakingTransactions"`
+ Signers []string `json:"signers,omitempty"`
+}
+
+// BlockWithFullTx represents a block that will serialize to the RPC representation of a block
+// having FULL transactions in the Transaction & Staking transaction fields.
+type BlockWithFullTx struct {
+ Number *hexutil.Big `json:"number"`
+ ViewID *hexutil.Big `json:"viewID"`
+ Epoch *hexutil.Big `json:"epoch"`
+ Hash common.Hash `json:"hash"`
+ ParentHash common.Hash `json:"parentHash"`
+ Nonce uint64 `json:"nonce"`
+ MixHash common.Hash `json:"mixHash"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ StateRoot common.Hash `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty uint64 `json:"difficulty"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ Size hexutil.Uint64 `json:"size"`
+ GasLimit hexutil.Uint64 `json:"gasLimit"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ Timestamp hexutil.Uint64 `json:"timestamp"`
+ TransactionsRoot common.Hash `json:"transactionsRoot"`
+ ReceiptsRoot common.Hash `json:"receiptsRoot"`
+ Uncles []common.Hash `json:"uncles"`
+ Transactions []*Transaction `json:"transactions"`
+ StakingTxs []*StakingTransaction `json:"stakingTransactions"`
+ Signers []string `json:"signers,omitempty"`
+}
+
+// Transaction represents a transaction that will serialize to the RPC representation of a transaction
+type Transaction struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *hexutil.Big `json:"blockNumber"`
+ From string `json:"from"`
+ Timestamp hexutil.Uint64 `json:"timestamp"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Hash common.Hash `json:"hash"`
+ Input hexutil.Bytes `json:"input"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ To string `json:"to"`
+ TransactionIndex hexutil.Uint `json:"transactionIndex"`
+ Value *hexutil.Big `json:"value"`
+ ShardID uint32 `json:"shardID"`
+ ToShardID uint32 `json:"toShardID"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+}
+
+// StakingTransaction represents a staking transaction that will serialize to the
+// RPC representation of a staking transaction
+type StakingTransaction struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *hexutil.Big `json:"blockNumber"`
+ From string `json:"from"`
+ Timestamp hexutil.Uint64 `json:"timestamp"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ Hash common.Hash `json:"hash"`
+ Nonce hexutil.Uint64 `json:"nonce"`
+ TransactionIndex hexutil.Uint `json:"transactionIndex"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+ Type string `json:"type"`
+ Msg interface{} `json:"msg"`
+}
+
+// CreateValidatorMsg represents a staking transaction's create validator directive that
+// will serialize to the RPC representation
+type CreateValidatorMsg struct {
+ ValidatorAddress string `json:"validatorAddress"`
+ CommissionRate *hexutil.Big `json:"commissionRate"`
+ MaxCommissionRate *hexutil.Big `json:"maxCommissionRate"`
+ MaxChangeRate *hexutil.Big `json:"maxChangeRate"`
+ MinSelfDelegation *hexutil.Big `json:"minSelfDelegation"`
+ MaxTotalDelegation *hexutil.Big `json:"maxTotalDelegation"`
+ Amount *hexutil.Big `json:"amount"`
+ Name string `json:"name"`
+ Website string `json:"website"`
+ Identity string `json:"identity"`
+ SecurityContact string `json:"securityContact"`
+ Details string `json:"details"`
+ SlotPubKeys []bls.SerializedPublicKey `json:"slotPubKeys"`
+}
+
+// EditValidatorMsg represents a staking transaction's edit validator directive that
+// will serialize to the RPC representation
+type EditValidatorMsg struct {
+ ValidatorAddress string `json:"validatorAddress"`
+ CommissionRate *hexutil.Big `json:"commissionRate"`
+ MinSelfDelegation *hexutil.Big `json:"minSelfDelegation"`
+ MaxTotalDelegation *hexutil.Big `json:"maxTotalDelegation"`
+ Name string `json:"name"`
+ Website string `json:"website"`
+ Identity string `json:"identity"`
+ SecurityContact string `json:"securityContact"`
+ Details string `json:"details"`
+ SlotPubKeyToAdd *bls.SerializedPublicKey `json:"slotPubKeyToAdd"`
+ SlotPubKeyToRemove *bls.SerializedPublicKey `json:"slotPubKeyToRemove"`
+}
+
+// CollectRewardsMsg represents a staking transaction's collect rewards directive that
+// will serialize to the RPC representation
+type CollectRewardsMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+}
+
+// DelegateMsg represents a staking transaction's delegate directive that
+// will serialize to the RPC representation
+type DelegateMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+ ValidatorAddress string `json:"validatorAddress"`
+ Amount *hexutil.Big `json:"amount"`
+}
+
+// UndelegateMsg represents a staking transaction's delegate directive that
+// will serialize to the RPC representation
+type UndelegateMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+ ValidatorAddress string `json:"validatorAddress"`
+ Amount *hexutil.Big `json:"amount"`
+}
+
+// TxReceipt represents a transaction receipt that will serialize to the RPC representation.
+type TxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ TransactionHash common.Hash `json:"transactionHash"`
+ BlockNumber hexutil.Uint64 `json:"blockNumber"`
+ TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"`
+ ContractAddress common.Address `json:"contractAddress"`
+ Logs []*types.Log `json:"logs"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ ShardID uint32 `json:"shardID"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Root hexutil.Bytes `json:"root,omitempty"`
+ Status hexutil.Uint `json:"status,omitempty"`
+}
+
+// StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation.
+type StakingTxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ TransactionHash common.Hash `json:"transactionHash"`
+ BlockNumber hexutil.Uint64 `json:"blockNumber"`
+ TransactionIndex hexutil.Uint64 `json:"transactionIndex"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"`
+ ContractAddress common.Address `json:"contractAddress"`
+ Logs []*types.Log `json:"logs"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ Sender string `json:"sender"`
+ Type staking.Directive `json:"type"`
+ Root hexutil.Bytes `json:"root,omitempty"`
+ Status hexutil.Uint `json:"status,omitempty"`
+}
+
+// CxReceipt represents a CxReceipt that will serialize to the RPC representation of a CxReceipt
+type CxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *hexutil.Big `json:"blockNumber"`
+ TxHash common.Hash `json:"hash"`
+ From string `json:"from"`
+ To string `json:"to"`
+ ShardID uint32 `json:"shardID"`
+ ToShardID uint32 `json:"toShardID"`
+ Amount *hexutil.Big `json:"value"`
+}
+
+// NewCxReceipt returns a CxReceipt that will serialize to the RPC representation
+func NewCxReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uint64) (*CxReceipt, error) {
+ result := &CxReceipt{
+ BlockHash: blockHash,
+ TxHash: cx.TxHash,
+ Amount: (*hexutil.Big)(cx.Amount),
+ ShardID: cx.ShardID,
+ ToShardID: cx.ToShardID,
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(cx.From)
+ if err != nil {
+ return nil, err
+ }
+ toAddr := ""
+ if cx.To != nil {
+ if toAddr, err = internal_common.AddressToBech32(*cx.To); err != nil {
+ return nil, err
+ }
+ }
+ result.From = fromAddr
+ result.To = toAddr
+
+ return result, nil
+}
+
+// NewTransaction returns a transaction that will serialize to the RPC
+// representation, with the given location metadata set (if available).
+// Note that all txs on Harmony are replay protected (post EIP155 epoch).
+func NewTransaction(
+ tx *types.Transaction, blockHash common.Hash,
+ blockNumber uint64, timestamp uint64, index uint64,
+) (*Transaction, error) {
+ from, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ v, r, s := tx.RawSignatureValues()
+
+ result := &Transaction{
+ Gas: hexutil.Uint64(tx.Gas()),
+ GasPrice: (*hexutil.Big)(tx.GasPrice()),
+ Hash: tx.Hash(),
+ Input: hexutil.Bytes(tx.Data()),
+ Nonce: hexutil.Uint64(tx.Nonce()),
+ Value: (*hexutil.Big)(tx.Value()),
+ ShardID: tx.ShardID(),
+ ToShardID: tx.ToShardID(),
+ Timestamp: hexutil.Uint64(timestamp),
+ V: (*hexutil.Big)(v),
+ R: (*hexutil.Big)(r),
+ S: (*hexutil.Big)(s),
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
+ result.TransactionIndex = hexutil.Uint(index)
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(from)
+ if err != nil {
+ return nil, err
+ }
+ toAddr := ""
+
+ if tx.To() != nil {
+ if toAddr, err = internal_common.AddressToBech32(*tx.To()); err != nil {
+ return nil, err
+ }
+ result.From = fromAddr
+ } else {
+ result.From = strings.ToLower(from.Hex())
+ }
+ result.To = toAddr
+
+ return result, nil
+}
+
+// NewReceipt returns a transaction OR staking transaction that will serialize to the RPC
+// representation.
+func NewReceipt(
+ tx interface{}, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (interface{}, error) {
+ plainTx, ok := tx.(*types.Transaction)
+ if ok {
+ return NewTxReceipt(plainTx, blockHash, blockIndex, blockNumber, receipt)
+ }
+ stakingTx, ok := tx.(*staking.StakingTransaction)
+ if ok {
+ return NewStakingTxReceipt(stakingTx, blockHash, blockIndex, blockNumber, receipt)
+ }
+ return nil, fmt.Errorf("unknown transaction type for RPC receipt")
+}
+
+// NewTxReceipt returns a transaction receipt that will serialize to the RPC representation
+func NewTxReceipt(
+ tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (*TxReceipt, error) {
+ // Set correct to & from address
+ senderAddr, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ var sender, receiver string
+ if tx.To() == nil {
+ // Handle response type for contract receipts
+ sender = senderAddr.String()
+ receiver = ""
+ } else {
+ // Handle response type for regular transaction
+ sender, err = internal_common.AddressToBech32(senderAddr)
+ if err != nil {
+ return nil, err
+ }
+ receiver, err = internal_common.AddressToBech32(*tx.To())
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Declare receipt
+ txReceipt := &TxReceipt{
+ BlockHash: blockHash,
+ TransactionHash: tx.Hash(),
+ BlockNumber: hexutil.Uint64(blockNumber),
+ TransactionIndex: hexutil.Uint64(blockIndex),
+ GasUsed: hexutil.Uint64(receipt.GasUsed),
+ CumulativeGasUsed: hexutil.Uint64(receipt.CumulativeGasUsed),
+ Logs: receipt.Logs,
+ LogsBloom: receipt.Bloom,
+ ShardID: tx.ShardID(),
+ From: sender,
+ To: receiver,
+ }
+
+ // Set optionals
+ if len(receipt.PostState) > 0 {
+ txReceipt.Root = receipt.PostState
+ } else {
+ txReceipt.Status = hexutil.Uint(receipt.Status)
+ }
+
+ // Set empty array for empty logs
+ if receipt.Logs == nil {
+ txReceipt.Logs = []*types.Log{}
+ }
+
+ // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
+ if receipt.ContractAddress != (common.Address{}) {
+ txReceipt.ContractAddress = receipt.ContractAddress
+ }
+ return txReceipt, nil
+}
+
+// NewStakingTxReceipt returns a staking transaction receipt that will serialize to the RPC representation
+func NewStakingTxReceipt(
+ tx *staking.StakingTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (*StakingTxReceipt, error) {
+ // Set correct sender
+ senderAddr, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ sender, err := internal_common.AddressToBech32(senderAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Declare receipt
+ txReceipt := &StakingTxReceipt{
+ BlockHash: blockHash,
+ TransactionHash: tx.Hash(),
+ BlockNumber: hexutil.Uint64(blockNumber),
+ TransactionIndex: hexutil.Uint64(blockIndex),
+ GasUsed: hexutil.Uint64(receipt.GasUsed),
+ CumulativeGasUsed: hexutil.Uint64(receipt.CumulativeGasUsed),
+ Logs: receipt.Logs,
+ LogsBloom: receipt.Bloom,
+ Sender: sender,
+ Type: tx.StakingType(),
+ }
+
+ // Set optionals
+ if len(receipt.PostState) > 0 {
+ txReceipt.Root = receipt.PostState
+ } else {
+ txReceipt.Status = hexutil.Uint(receipt.Status)
+ }
+
+ // Set empty array for empty logs
+ if receipt.Logs == nil {
+ txReceipt.Logs = []*types.Log{}
+ }
+
+ // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
+ if receipt.ContractAddress != (common.Address{}) {
+ txReceipt.ContractAddress = receipt.ContractAddress
+ }
+ return txReceipt, nil
+}
+
+// NewStakingTransaction returns a staking transaction that will serialize to the RPC
+// representation, with the given location metadata set (if available).
+func NewStakingTransaction(tx *staking.StakingTransaction, blockHash common.Hash,
+ blockNumber uint64, timestamp uint64, index uint64,
+) (*StakingTransaction, error) {
+ from, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ v, r, s := tx.RawSignatureValues()
+
+ var rpcMsg interface{}
+ switch tx.StakingType() {
+ case staking.DirectiveCreateValidator:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCreateValidator)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.CreateValidator)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &CreateValidatorMsg{
+ ValidatorAddress: validatorAddress,
+ CommissionRate: (*hexutil.Big)(msg.CommissionRates.Rate.Int),
+ MaxCommissionRate: (*hexutil.Big)(msg.CommissionRates.MaxRate.Int),
+ MaxChangeRate: (*hexutil.Big)(msg.CommissionRates.MaxChangeRate.Int),
+ MinSelfDelegation: (*hexutil.Big)(msg.MinSelfDelegation),
+ MaxTotalDelegation: (*hexutil.Big)(msg.MaxTotalDelegation),
+ Amount: (*hexutil.Big)(msg.Amount),
+ Name: msg.Description.Name,
+ Website: msg.Description.Website,
+ Identity: msg.Description.Identity,
+ SecurityContact: msg.Description.SecurityContact,
+ Details: msg.Description.Details,
+ SlotPubKeys: msg.SlotPubKeys,
+ }
+ case staking.DirectiveEditValidator:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.EditValidator)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ // Edit validators txs need not have commission rates to edit
+ commissionRate := &hexutil.Big{}
+ if msg.CommissionRate != nil {
+ commissionRate = (*hexutil.Big)(msg.CommissionRate.Int)
+ }
+ rpcMsg = &EditValidatorMsg{
+ ValidatorAddress: validatorAddress,
+ CommissionRate: commissionRate,
+ MinSelfDelegation: (*hexutil.Big)(msg.MinSelfDelegation),
+ MaxTotalDelegation: (*hexutil.Big)(msg.MaxTotalDelegation),
+ Name: msg.Description.Name,
+ Website: msg.Description.Website,
+ Identity: msg.Description.Identity,
+ SecurityContact: msg.Description.SecurityContact,
+ Details: msg.Description.Details,
+ SlotPubKeyToAdd: msg.SlotKeyToAdd,
+ SlotPubKeyToRemove: msg.SlotKeyToRemove,
+ }
+ case staking.DirectiveCollectRewards:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCollectRewards)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.CollectRewards)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &CollectRewardsMsg{DelegatorAddress: delegatorAddress}
+ case staking.DirectiveDelegate:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveDelegate)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.Delegate)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &DelegateMsg{
+ DelegatorAddress: delegatorAddress,
+ ValidatorAddress: validatorAddress,
+ Amount: (*hexutil.Big)(msg.Amount),
+ }
+ case staking.DirectiveUndelegate:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.Undelegate)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &UndelegateMsg{
+ DelegatorAddress: delegatorAddress,
+ ValidatorAddress: validatorAddress,
+ Amount: (*hexutil.Big)(msg.Amount),
+ }
+ }
+
+ result := &StakingTransaction{
+ Gas: hexutil.Uint64(tx.Gas()),
+ GasPrice: (*hexutil.Big)(tx.GasPrice()),
+ Hash: tx.Hash(),
+ Nonce: hexutil.Uint64(tx.Nonce()),
+ Timestamp: hexutil.Uint64(timestamp),
+ V: (*hexutil.Big)(v),
+ R: (*hexutil.Big)(r),
+ S: (*hexutil.Big)(s),
+ Type: tx.StakingType().String(),
+ Msg: rpcMsg,
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
+ result.TransactionIndex = hexutil.Uint(index)
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(from)
+ if err != nil {
+ return nil, err
+ }
+ result.From = fromAddr
+
+ return result, nil
+}
+
+// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
+// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
+// transaction hashes.
+func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leader string) (interface{}, error) {
+ if blockArgs.FullTx {
+ return NewBlockWithFullTx(b, blockArgs, leader)
+ }
+ return NewBlockWithTxHash(b, blockArgs, leader)
+}
+
+// NewBlockWithTxHash ..
+func NewBlockWithTxHash(
+ b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
+) (*BlockWithTxHash, error) {
+ if blockArgs.FullTx {
+ return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash")
+ }
+
+ head := b.Header()
+ blk := &BlockWithTxHash{
+ Number: (*hexutil.Big)(head.Number()),
+ ViewID: (*hexutil.Big)(head.ViewID()),
+ Epoch: (*hexutil.Big)(head.Epoch()),
+ Hash: b.Hash(),
+ ParentHash: head.ParentHash(),
+ Nonce: 0, // Remove this because we don't have it in our header
+ MixHash: head.MixDigest(),
+ LogsBloom: head.Bloom(),
+ StateRoot: head.Root(),
+ Miner: leader,
+ Difficulty: 0, // Remove this because we don't have it in our header
+ ExtraData: hexutil.Bytes(head.Extra()),
+ Size: hexutil.Uint64(b.Size()),
+ GasLimit: hexutil.Uint64(head.GasLimit()),
+ GasUsed: hexutil.Uint64(head.GasUsed()),
+ Timestamp: hexutil.Uint64(head.Time().Uint64()),
+ TransactionsRoot: head.TxHash(),
+ ReceiptsRoot: head.ReceiptHash(),
+ Uncles: []common.Hash{},
+ Transactions: []common.Hash{},
+ StakingTxs: []common.Hash{},
+ }
+
+ for _, tx := range b.Transactions() {
+ blk.Transactions = append(blk.Transactions, tx.Hash())
+ }
+
+ if blockArgs.InclStaking {
+ for _, stx := range b.StakingTransactions() {
+ blk.StakingTxs = append(blk.StakingTxs, stx.Hash())
+ }
+ }
+
+ if blockArgs.WithSigners {
+ blk.Signers = blockArgs.Signers
+ }
+ return blk, nil
+}
+
+// NewBlockWithFullTx ..
+func NewBlockWithFullTx(
+ b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
+) (*BlockWithFullTx, error) {
+ if !blockArgs.FullTx {
+ return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx")
+ }
+
+ head := b.Header()
+ blk := &BlockWithFullTx{
+ Number: (*hexutil.Big)(head.Number()),
+ ViewID: (*hexutil.Big)(head.ViewID()),
+ Epoch: (*hexutil.Big)(head.Epoch()),
+ Hash: b.Hash(),
+ ParentHash: head.ParentHash(),
+ Nonce: 0, // Remove this because we don't have it in our header
+ MixHash: head.MixDigest(),
+ LogsBloom: head.Bloom(),
+ StateRoot: head.Root(),
+ Miner: leader,
+ Difficulty: 0, // Remove this because we don't have it in our header
+ ExtraData: hexutil.Bytes(head.Extra()),
+ Size: hexutil.Uint64(b.Size()),
+ GasLimit: hexutil.Uint64(head.GasLimit()),
+ GasUsed: hexutil.Uint64(head.GasUsed()),
+ Timestamp: hexutil.Uint64(head.Time().Uint64()),
+ TransactionsRoot: head.TxHash(),
+ ReceiptsRoot: head.ReceiptHash(),
+ Uncles: []common.Hash{},
+ Transactions: []*Transaction{},
+ StakingTxs: []*StakingTransaction{},
+ }
+
+ for _, tx := range b.Transactions() {
+ fmtTx, err := NewTransactionFromBlockHash(b, tx.Hash())
+ if err != nil {
+ return nil, err
+ }
+ blk.Transactions = append(blk.Transactions, fmtTx)
+ }
+
+ if blockArgs.InclStaking {
+ for _, stx := range b.StakingTransactions() {
+ fmtStx, err := NewStakingTransactionFromBlockHash(b, stx.Hash())
+ if err != nil {
+ return nil, err
+ }
+ blk.StakingTxs = append(blk.StakingTxs, fmtStx)
+ }
+ }
+
+ if blockArgs.WithSigners {
+ blk.Signers = blockArgs.Signers
+ }
+ return blk, nil
+}
+
+// NewTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
+func NewTransactionFromBlockHash(b *types.Block, hash common.Hash) (*Transaction, error) {
+ for idx, tx := range b.Transactions() {
+ if tx.Hash() == hash {
+ return NewTransactionFromBlockIndex(b, uint64(idx))
+ }
+ }
+ return nil, fmt.Errorf("tx %v not found in block %v", hash, b.Hash().String())
+}
+
+// NewTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
+func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, error) {
+ txs := b.Transactions()
+ if index >= uint64(len(txs)) {
+ return nil, fmt.Errorf(
+ "tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(),
+ )
+ }
+ return NewTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
+}
+
+// NewStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
+func NewStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) (*StakingTransaction, error) {
+ for idx, tx := range b.StakingTransactions() {
+ if tx.Hash() == hash {
+ return NewStakingTransactionFromBlockIndex(b, uint64(idx))
+ }
+ }
+ return nil, fmt.Errorf("tx %v not found in block %v", hash, b.Hash().String())
+}
+
+// NewStakingTransactionFromBlockIndex returns a staking transaction that will serialize to the RPC representation.
+func NewStakingTransactionFromBlockIndex(b *types.Block, index uint64) (*StakingTransaction, error) {
+ txs := b.StakingTransactions()
+ if index >= uint64(len(txs)) {
+ return nil, fmt.Errorf(
+ "tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(),
+ )
+ }
+ return NewStakingTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
+}
diff --git a/rpc/v2/legacy.go b/rpc/v2/legacy.go
new file mode 100644
index 000000000..c68dc9b92
--- /dev/null
+++ b/rpc/v2/legacy.go
@@ -0,0 +1,39 @@
+package v2
+
+import (
+ "context"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/harmony-one/harmony/hmy"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+)
+
+// PublicLegacyService provides an API to access the Harmony blockchain.
+// Services here are legacy methods, specific to the V1 RPC that can be deprecated in the future.
+type PublicLegacyService struct {
+ hmy *hmy.Harmony
+}
+
+// NewPublicLegacyAPI creates a new API for the RPC interface
+func NewPublicLegacyAPI(hmy *hmy.Harmony) rpc.API {
+ return rpc.API{
+ Namespace: "hmyv2",
+ Version: "1.0",
+ Service: &PublicLegacyService{hmy},
+ Public: true,
+ }
+}
+
+// GetBalance returns the amount of Atto for the given address in the state of the
+// given block number.
+func (s *PublicLegacyService) GetBalance(
+ ctx context.Context, address string,
+) (*big.Int, error) {
+ addr := internal_common.ParseAddr(address)
+ balance, err := s.hmy.GetBalance(ctx, addr, rpc.BlockNumber(-1))
+ if err != nil {
+ return nil, err
+ }
+ return balance, nil
+}
diff --git a/rpc/v2/types.go b/rpc/v2/types.go
new file mode 100644
index 000000000..057bbf5b3
--- /dev/null
+++ b/rpc/v2/types.go
@@ -0,0 +1,734 @@
+package v2
+
+import (
+ "fmt"
+ "math/big"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ ethtypes "github.com/ethereum/go-ethereum/core/types"
+ "github.com/harmony-one/harmony/core/types"
+ "github.com/harmony-one/harmony/crypto/bls"
+ internal_common "github.com/harmony-one/harmony/internal/common"
+ rpc_common "github.com/harmony-one/harmony/rpc/common"
+ staking "github.com/harmony-one/harmony/staking/types"
+)
+
+// BlockWithTxHash represents a block that will serialize to the RPC representation of a block
+// having ONLY transaction hashes in the Transaction & Staking transaction fields.
+type BlockWithTxHash struct {
+ Number *big.Int `json:"number"`
+ ViewID *big.Int `json:"viewID"`
+ Epoch *big.Int `json:"epoch"`
+ Hash common.Hash `json:"hash"`
+ ParentHash common.Hash `json:"parentHash"`
+ Nonce uint64 `json:"nonce"`
+ MixHash common.Hash `json:"mixHash"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ StateRoot common.Hash `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty uint64 `json:"difficulty"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ Size uint64 `json:"size"`
+ GasLimit uint64 `json:"gasLimit"`
+ GasUsed uint64 `json:"gasUsed"`
+ Timestamp *big.Int `json:"timestamp"`
+ TransactionsRoot common.Hash `json:"transactionsRoot"`
+ ReceiptsRoot common.Hash `json:"receiptsRoot"`
+ Uncles []common.Hash `json:"uncles"`
+ Transactions []common.Hash `json:"transactions"`
+ StakingTxs []common.Hash `json:"stakingTransactions"`
+ Signers []string `json:"signers,omitempty"`
+}
+
+// BlockWithFullTx represents a block that will serialize to the RPC representation of a block
+// having FULL transactions in the Transaction & Staking transaction fields.
+type BlockWithFullTx struct {
+ Number *big.Int `json:"number"`
+ ViewID *big.Int `json:"viewID"`
+ Epoch *big.Int `json:"epoch"`
+ Hash common.Hash `json:"hash"`
+ ParentHash common.Hash `json:"parentHash"`
+ Nonce uint64 `json:"nonce"`
+ MixHash common.Hash `json:"mixHash"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ StateRoot common.Hash `json:"stateRoot"`
+ Miner string `json:"miner"`
+ Difficulty uint64 `json:"difficulty"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ Size uint64 `json:"size"`
+ GasLimit uint64 `json:"gasLimit"`
+ GasUsed uint64 `json:"gasUsed"`
+ Timestamp *big.Int `json:"timestamp"`
+ TransactionsRoot common.Hash `json:"transactionsRoot"`
+ ReceiptsRoot common.Hash `json:"receiptsRoot"`
+ Uncles []common.Hash `json:"uncles"`
+ Transactions []*Transaction `json:"transactions"`
+ StakingTxs []*StakingTransaction `json:"stakingTransactions"`
+ Signers []string `json:"signers,omitempty"`
+}
+
+// Transaction represents a transaction that will serialize to the RPC representation of a transaction
+type Transaction struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *big.Int `json:"blockNumber"`
+ From string `json:"from"`
+ Timestamp uint64 `json:"timestamp"`
+ Gas uint64 `json:"gas"`
+ GasPrice *big.Int `json:"gasPrice"`
+ Hash common.Hash `json:"hash"`
+ Input hexutil.Bytes `json:"input"`
+ Nonce uint64 `json:"nonce"`
+ To string `json:"to"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ Value *big.Int `json:"value"`
+ ShardID uint32 `json:"shardID"`
+ ToShardID uint32 `json:"toShardID"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+}
+
+// StakingTransaction represents a transaction that will serialize to the RPC representation of a staking transaction
+type StakingTransaction struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *big.Int `json:"blockNumber"`
+ From string `json:"from"`
+ Timestamp uint64 `json:"timestamp"`
+ Gas uint64 `json:"gas"`
+ GasPrice *big.Int `json:"gasPrice"`
+ Hash common.Hash `json:"hash"`
+ Nonce uint64 `json:"nonce"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+ Type string `json:"type"`
+ Msg interface{} `json:"msg"`
+}
+
+// CreateValidatorMsg represents a staking transaction's create validator directive that
+// will serialize to the RPC representation
+type CreateValidatorMsg struct {
+ ValidatorAddress string `json:"validatorAddress"`
+ CommissionRate *big.Int `json:"commissionRate"`
+ MaxCommissionRate *big.Int `json:"maxCommissionRate"`
+ MaxChangeRate *big.Int `json:"maxChangeRate"`
+ MinSelfDelegation *big.Int `json:"minSelfDelegation"`
+ MaxTotalDelegation *big.Int `json:"maxTotalDelegation"`
+ Amount *big.Int `json:"amount"`
+ Name string `json:"name"`
+ Website string `json:"website"`
+ Identity string `json:"identity"`
+ SecurityContact string `json:"securityContact"`
+ Details string `json:"details"`
+ SlotPubKeys []bls.SerializedPublicKey `json:"slotPubKeys"`
+}
+
+// EditValidatorMsg represents a staking transaction's edit validator directive that
+// will serialize to the RPC representation
+type EditValidatorMsg struct {
+ ValidatorAddress string `json:"validatorAddress"`
+ CommissionRate *big.Int `json:"commissionRate"`
+ MinSelfDelegation *big.Int `json:"minSelfDelegation"`
+ MaxTotalDelegation *big.Int `json:"maxTotalDelegation"`
+ Name string `json:"name"`
+ Website string `json:"website"`
+ Identity string `json:"identity"`
+ SecurityContact string `json:"securityContact"`
+ Details string `json:"details"`
+ SlotPubKeyToAdd *bls.SerializedPublicKey `json:"slotPubKeyToAdd"`
+ SlotPubKeyToRemove *bls.SerializedPublicKey `json:"slotPubKeyToRemove"`
+}
+
+// CollectRewardsMsg represents a staking transaction's collect rewards directive that
+// will serialize to the RPC representation
+type CollectRewardsMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+}
+
+// DelegateMsg represents a staking transaction's delegate directive that
+// will serialize to the RPC representation
+type DelegateMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+ ValidatorAddress string `json:"validatorAddress"`
+ Amount *big.Int `json:"amount"`
+}
+
+// UndelegateMsg represents a staking transaction's delegate directive that
+// will serialize to the RPC representation
+type UndelegateMsg struct {
+ DelegatorAddress string `json:"delegatorAddress"`
+ ValidatorAddress string `json:"validatorAddress"`
+ Amount *big.Int `json:"amount"`
+}
+
+// TxReceipt represents a transaction receipt that will serialize to the RPC representation.
+type TxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ TransactionHash common.Hash `json:"transactionHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ GasUsed uint64 `json:"gasUsed"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
+ ContractAddress common.Address `json:"contractAddress"`
+ Logs []*types.Log `json:"logs"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ ShardID uint32 `json:"shardID"`
+ From string `json:"from"`
+ To string `json:"to"`
+ Root hexutil.Bytes `json:"root,omitempty"`
+ Status uint `json:"status,omitempty"`
+}
+
+// StakingTxReceipt represents a staking transaction receipt that will serialize to the RPC representation.
+type StakingTxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ TransactionHash common.Hash `json:"transactionHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ GasUsed uint64 `json:"gasUsed"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
+ ContractAddress common.Address `json:"contractAddress"`
+ Logs []*types.Log `json:"logs"`
+ LogsBloom ethtypes.Bloom `json:"logsBloom"`
+ Sender string `json:"sender"`
+ Type staking.Directive `json:"type"`
+ Root hexutil.Bytes `json:"root,omitempty"`
+ Status uint `json:"status,omitempty"`
+}
+
+// CxReceipt represents a CxReceipt that will serialize to the RPC representation of a CxReceipt
+type CxReceipt struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber *big.Int `json:"blockNumber"`
+ TxHash common.Hash `json:"hash"`
+ From string `json:"from"`
+ To string `json:"to"`
+ ShardID uint32 `json:"shardID"`
+ ToShardID uint32 `json:"toShardID"`
+ Amount *big.Int `json:"value"`
+}
+
+// NewCxReceipt returns a CxReceipt that will serialize to the RPC representation
+func NewCxReceipt(cx *types.CXReceipt, blockHash common.Hash, blockNumber uint64) (*CxReceipt, error) {
+ result := &CxReceipt{
+ BlockHash: blockHash,
+ TxHash: cx.TxHash,
+ Amount: cx.Amount,
+ ShardID: cx.ShardID,
+ ToShardID: cx.ToShardID,
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = (*big.Int)(new(big.Int).SetUint64(blockNumber))
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(cx.From)
+ if err != nil {
+ return nil, err
+ }
+ toAddr := ""
+ if cx.To != nil {
+ if toAddr, err = internal_common.AddressToBech32(*cx.To); err != nil {
+ return nil, err
+ }
+ }
+ result.From = fromAddr
+ result.To = toAddr
+
+ return result, nil
+}
+
+// NewTransaction returns a transaction that will serialize to the RPC
+// representation, with the given location metadata set (if available).
+// Note that all txs on Harmony are replay protected (post EIP155 epoch).
+func NewTransaction(
+ tx *types.Transaction, blockHash common.Hash,
+ blockNumber uint64, timestamp uint64, index uint64,
+) (*Transaction, error) {
+ from, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ v, r, s := tx.RawSignatureValues()
+
+ result := &Transaction{
+ Gas: tx.Gas(),
+ GasPrice: tx.GasPrice(),
+ Hash: tx.Hash(),
+ Input: hexutil.Bytes(tx.Data()),
+ Nonce: tx.Nonce(),
+ Value: tx.Value(),
+ ShardID: tx.ShardID(),
+ ToShardID: tx.ToShardID(),
+ Timestamp: timestamp,
+ V: (*hexutil.Big)(v),
+ R: (*hexutil.Big)(r),
+ S: (*hexutil.Big)(s),
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = new(big.Int).SetUint64(blockNumber)
+ result.TransactionIndex = index
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(from)
+ if err != nil {
+ return nil, err
+ }
+ toAddr := ""
+
+ if tx.To() != nil {
+ if toAddr, err = internal_common.AddressToBech32(*tx.To()); err != nil {
+ return nil, err
+ }
+ result.From = fromAddr
+ } else {
+ result.From = strings.ToLower(from.Hex())
+ }
+ result.To = toAddr
+
+ return result, nil
+}
+
+// NewReceipt returns a transaction OR staking transaction that will serialize to the RPC representation
+func NewReceipt(
+ tx interface{}, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (interface{}, error) {
+ plainTx, ok := tx.(*types.Transaction)
+ if ok {
+ return NewTxReceipt(plainTx, blockHash, blockIndex, blockNumber, receipt)
+ }
+ stakingTx, ok := tx.(*staking.StakingTransaction)
+ if ok {
+ return NewStakingTxReceipt(stakingTx, blockHash, blockIndex, blockNumber, receipt)
+ }
+ return nil, fmt.Errorf("unknown transaction type for RPC receipt")
+}
+
+// NewTxReceipt returns a plain transaction receipt that will serialize to the RPC representation
+func NewTxReceipt(
+ tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (*TxReceipt, error) {
+ // Set correct to & from address
+ senderAddr, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ var sender, receiver string
+ if tx.To() == nil {
+ // Handle response type for contract receipts
+ sender = senderAddr.String()
+ receiver = ""
+ } else {
+ // Handle response type for regular transaction
+ sender, err = internal_common.AddressToBech32(senderAddr)
+ if err != nil {
+ return nil, err
+ }
+ receiver, err = internal_common.AddressToBech32(*tx.To())
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Declare receipt
+ txReceipt := &TxReceipt{
+ BlockHash: blockHash,
+ TransactionHash: tx.Hash(),
+ BlockNumber: blockNumber,
+ TransactionIndex: blockIndex,
+ GasUsed: receipt.GasUsed,
+ CumulativeGasUsed: receipt.CumulativeGasUsed,
+ Logs: receipt.Logs,
+ LogsBloom: receipt.Bloom,
+ ShardID: tx.ShardID(),
+ From: sender,
+ To: receiver,
+ }
+
+ // Set optionals
+ if len(receipt.PostState) > 0 {
+ txReceipt.Root = receipt.PostState
+ } else {
+ txReceipt.Status = uint(receipt.Status)
+ }
+
+ // Set empty array for empty logs
+ if receipt.Logs == nil {
+ txReceipt.Logs = []*types.Log{}
+ }
+
+ // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
+ if receipt.ContractAddress != (common.Address{}) {
+ txReceipt.ContractAddress = receipt.ContractAddress
+ }
+ return txReceipt, nil
+}
+
+// NewStakingTxReceipt returns a staking transaction receipt that will serialize to the RPC representation
+func NewStakingTxReceipt(
+ tx *staking.StakingTransaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt,
+) (*StakingTxReceipt, error) {
+ // Set correct sender
+ senderAddr, err := tx.SenderAddress()
+ if err != nil {
+ return nil, err
+ }
+ sender, err := internal_common.AddressToBech32(senderAddr)
+ if err != nil {
+ return nil, err
+ }
+
+ // Declare receipt
+ txReceipt := &StakingTxReceipt{
+ BlockHash: blockHash,
+ TransactionHash: tx.Hash(),
+ BlockNumber: blockNumber,
+ TransactionIndex: blockIndex,
+ GasUsed: receipt.GasUsed,
+ CumulativeGasUsed: receipt.CumulativeGasUsed,
+ Logs: receipt.Logs,
+ LogsBloom: receipt.Bloom,
+ Sender: sender,
+ Type: tx.StakingType(),
+ }
+
+ // Set optionals
+ if len(receipt.PostState) > 0 {
+ txReceipt.Root = receipt.PostState
+ } else {
+ txReceipt.Status = uint(receipt.Status)
+ }
+
+ // Set empty array for empty logs
+ if receipt.Logs == nil {
+ txReceipt.Logs = []*types.Log{}
+ }
+
+ // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
+ if receipt.ContractAddress != (common.Address{}) {
+ txReceipt.ContractAddress = receipt.ContractAddress
+ }
+ return txReceipt, nil
+}
+
+// NewStakingTransaction returns a staking transaction that will serialize to the RPC
+// representation, with the given location metadata set (if available).
+func NewStakingTransaction(
+ tx *staking.StakingTransaction, blockHash common.Hash,
+ blockNumber uint64, timestamp uint64, index uint64,
+) (*StakingTransaction, error) {
+ from, err := tx.SenderAddress()
+ if err != nil {
+ return nil, nil
+ }
+ v, r, s := tx.RawSignatureValues()
+
+ var rpcMsg interface{}
+ switch tx.StakingType() {
+ case staking.DirectiveCreateValidator:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCreateValidator)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.CreateValidator)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &CreateValidatorMsg{
+ ValidatorAddress: validatorAddress,
+ CommissionRate: msg.CommissionRates.Rate.Int,
+ MaxCommissionRate: msg.CommissionRates.MaxRate.Int,
+ MaxChangeRate: msg.CommissionRates.MaxChangeRate.Int,
+ MinSelfDelegation: msg.MinSelfDelegation,
+ MaxTotalDelegation: msg.MaxTotalDelegation,
+ Amount: msg.Amount,
+ Name: msg.Description.Name,
+ Website: msg.Description.Website,
+ Identity: msg.Description.Identity,
+ SecurityContact: msg.Description.SecurityContact,
+ Details: msg.Description.Details,
+ SlotPubKeys: msg.SlotPubKeys,
+ }
+ case staking.DirectiveEditValidator:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveEditValidator)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.EditValidator)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ // Edit validators txs need not have commission rates to edit
+ commissionRate := &big.Int{}
+ if msg.CommissionRate != nil {
+ commissionRate = msg.CommissionRate.Int
+ }
+ rpcMsg = &EditValidatorMsg{
+ ValidatorAddress: validatorAddress,
+ CommissionRate: commissionRate,
+ MinSelfDelegation: msg.MinSelfDelegation,
+ MaxTotalDelegation: msg.MaxTotalDelegation,
+ Name: msg.Description.Name,
+ Website: msg.Description.Website,
+ Identity: msg.Description.Identity,
+ SecurityContact: msg.Description.SecurityContact,
+ Details: msg.Description.Details,
+ SlotPubKeyToAdd: msg.SlotKeyToAdd,
+ SlotPubKeyToRemove: msg.SlotKeyToRemove,
+ }
+ case staking.DirectiveCollectRewards:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveCollectRewards)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.CollectRewards)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &CollectRewardsMsg{DelegatorAddress: delegatorAddress}
+ case staking.DirectiveDelegate:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveDelegate)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.Delegate)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &DelegateMsg{
+ DelegatorAddress: delegatorAddress,
+ ValidatorAddress: validatorAddress,
+ Amount: msg.Amount,
+ }
+ case staking.DirectiveUndelegate:
+ rawMsg, err := staking.RLPDecodeStakeMsg(tx.Data(), staking.DirectiveUndelegate)
+ if err != nil {
+ return nil, err
+ }
+ msg, ok := rawMsg.(*staking.Undelegate)
+ if !ok {
+ return nil, fmt.Errorf("could not decode staking message")
+ }
+ delegatorAddress, err := internal_common.AddressToBech32(msg.DelegatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ validatorAddress, err := internal_common.AddressToBech32(msg.ValidatorAddress)
+ if err != nil {
+ return nil, err
+ }
+ rpcMsg = &UndelegateMsg{
+ DelegatorAddress: delegatorAddress,
+ ValidatorAddress: validatorAddress,
+ Amount: msg.Amount,
+ }
+ }
+
+ result := &StakingTransaction{
+ Gas: tx.Gas(),
+ GasPrice: tx.GasPrice(),
+ Hash: tx.Hash(),
+ Nonce: tx.Nonce(),
+ Timestamp: timestamp,
+ V: (*hexutil.Big)(v),
+ R: (*hexutil.Big)(r),
+ S: (*hexutil.Big)(s),
+ Type: tx.StakingType().String(),
+ Msg: rpcMsg,
+ }
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = new(big.Int).SetUint64(blockNumber)
+ result.TransactionIndex = index
+ }
+
+ fromAddr, err := internal_common.AddressToBech32(from)
+ if err != nil {
+ return nil, err
+ }
+ result.From = fromAddr
+
+ return result, nil
+}
+
+// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
+// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
+// transaction hashes.
+func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leader string) (interface{}, error) {
+ if blockArgs.FullTx {
+ return NewBlockWithFullTx(b, blockArgs, leader)
+ }
+ return NewBlockWithTxHash(b, blockArgs, leader)
+}
+
+// NewBlockWithTxHash return a block with only the transaction hash that will serialize to the RPC representation
+func NewBlockWithTxHash(
+ b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
+) (*BlockWithTxHash, error) {
+ if blockArgs.FullTx {
+ return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash")
+ }
+
+ head := b.Header()
+ blk := &BlockWithTxHash{
+ Number: head.Number(),
+ ViewID: head.ViewID(),
+ Epoch: head.Epoch(),
+ Hash: b.Hash(),
+ ParentHash: head.ParentHash(),
+ Nonce: 0, // Remove this because we don't have it in our header
+ MixHash: head.MixDigest(),
+ LogsBloom: head.Bloom(),
+ StateRoot: head.Root(),
+ Miner: leader,
+ Difficulty: 0, // Remove this because we don't have it in our header
+ ExtraData: hexutil.Bytes(head.Extra()),
+ Size: uint64(b.Size()),
+ GasLimit: head.GasLimit(),
+ GasUsed: head.GasUsed(),
+ Timestamp: head.Time(),
+ TransactionsRoot: head.TxHash(),
+ ReceiptsRoot: head.ReceiptHash(),
+ Uncles: []common.Hash{},
+ Transactions: []common.Hash{},
+ StakingTxs: []common.Hash{},
+ }
+
+ for _, tx := range b.Transactions() {
+ blk.Transactions = append(blk.Transactions, tx.Hash())
+ }
+
+ if blockArgs.InclStaking {
+ for _, stx := range b.StakingTransactions() {
+ blk.StakingTxs = append(blk.StakingTxs, stx.Hash())
+ }
+ }
+
+ if blockArgs.WithSigners {
+ blk.Signers = blockArgs.Signers
+ }
+ return blk, nil
+}
+
+// NewBlockWithFullTx return a block with the transaction that will serialize to the RPC representation
+func NewBlockWithFullTx(
+ b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
+) (*BlockWithFullTx, error) {
+ if !blockArgs.FullTx {
+ return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx")
+ }
+
+ head := b.Header()
+ blk := &BlockWithFullTx{
+ Number: head.Number(),
+ ViewID: head.ViewID(),
+ Epoch: head.Epoch(),
+ Hash: b.Hash(),
+ ParentHash: head.ParentHash(),
+ Nonce: 0, // Remove this because we don't have it in our header
+ MixHash: head.MixDigest(),
+ LogsBloom: head.Bloom(),
+ StateRoot: head.Root(),
+ Miner: leader,
+ Difficulty: 0, // Remove this because we don't have it in our header
+ ExtraData: hexutil.Bytes(head.Extra()),
+ Size: uint64(b.Size()),
+ GasLimit: head.GasLimit(),
+ GasUsed: head.GasUsed(),
+ Timestamp: head.Time(),
+ TransactionsRoot: head.TxHash(),
+ ReceiptsRoot: head.ReceiptHash(),
+ Uncles: []common.Hash{},
+ Transactions: []*Transaction{},
+ StakingTxs: []*StakingTransaction{},
+ }
+
+ for _, tx := range b.Transactions() {
+ fmtTx, err := NewTransactionFromBlockHash(b, tx.Hash())
+ if err != nil {
+ return nil, err
+ }
+ blk.Transactions = append(blk.Transactions, fmtTx)
+ }
+
+ if blockArgs.InclStaking {
+ for _, stx := range b.StakingTransactions() {
+ fmtStx, err := NewStakingTransactionFromBlockHash(b, stx.Hash())
+ if err != nil {
+ return nil, err
+ }
+ blk.StakingTxs = append(blk.StakingTxs, fmtStx)
+ }
+ }
+
+ if blockArgs.WithSigners {
+ blk.Signers = blockArgs.Signers
+ }
+ return blk, nil
+}
+
+// NewTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
+func NewTransactionFromBlockHash(b *types.Block, hash common.Hash) (*Transaction, error) {
+ for idx, tx := range b.Transactions() {
+ if tx.Hash() == hash {
+ return NewTransactionFromBlockIndex(b, uint64(idx))
+ }
+ }
+ return nil, fmt.Errorf("tx %v not found in block %v", hash, b.Hash().String())
+}
+
+// NewTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
+func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, error) {
+ txs := b.Transactions()
+ if index >= uint64(len(txs)) {
+ return nil, fmt.Errorf(
+ "tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(),
+ )
+ }
+ return NewTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
+}
+
+// NewStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
+func NewStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) (*StakingTransaction, error) {
+ for idx, tx := range b.StakingTransactions() {
+ if tx.Hash() == hash {
+ return NewStakingTransactionFromBlockIndex(b, uint64(idx))
+ }
+ }
+ return nil, fmt.Errorf("tx %v not found in block %v", hash, b.Hash().String())
+}
+
+// NewStakingTransactionFromBlockIndex returns a staking transaction that will serialize to the RPC representation.
+func NewStakingTransactionFromBlockIndex(b *types.Block, index uint64) (*StakingTransaction, error) {
+ txs := b.StakingTransactions()
+ if index >= uint64(len(txs)) {
+ return nil, fmt.Errorf(
+ "tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(),
+ )
+ }
+ return NewStakingTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
+}