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
parent
5062c178a7
commit
579250a071
@ -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, |
||||
}, |
||||
} |
||||
} |
@ -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 |
||||
} |
@ -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…
Reference in new issue