Node API Refactor - pt2 (Stage 2.2 of Node API Overhaul) (#3259)

* [rpc] Refactor - expose RPC functions in rpc package

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt1

* Move apiv1 into rpc package as baseline
* Create version enum
* Refactor API creation to use version enum

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

* [rpc] Refactor - Rejoin GetBlockByNumber & GetBlockByHash

* Both RPC versions are supported in 1 function
* Created types directory for v1 & v2 response types
* Added framework to make rpc layer more clear and legible

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt2

* Rejoin blockchain.go functions
* Create legacy service
* Improve inline documentation

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt3

* Use StructuredResponse when returning an exposed type from RPC
* Remove addrlock.go
* Add RPCBlock creation with exposed struct for easy utility from consumers
* Update blockchain.go to reflect changes of RPCBlock

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt3

* Create types for RPCs that are returned.
* Reorganize types to appropriate v1 & v2 package.

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt4

* Add context to all RPCs
* Add response switches based on different versions of the API
* Add debug logs for RPC that don't return errors

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt5

* Add versions switches to all transaction related RPCs
* Integrate response with newly defined structures
* Add debug messages for RPCs that don't return errors
* Add inline documentation for all transaction related RPCs

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt6

* Remove apiv2
* Move StructuredResponse & TxHistoryArgs to main rpc types.go

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

* [rpc] Refactor - Undo v1 & v2 code duplication pt7

* Create contract, pool, staking & transaction services
* Move apis into correct services
* Remove util.go (functions are now baked into RPCs in pool.go)
* Remove transaction.go (functions/methods are distributed among created services)
* Update changes for StructuredResponse

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

* [rpc] Refactor - Add rpc server start/stop to pkg

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

* [node] Refactor - use new RPC start / stop for servers

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

* [hmy] Refactor - rename network.go to net.go to match rpc

* Change names for consistency with rpc pkg

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

* fix go lint & go imports

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

* [rpc] Refactor - Fix GetTransactionReceipt for staking txs

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

* [rpc] Refactor - Rename v1 & v2 struct to not have leading 'RPC'

* Update comments

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

* [rpc] Refactor - make defaultBlocksPeriod be the num blocks per epoch

Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>
pull/3266/head
Daniel Van Der Maden 4 years ago committed by GitHub
parent 5062c178a7
commit 579250a071
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      hmy/blockchain.go
  2. 2
      hmy/hmy.go
  3. 2
      hmy/net.go
  4. 2
      hmy/staking.go
  5. 98
      internal/hmyapi/README.md
  6. 54
      internal/hmyapi/apiv1/addrlock.go
  7. 1033
      internal/hmyapi/apiv1/blockchain.go
  8. 32
      internal/hmyapi/apiv1/debug.go
  9. 18
      internal/hmyapi/apiv1/error.go
  10. 54
      internal/hmyapi/apiv1/harmony.go
  11. 29
      internal/hmyapi/apiv1/net.go
  12. 410
      internal/hmyapi/apiv1/transactionpool.go
  13. 538
      internal/hmyapi/apiv1/types.go
  14. 73
      internal/hmyapi/apiv1/util.go
  15. 54
      internal/hmyapi/apiv2/addrlock.go
  16. 987
      internal/hmyapi/apiv2/blockchain.go
  17. 32
      internal/hmyapi/apiv2/debug.go
  18. 69
      internal/hmyapi/apiv2/harmony.go
  19. 28
      internal/hmyapi/apiv2/net.go
  20. 432
      internal/hmyapi/apiv2/transactionpool.go
  21. 539
      internal/hmyapi/apiv2/types.go
  22. 74
      internal/hmyapi/apiv2/util.go
  23. 64
      internal/hmyapi/backend.go
  24. 136
      node/rpc.go
  25. 668
      rpc/blockchain.go
  26. 0
      rpc/common/hacks.go
  27. 25
      rpc/common/types.go
  28. 196
      rpc/contract.go
  29. 40
      rpc/debug.go
  30. 6
      rpc/error.go
  31. 0
      rpc/filters/api.go
  32. 0
      rpc/filters/filter.go
  33. 0
      rpc/filters/filter_criteria.go
  34. 0
      rpc/filters/filter_system.go
  35. 88
      rpc/harmony.go
  36. 59
      rpc/net.go
  37. 295
      rpc/pool.go
  38. 167
      rpc/rpc.go
  39. 621
      rpc/staking.go
  40. 747
      rpc/transaction.go
  41. 200
      rpc/types.go
  42. 40
      rpc/v1/legacy.go
  43. 735
      rpc/v1/types.go
  44. 39
      rpc/v2/legacy.go
  45. 734
      rpc/v2/types.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
}

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

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

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

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

@ -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 <http://www.gnu.org/licenses/>.
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()
}

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

@ -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 <http://www.gnu.org/licenses/>.
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()
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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)
}
Loading…
Cancel
Save