Node API Refactor (Stage 2.1 of Node API Overhaul) (#3244)
* Add latest dir removal for make clean Fix docker image in README Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Expose Harmony attributes & remove accessors * Add internals for APIBackend & Harmony fuse * Add skeleton for refactor of GetTotalStakingSnapshot Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Re-implement APIBacked methods in Harmony * Add 'FIXME:done' comments to indicate implemented methods, some were left out as they are simple accessors and will transition to using the exposed harmony attributes. * Split functions into standalone files for clarity. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Fix GetTotalStakingSnapshot in Harmony implementation Previous implementation used rps.LatestBlockNumber, which is a constant -1, thus, the cache was never used Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Move ErrFinalizedTransaction to hmy.go Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make PublicHarmonyAPI use Harmony instead of Backed Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make NewPublicBlockChainAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make NewPublicTransactionPoolAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make DebugAPI use Harmony instead of Backend Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make filers use Harmony instead of Backend * Fix unhandled errors Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Change NetVersion to ChainID Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Change API service declaration to use Harmony * Remove irrelevant TODOs Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [node] Refactor - Move harmony create to APIs fetch Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Remove api_backend.go & Backend * Update hmyapi net.go to return ChainID as Version * Remove unused err return on New Harmony obj Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Prettify var names, structure & comments Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Prettify var names, structure & comments Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Remove backend interface * Fix lint Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Remove exposure for eventMux and chainDb This is to satisfy existing interfaces, mainly the in filters. Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy + hmyapi] Refactor - Fix imports Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmy] Refactor - Apply changes from #3243 Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Make filters use Backend instead of hmy This is for testing from eth if we need it in the future Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [hmyapi] Refactor - Fix imports Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>pull/3245/head
parent
a354b93676
commit
d4df3aa039
@ -1,949 +0,0 @@ |
|||||||
package hmy |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
"sync" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/math" |
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/ethereum/go-ethereum/rpc" |
|
||||||
"github.com/harmony-one/harmony/api/proto" |
|
||||||
"github.com/harmony-one/harmony/block" |
|
||||||
"github.com/harmony-one/harmony/consensus/quorum" |
|
||||||
"github.com/harmony-one/harmony/core" |
|
||||||
"github.com/harmony-one/harmony/core/rawdb" |
|
||||||
"github.com/harmony-one/harmony/core/state" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
"github.com/harmony-one/harmony/core/vm" |
|
||||||
"github.com/harmony-one/harmony/crypto/bls" |
|
||||||
internal_bls "github.com/harmony-one/harmony/crypto/bls" |
|
||||||
internal_common "github.com/harmony-one/harmony/internal/common" |
|
||||||
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
|
||||||
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
|
||||||
"github.com/harmony-one/harmony/internal/params" |
|
||||||
"github.com/harmony-one/harmony/internal/utils" |
|
||||||
"github.com/harmony-one/harmony/numeric" |
|
||||||
"github.com/harmony-one/harmony/shard" |
|
||||||
"github.com/harmony-one/harmony/shard/committee" |
|
||||||
"github.com/harmony-one/harmony/staking/availability" |
|
||||||
"github.com/harmony-one/harmony/staking/effective" |
|
||||||
"github.com/harmony-one/harmony/staking/network" |
|
||||||
staking "github.com/harmony-one/harmony/staking/types" |
|
||||||
lru "github.com/hashicorp/golang-lru" |
|
||||||
"github.com/libp2p/go-libp2p-core/peer" |
|
||||||
"github.com/pkg/errors" |
|
||||||
"golang.org/x/sync/singleflight" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// ErrFinalizedTransaction is returned if the transaction to be submitted is already on-chain
|
|
||||||
ErrFinalizedTransaction = errors.New("transaction already finalized") |
|
||||||
) |
|
||||||
|
|
||||||
// APIBackend An implementation of internal/hmyapi/Backend. Full client.
|
|
||||||
type APIBackend struct { |
|
||||||
hmy *Harmony |
|
||||||
TotalStakingCache struct { |
|
||||||
sync.Mutex |
|
||||||
BlockHeight int64 |
|
||||||
TotalStaking *big.Int |
|
||||||
} |
|
||||||
apiCache singleflight.Group |
|
||||||
LeaderCache *lru.Cache |
|
||||||
} |
|
||||||
|
|
||||||
// SingleFlightRequest ...
|
|
||||||
func (b *APIBackend) SingleFlightRequest( |
|
||||||
key string, |
|
||||||
fn func() (interface{}, error), |
|
||||||
) (interface{}, error) { |
|
||||||
res, err, _ := b.apiCache.Do(key, fn) |
|
||||||
return res, err |
|
||||||
} |
|
||||||
|
|
||||||
// SingleFlightForgetKey ...
|
|
||||||
func (b *APIBackend) SingleFlightForgetKey(key string) { |
|
||||||
b.apiCache.Forget(key) |
|
||||||
} |
|
||||||
|
|
||||||
// ChainDb ...
|
|
||||||
func (b *APIBackend) ChainDb() ethdb.Database { |
|
||||||
return b.hmy.chainDb |
|
||||||
} |
|
||||||
|
|
||||||
// GetBlock ...
|
|
||||||
func (b *APIBackend) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { |
|
||||||
return b.hmy.blockchain.GetBlockByHash(hash), nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetPoolTransaction ...
|
|
||||||
func (b *APIBackend) GetPoolTransaction(hash common.Hash) types.PoolTransaction { |
|
||||||
return b.hmy.txPool.Get(hash) |
|
||||||
} |
|
||||||
|
|
||||||
// BlockByNumber ...
|
|
||||||
func (b *APIBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { |
|
||||||
// Pending block is only known by the miner
|
|
||||||
if blockNr == rpc.PendingBlockNumber { |
|
||||||
return nil, errors.New("not implemented") |
|
||||||
} |
|
||||||
// Otherwise resolve and return the block
|
|
||||||
if blockNr == rpc.LatestBlockNumber { |
|
||||||
return b.hmy.blockchain.CurrentBlock(), nil |
|
||||||
} |
|
||||||
return b.hmy.blockchain.GetBlockByNumber(uint64(blockNr)), nil |
|
||||||
} |
|
||||||
|
|
||||||
// StateAndHeaderByNumber ...
|
|
||||||
func (b *APIBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) { |
|
||||||
// Pending state is only known by the miner
|
|
||||||
if blockNr == rpc.PendingBlockNumber { |
|
||||||
return nil, nil, errors.New("not implemented") |
|
||||||
} |
|
||||||
// Otherwise resolve the block number and return its state
|
|
||||||
header, err := b.HeaderByNumber(ctx, blockNr) |
|
||||||
if header == nil || err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
stateDb, err := b.hmy.blockchain.StateAt(header.Root()) |
|
||||||
return stateDb, header, err |
|
||||||
} |
|
||||||
|
|
||||||
// HeaderByNumber ...
|
|
||||||
func (b *APIBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) { |
|
||||||
// Pending block is only known by the miner
|
|
||||||
if blockNr == rpc.PendingBlockNumber { |
|
||||||
return nil, errors.New("not implemented") |
|
||||||
} |
|
||||||
// Otherwise resolve and return the block
|
|
||||||
if blockNr == rpc.LatestBlockNumber { |
|
||||||
return b.hmy.blockchain.CurrentBlock().Header(), nil |
|
||||||
} |
|
||||||
return b.hmy.blockchain.GetHeaderByNumber(uint64(blockNr)), nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetPoolNonce ...
|
|
||||||
func (b *APIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { |
|
||||||
return b.hmy.txPool.State().GetNonce(addr), nil |
|
||||||
} |
|
||||||
|
|
||||||
// SendTx ...
|
|
||||||
func (b *APIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { |
|
||||||
tx, _, _, _ := rawdb.ReadTransaction(b.ChainDb(), signedTx.Hash()) |
|
||||||
if tx == nil { |
|
||||||
return b.hmy.nodeAPI.AddPendingTransaction(signedTx) |
|
||||||
} |
|
||||||
return ErrFinalizedTransaction |
|
||||||
} |
|
||||||
|
|
||||||
// ChainConfig ...
|
|
||||||
func (b *APIBackend) ChainConfig() *params.ChainConfig { |
|
||||||
return b.hmy.blockchain.Config() |
|
||||||
} |
|
||||||
|
|
||||||
// CurrentBlock ...
|
|
||||||
func (b *APIBackend) CurrentBlock() *types.Block { |
|
||||||
return types.NewBlockWithHeader(b.hmy.blockchain.CurrentHeader()) |
|
||||||
} |
|
||||||
|
|
||||||
// GetReceipts ...
|
|
||||||
func (b *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { |
|
||||||
return b.hmy.blockchain.GetReceiptsByHash(hash), nil |
|
||||||
} |
|
||||||
|
|
||||||
// EventMux ...
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) EventMux() *event.TypeMux { return b.hmy.eventMux } |
|
||||||
|
|
||||||
const ( |
|
||||||
// BloomBitsBlocks is the number of blocks a single bloom bit section vector
|
|
||||||
// contains on the server side.
|
|
||||||
BloomBitsBlocks uint64 = 4096 |
|
||||||
) |
|
||||||
|
|
||||||
// BloomStatus ...
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) BloomStatus() (uint64, uint64) { |
|
||||||
sections, _, _ := b.hmy.bloomIndexer.Sections() |
|
||||||
return BloomBitsBlocks, sections |
|
||||||
} |
|
||||||
|
|
||||||
// ProtocolVersion ...
|
|
||||||
func (b *APIBackend) ProtocolVersion() int { |
|
||||||
return proto.ProtocolVersion |
|
||||||
} |
|
||||||
|
|
||||||
// Filter related APIs
|
|
||||||
|
|
||||||
// GetLogs ...
|
|
||||||
func (b *APIBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) { |
|
||||||
receipts := b.hmy.blockchain.GetReceiptsByHash(blockHash) |
|
||||||
if receipts == nil { |
|
||||||
return nil, errors.New("Missing receipts") |
|
||||||
} |
|
||||||
logs := make([][]*types.Log, len(receipts)) |
|
||||||
for i, receipt := range receipts { |
|
||||||
logs[i] = receipt.Logs |
|
||||||
} |
|
||||||
return logs, nil |
|
||||||
} |
|
||||||
|
|
||||||
// HeaderByHash ...
|
|
||||||
func (b *APIBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) { |
|
||||||
header := b.hmy.blockchain.GetHeaderByHash(blockHash) |
|
||||||
if header == nil { |
|
||||||
return nil, errors.New("Header is not found") |
|
||||||
} |
|
||||||
return header, nil |
|
||||||
} |
|
||||||
|
|
||||||
// ServiceFilter ...
|
|
||||||
func (b *APIBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { |
|
||||||
// TODO(ricl): implement
|
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeNewTxsEvent subcribes new tx event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { |
|
||||||
return b.hmy.TxPool().SubscribeNewTxsEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeChainEvent subcribes chain event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { |
|
||||||
return b.hmy.BlockChain().SubscribeChainEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeChainHeadEvent subcribes chain head event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { |
|
||||||
return b.hmy.BlockChain().SubscribeChainHeadEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeChainSideEvent subcribes chain side event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { |
|
||||||
return b.hmy.BlockChain().SubscribeChainSideEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeRemovedLogsEvent subcribes removed logs event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { |
|
||||||
return b.hmy.BlockChain().SubscribeRemovedLogsEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// SubscribeLogsEvent subcribes log event.
|
|
||||||
// TODO: this is not implemented or verified yet for harmony.
|
|
||||||
func (b *APIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { |
|
||||||
return b.hmy.BlockChain().SubscribeLogsEvent(ch) |
|
||||||
} |
|
||||||
|
|
||||||
// GetPoolTransactions returns pool transactions.
|
|
||||||
func (b *APIBackend) GetPoolTransactions() (types.PoolTransactions, error) { |
|
||||||
pending, err := b.hmy.txPool.Pending() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
queued, err := b.hmy.txPool.Queued() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
var txs types.PoolTransactions |
|
||||||
for _, batch := range pending { |
|
||||||
txs = append(txs, batch...) |
|
||||||
} |
|
||||||
for _, batch := range queued { |
|
||||||
txs = append(txs, batch...) |
|
||||||
} |
|
||||||
return txs, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetPoolStats returns the number of pending and queued transactions
|
|
||||||
func (b *APIBackend) GetPoolStats() (pendingCount, queuedCount int) { |
|
||||||
return b.hmy.txPool.Stats() |
|
||||||
} |
|
||||||
|
|
||||||
// GetAccountNonce returns the nonce value of the given address for the given block number
|
|
||||||
func (b *APIBackend) GetAccountNonce( |
|
||||||
ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) { |
|
||||||
state, _, err := b.StateAndHeaderByNumber(ctx, blockNr) |
|
||||||
if state == nil || err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
return state.GetNonce(address), state.Error() |
|
||||||
} |
|
||||||
|
|
||||||
// GetBalance returns balance of an given address.
|
|
||||||
func (b *APIBackend) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { |
|
||||||
state, _, err := b.StateAndHeaderByNumber(ctx, blockNr) |
|
||||||
if state == nil || err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return state.GetBalance(address), state.Error() |
|
||||||
} |
|
||||||
|
|
||||||
// GetTransactionsHistory returns list of transactions hashes of address.
|
|
||||||
func (b *APIBackend) GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) { |
|
||||||
return b.hmy.nodeAPI.GetTransactionsHistory(address, txType, order) |
|
||||||
} |
|
||||||
|
|
||||||
// GetStakingTransactionsHistory returns list of staking transactions hashes of address.
|
|
||||||
func (b *APIBackend) GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) { |
|
||||||
return b.hmy.nodeAPI.GetStakingTransactionsHistory(address, txType, order) |
|
||||||
} |
|
||||||
|
|
||||||
// GetTransactionsCount returns the number of regular transactions of address.
|
|
||||||
func (b *APIBackend) GetTransactionsCount(address, txType string) (uint64, error) { |
|
||||||
return b.hmy.nodeAPI.GetTransactionsCount(address, txType) |
|
||||||
} |
|
||||||
|
|
||||||
// GetStakingTransactionsCount returns the number of staking transactions of address.
|
|
||||||
func (b *APIBackend) GetStakingTransactionsCount(address, txType string) (uint64, error) { |
|
||||||
return b.hmy.nodeAPI.GetStakingTransactionsCount(address, txType) |
|
||||||
} |
|
||||||
|
|
||||||
// NetVersion returns net version
|
|
||||||
func (b *APIBackend) NetVersion() uint64 { |
|
||||||
return b.hmy.NetVersion() |
|
||||||
} |
|
||||||
|
|
||||||
// GetEVM returns a new EVM entity
|
|
||||||
func (b *APIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) { |
|
||||||
// TODO(ricl): The code is borrowed from [go-ethereum](https://github.com/ethereum/go-ethereum/blob/40cdcf8c47ff094775aca08fd5d94051f9cf1dbb/les/api_backend.go#L114)
|
|
||||||
// [question](https://ethereum.stackexchange.com/q/72977/54923)
|
|
||||||
// Might need to reconsider the SetBalance behavior
|
|
||||||
state.SetBalance(msg.From(), math.MaxBig256) |
|
||||||
vmError := func() error { return nil } |
|
||||||
|
|
||||||
context := core.NewEVMContext(msg, header, b.hmy.BlockChain(), nil) |
|
||||||
return vm.NewEVM(context, state, b.hmy.blockchain.Config(), *b.hmy.blockchain.GetVMConfig()), vmError, nil |
|
||||||
} |
|
||||||
|
|
||||||
// RPCGasCap returns the gas cap of rpc
|
|
||||||
func (b *APIBackend) RPCGasCap() *big.Int { |
|
||||||
return b.hmy.RPCGasCap // TODO(ricl): should be hmy.config.RPCGasCap
|
|
||||||
} |
|
||||||
|
|
||||||
// GetShardID returns shardID of this node
|
|
||||||
func (b *APIBackend) GetShardID() uint32 { |
|
||||||
return b.hmy.shardID |
|
||||||
} |
|
||||||
|
|
||||||
// GetValidators returns validators for a particular epoch.
|
|
||||||
func (b *APIBackend) GetValidators(epoch *big.Int) (*shard.Committee, error) { |
|
||||||
state, err := b.hmy.BlockChain().ReadShardState(epoch) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
for _, committee := range state.Shards { |
|
||||||
if committee.ShardID == b.GetShardID() { |
|
||||||
return &committee, nil |
|
||||||
} |
|
||||||
} |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
// ResendCx retrieve blockHash from txID and add blockHash to CxPool for resending
|
|
||||||
// Note that cross shard txn is only for regular txns, not for staking txns, so the input txn hash
|
|
||||||
// is expected to be regular txn hash
|
|
||||||
func (b *APIBackend) ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) { |
|
||||||
blockHash, blockNum, index := b.hmy.BlockChain().ReadTxLookupEntry(txID) |
|
||||||
if blockHash == (common.Hash{}) { |
|
||||||
return 0, false |
|
||||||
} |
|
||||||
|
|
||||||
blk := b.hmy.BlockChain().GetBlockByHash(blockHash) |
|
||||||
if blk == nil { |
|
||||||
return 0, false |
|
||||||
} |
|
||||||
|
|
||||||
txs := blk.Transactions() |
|
||||||
// a valid index is from 0 to len-1
|
|
||||||
if int(index) > len(txs)-1 { |
|
||||||
return 0, false |
|
||||||
} |
|
||||||
tx := txs[int(index)] |
|
||||||
|
|
||||||
// check whether it is a valid cross shard tx
|
|
||||||
if tx.ShardID() == tx.ToShardID() || blk.Header().ShardID() != tx.ShardID() { |
|
||||||
return 0, false |
|
||||||
} |
|
||||||
entry := core.CxEntry{blockHash, tx.ToShardID()} |
|
||||||
success := b.hmy.CxPool().Add(entry) |
|
||||||
return blockNum, success |
|
||||||
} |
|
||||||
|
|
||||||
// IsLeader exposes if node is currently leader
|
|
||||||
func (b *APIBackend) IsLeader() bool { |
|
||||||
return b.hmy.nodeAPI.IsCurrentlyLeader() |
|
||||||
} |
|
||||||
|
|
||||||
// SendStakingTx adds a staking transaction
|
|
||||||
func (b *APIBackend) SendStakingTx(ctx context.Context, signedStakingTx *staking.StakingTransaction) error { |
|
||||||
stx, _, _, _ := rawdb.ReadStakingTransaction(b.ChainDb(), signedStakingTx.Hash()) |
|
||||||
if stx == nil { |
|
||||||
return b.hmy.nodeAPI.AddPendingStakingTransaction(signedStakingTx) |
|
||||||
} |
|
||||||
return ErrFinalizedTransaction |
|
||||||
} |
|
||||||
|
|
||||||
// GetElectedValidatorAddresses returns the address of elected validators for current epoch
|
|
||||||
func (b *APIBackend) GetElectedValidatorAddresses() []common.Address { |
|
||||||
list, _ := b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentBlock().Epoch()) |
|
||||||
return list.StakedValidators().Addrs |
|
||||||
} |
|
||||||
|
|
||||||
// GetAllValidatorAddresses returns the up to date validator candidates for next epoch
|
|
||||||
func (b *APIBackend) GetAllValidatorAddresses() []common.Address { |
|
||||||
return b.hmy.BlockChain().ValidatorCandidates() |
|
||||||
} |
|
||||||
|
|
||||||
var ( |
|
||||||
zero = numeric.ZeroDec() |
|
||||||
) |
|
||||||
|
|
||||||
// GetValidatorInformation returns the information of validator
|
|
||||||
func (b *APIBackend) GetValidatorInformation( |
|
||||||
addr common.Address, block *types.Block, |
|
||||||
) (*staking.ValidatorRPCEnhanced, error) { |
|
||||||
bc := b.hmy.BlockChain() |
|
||||||
wrapper, err := bc.ReadValidatorInformationAt(addr, block.Root()) |
|
||||||
if err != nil { |
|
||||||
s, _ := internal_common.AddressToBech32(addr) |
|
||||||
return nil, errors.Wrapf(err, "not found address in current state %s", s) |
|
||||||
} |
|
||||||
|
|
||||||
now := block.Epoch() |
|
||||||
// At the last block of epoch, block epoch is e while val.LastEpochInCommittee
|
|
||||||
// is already updated to e+1. So need the >= check rather than ==
|
|
||||||
inCommittee := wrapper.LastEpochInCommittee.Cmp(now) >= 0 |
|
||||||
defaultReply := &staking.ValidatorRPCEnhanced{ |
|
||||||
CurrentlyInCommittee: inCommittee, |
|
||||||
Wrapper: *wrapper, |
|
||||||
Performance: nil, |
|
||||||
ComputedMetrics: nil, |
|
||||||
TotalDelegated: wrapper.TotalDelegation(), |
|
||||||
EPoSStatus: effective.ValidatorStatus( |
|
||||||
inCommittee, wrapper.Status, |
|
||||||
).String(), |
|
||||||
EPoSWinningStake: nil, |
|
||||||
BootedStatus: nil, |
|
||||||
ActiveStatus: wrapper.Validator.Status.String(), |
|
||||||
Lifetime: &staking.AccumulatedOverLifetime{ |
|
||||||
wrapper.BlockReward, |
|
||||||
wrapper.Counters, |
|
||||||
zero, |
|
||||||
nil, |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
snapshot, err := bc.ReadValidatorSnapshotAtEpoch( |
|
||||||
now, addr, |
|
||||||
) |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
return defaultReply, nil |
|
||||||
} |
|
||||||
|
|
||||||
computed := availability.ComputeCurrentSigning( |
|
||||||
snapshot.Validator, wrapper, |
|
||||||
) |
|
||||||
beaconChainBlocks := uint64( |
|
||||||
b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64(), |
|
||||||
) % shard.Schedule.BlocksPerEpoch() |
|
||||||
computed.BlocksLeftInEpoch = shard.Schedule.BlocksPerEpoch() - beaconChainBlocks |
|
||||||
|
|
||||||
if defaultReply.CurrentlyInCommittee { |
|
||||||
defaultReply.Performance = &staking.CurrentEpochPerformance{ |
|
||||||
CurrentSigningPercentage: *computed, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
stats, err := bc.ReadValidatorStats(addr) |
|
||||||
if err != nil { |
|
||||||
// when validator has no stats, default boot-status to not booted
|
|
||||||
notBooted := effective.NotBooted.String() |
|
||||||
defaultReply.BootedStatus = ¬Booted |
|
||||||
return defaultReply, nil |
|
||||||
} |
|
||||||
|
|
||||||
latestAPR := numeric.ZeroDec() |
|
||||||
l := len(stats.APRs) |
|
||||||
if l > 0 { |
|
||||||
latestAPR = stats.APRs[l-1].Value |
|
||||||
} |
|
||||||
defaultReply.Lifetime.APR = latestAPR |
|
||||||
defaultReply.Lifetime.EpochAPRs = stats.APRs |
|
||||||
|
|
||||||
// average apr cache keys
|
|
||||||
// key := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64())
|
|
||||||
// prevKey := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()-1)
|
|
||||||
|
|
||||||
// delete entry for previous epoch
|
|
||||||
// b.apiCache.Forget(prevKey)
|
|
||||||
|
|
||||||
// calculate last APRHistoryLength epochs for averaging APR
|
|
||||||
// epochFrom := bc.Config().StakingEpoch
|
|
||||||
// nowMinus := big.NewInt(0).Sub(now, big.NewInt(staking.APRHistoryLength))
|
|
||||||
// if nowMinus.Cmp(epochFrom) > 0 {
|
|
||||||
// epochFrom = nowMinus
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if len(stats.APRs) > 0 && stats.APRs[0].Epoch.Cmp(epochFrom) > 0 {
|
|
||||||
// epochFrom = stats.APRs[0].Epoch
|
|
||||||
// }
|
|
||||||
|
|
||||||
// epochToAPRs := map[int64]numeric.Dec{}
|
|
||||||
// for i := 0; i < len(stats.APRs); i++ {
|
|
||||||
// entry := stats.APRs[i]
|
|
||||||
// epochToAPRs[entry.Epoch.Int64()] = entry.Value
|
|
||||||
// }
|
|
||||||
|
|
||||||
// at this point, validator is active and has apr's for the recent 100 epochs
|
|
||||||
// compute average apr over history
|
|
||||||
// if avgAPR, err := b.SingleFlightRequest(
|
|
||||||
// key, func() (interface{}, error) {
|
|
||||||
// total := numeric.ZeroDec()
|
|
||||||
// count := 0
|
|
||||||
// for i := epochFrom.Int64(); i < now.Int64(); i++ {
|
|
||||||
// if apr, ok := epochToAPRs[i]; ok {
|
|
||||||
// total = total.Add(apr)
|
|
||||||
// }
|
|
||||||
// count++
|
|
||||||
// }
|
|
||||||
// if count == 0 {
|
|
||||||
// return nil, errors.New("no apr snapshots available")
|
|
||||||
// }
|
|
||||||
// return total.QuoInt64(int64(count)), nil
|
|
||||||
// },
|
|
||||||
// ); err != nil {
|
|
||||||
// // could not compute average apr from snapshot
|
|
||||||
// // assign the latest apr available from stats
|
|
||||||
// defaultReply.Lifetime.APR = numeric.ZeroDec()
|
|
||||||
// } else {
|
|
||||||
// defaultReply.Lifetime.APR = avgAPR.(numeric.Dec)
|
|
||||||
// }
|
|
||||||
|
|
||||||
if defaultReply.CurrentlyInCommittee { |
|
||||||
defaultReply.ComputedMetrics = stats |
|
||||||
defaultReply.EPoSWinningStake = &stats.TotalEffectiveStake |
|
||||||
} |
|
||||||
|
|
||||||
if !defaultReply.CurrentlyInCommittee { |
|
||||||
reason := stats.BootedStatus.String() |
|
||||||
defaultReply.BootedStatus = &reason |
|
||||||
} |
|
||||||
|
|
||||||
return defaultReply, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetMedianRawStakeSnapshot ..
|
|
||||||
func (b *APIBackend) GetMedianRawStakeSnapshot() ( |
|
||||||
*committee.CompletedEPoSRound, error, |
|
||||||
) { |
|
||||||
blockNr := b.CurrentBlock().NumberU64() |
|
||||||
key := fmt.Sprintf("median-%d", blockNr) |
|
||||||
|
|
||||||
// delete cache for previous block
|
|
||||||
prevKey := fmt.Sprintf("median-%d", blockNr-1) |
|
||||||
b.apiCache.Forget(prevKey) |
|
||||||
|
|
||||||
res, err := b.SingleFlightRequest( |
|
||||||
key, |
|
||||||
func() (interface{}, error) { |
|
||||||
// Compute for next epoch
|
|
||||||
epoch := big.NewInt(0).Add(b.CurrentBlock().Epoch(), big.NewInt(1)) |
|
||||||
return committee.NewEPoSRound(epoch, b.hmy.BlockChain()) |
|
||||||
}, |
|
||||||
) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return res.(*committee.CompletedEPoSRound), nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetLatestChainHeaders ..
|
|
||||||
func (b *APIBackend) GetLatestChainHeaders() *block.HeaderPair { |
|
||||||
return &block.HeaderPair{ |
|
||||||
BeaconHeader: b.hmy.BeaconChain().CurrentHeader(), |
|
||||||
ShardHeader: b.hmy.BlockChain().CurrentHeader(), |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// GetTotalStakingSnapshot ..
|
|
||||||
func (b *APIBackend) GetTotalStakingSnapshot() *big.Int { |
|
||||||
b.TotalStakingCache.Lock() |
|
||||||
defer b.TotalStakingCache.Unlock() |
|
||||||
if b.TotalStakingCache.BlockHeight != -1 && |
|
||||||
b.TotalStakingCache.BlockHeight > int64(rpc.LatestBlockNumber)-20 { |
|
||||||
return b.TotalStakingCache.TotalStaking |
|
||||||
} |
|
||||||
b.TotalStakingCache.BlockHeight = int64(rpc.LatestBlockNumber) |
|
||||||
candidates := b.hmy.BlockChain().ValidatorCandidates() |
|
||||||
if len(candidates) == 0 { |
|
||||||
b.TotalStakingCache.TotalStaking = big.NewInt(0) |
|
||||||
return b.TotalStakingCache.TotalStaking |
|
||||||
} |
|
||||||
stakes := big.NewInt(0) |
|
||||||
for i := range candidates { |
|
||||||
snapshot, _ := b.hmy.BlockChain().ReadValidatorSnapshot(candidates[i]) |
|
||||||
validator, _ := b.hmy.BlockChain().ReadValidatorInformation(candidates[i]) |
|
||||||
if !committee.IsEligibleForEPoSAuction( |
|
||||||
snapshot, validator, |
|
||||||
) { |
|
||||||
continue |
|
||||||
} |
|
||||||
for i := range validator.Delegations { |
|
||||||
stakes.Add(stakes, validator.Delegations[i].Amount) |
|
||||||
} |
|
||||||
} |
|
||||||
b.TotalStakingCache.TotalStaking = stakes |
|
||||||
return b.TotalStakingCache.TotalStaking |
|
||||||
} |
|
||||||
|
|
||||||
// GetDelegationsByValidator returns all delegation information of a validator
|
|
||||||
func (b *APIBackend) GetDelegationsByValidator(validator common.Address) []*staking.Delegation { |
|
||||||
wrapper, err := b.hmy.BlockChain().ReadValidatorInformation(validator) |
|
||||||
if err != nil || wrapper == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
delegations := []*staking.Delegation{} |
|
||||||
for i := range wrapper.Delegations { |
|
||||||
delegations = append(delegations, &wrapper.Delegations[i]) |
|
||||||
} |
|
||||||
return delegations |
|
||||||
} |
|
||||||
|
|
||||||
// GetDelegationsByDelegatorByBlock returns all delegation information of a delegator
|
|
||||||
func (b *APIBackend) GetDelegationsByDelegatorByBlock( |
|
||||||
delegator common.Address, block *types.Block, |
|
||||||
) ([]common.Address, []*staking.Delegation) { |
|
||||||
addresses := []common.Address{} |
|
||||||
delegations := []*staking.Delegation{} |
|
||||||
delegationIndexes, err := b.hmy.BlockChain(). |
|
||||||
ReadDelegationsByDelegatorAt(delegator, block.Number()) |
|
||||||
if err != nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
for i := range delegationIndexes { |
|
||||||
wrapper, err := b.hmy.BlockChain().ReadValidatorInformationAt( |
|
||||||
delegationIndexes[i].ValidatorAddress, block.Root(), |
|
||||||
) |
|
||||||
if err != nil || wrapper == nil { |
|
||||||
return nil, nil |
|
||||||
} |
|
||||||
|
|
||||||
if uint64(len(wrapper.Delegations)) > delegationIndexes[i].Index { |
|
||||||
delegations = append(delegations, &wrapper.Delegations[delegationIndexes[i].Index]) |
|
||||||
} else { |
|
||||||
delegations = append(delegations, nil) |
|
||||||
} |
|
||||||
addresses = append(addresses, delegationIndexes[i].ValidatorAddress) |
|
||||||
} |
|
||||||
return addresses, delegations |
|
||||||
} |
|
||||||
|
|
||||||
// GetDelegationsByDelegator returns all delegation information of a delegator
|
|
||||||
func (b *APIBackend) GetDelegationsByDelegator( |
|
||||||
delegator common.Address, |
|
||||||
) ([]common.Address, []*staking.Delegation) { |
|
||||||
block := b.hmy.BlockChain().CurrentBlock() |
|
||||||
return b.GetDelegationsByDelegatorByBlock(delegator, block) |
|
||||||
} |
|
||||||
|
|
||||||
// GetValidatorSelfDelegation returns the amount of staking after applying all delegated stakes
|
|
||||||
func (b *APIBackend) GetValidatorSelfDelegation(addr common.Address) *big.Int { |
|
||||||
wrapper, err := b.hmy.BlockChain().ReadValidatorInformation(addr) |
|
||||||
if err != nil || wrapper == nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
if len(wrapper.Delegations) == 0 { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return wrapper.Delegations[0].Amount |
|
||||||
} |
|
||||||
|
|
||||||
// GetShardState ...
|
|
||||||
func (b *APIBackend) GetShardState() (*shard.State, error) { |
|
||||||
return b.hmy.BlockChain().ReadShardState(b.hmy.BlockChain().CurrentHeader().Epoch()) |
|
||||||
} |
|
||||||
|
|
||||||
// GetCurrentStakingErrorSink ..
|
|
||||||
func (b *APIBackend) GetCurrentStakingErrorSink() types.TransactionErrorReports { |
|
||||||
return b.hmy.nodeAPI.ReportStakingErrorSink() |
|
||||||
} |
|
||||||
|
|
||||||
// GetCurrentTransactionErrorSink ..
|
|
||||||
func (b *APIBackend) GetCurrentTransactionErrorSink() types.TransactionErrorReports { |
|
||||||
return b.hmy.nodeAPI.ReportPlainErrorSink() |
|
||||||
} |
|
||||||
|
|
||||||
// GetPendingCXReceipts ..
|
|
||||||
func (b *APIBackend) GetPendingCXReceipts() []*types.CXReceiptsProof { |
|
||||||
return b.hmy.nodeAPI.PendingCXReceipts() |
|
||||||
} |
|
||||||
|
|
||||||
// GetCurrentUtilityMetrics ..
|
|
||||||
func (b *APIBackend) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) { |
|
||||||
return network.NewUtilityMetricSnapshot(b.hmy.BlockChain()) |
|
||||||
} |
|
||||||
|
|
||||||
func (b *APIBackend) readAndUpdateRawStakes( |
|
||||||
epoch *big.Int, |
|
||||||
decider quorum.Decider, |
|
||||||
comm shard.Committee, |
|
||||||
rawStakes []effective.SlotPurchase, |
|
||||||
validatorSpreads map[common.Address]numeric.Dec, |
|
||||||
) []effective.SlotPurchase { |
|
||||||
for i := range comm.Slots { |
|
||||||
slot := comm.Slots[i] |
|
||||||
slotAddr := slot.EcdsaAddress |
|
||||||
slotKey := slot.BLSPublicKey |
|
||||||
spread, ok := validatorSpreads[slotAddr] |
|
||||||
if !ok { |
|
||||||
snapshot, err := b.hmy.BlockChain().ReadValidatorSnapshotAtEpoch(epoch, slotAddr) |
|
||||||
if err != nil { |
|
||||||
continue |
|
||||||
} |
|
||||||
wrapper := snapshot.Validator |
|
||||||
spread = numeric.NewDecFromBigInt(wrapper.TotalDelegation()). |
|
||||||
QuoInt64(int64(len(wrapper.SlotPubKeys))) |
|
||||||
validatorSpreads[slotAddr] = spread |
|
||||||
} |
|
||||||
|
|
||||||
commonRPC.SetRawStake(decider, slotKey, spread) |
|
||||||
// add entry to array for median calculation
|
|
||||||
rawStakes = append(rawStakes, effective.SlotPurchase{ |
|
||||||
slotAddr, |
|
||||||
slotKey, |
|
||||||
spread, |
|
||||||
spread, |
|
||||||
}) |
|
||||||
} |
|
||||||
return rawStakes |
|
||||||
} |
|
||||||
|
|
||||||
func (b *APIBackend) getSuperCommittees() (*quorum.Transition, error) { |
|
||||||
nowE := b.hmy.BlockChain().CurrentHeader().Epoch() |
|
||||||
thenE := new(big.Int).Sub(nowE, common.Big1) |
|
||||||
|
|
||||||
var ( |
|
||||||
nowCommittee, prevCommittee *shard.State |
|
||||||
err error |
|
||||||
) |
|
||||||
nowCommittee, err = b.hmy.BlockChain().ReadShardState(nowE) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
prevCommittee, err = b.hmy.BlockChain().ReadShardState(thenE) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
stakedSlotsNow, stakedSlotsThen := |
|
||||||
shard.ExternalSlotsAvailableForEpoch(nowE), |
|
||||||
shard.ExternalSlotsAvailableForEpoch(thenE) |
|
||||||
|
|
||||||
then, now := |
|
||||||
quorum.NewRegistry(stakedSlotsThen), |
|
||||||
quorum.NewRegistry(stakedSlotsNow) |
|
||||||
|
|
||||||
rawStakes := []effective.SlotPurchase{} |
|
||||||
validatorSpreads := map[common.Address]numeric.Dec{} |
|
||||||
for _, comm := range prevCommittee.Shards { |
|
||||||
decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) |
|
||||||
// before staking skip computing
|
|
||||||
if b.hmy.BlockChain().Config().IsStaking(prevCommittee.Epoch) { |
|
||||||
if _, err := decider.SetVoters(&comm, prevCommittee.Epoch); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
rawStakes = b.readAndUpdateRawStakes(thenE, decider, comm, rawStakes, validatorSpreads) |
|
||||||
then.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider |
|
||||||
} |
|
||||||
then.MedianStake = effective.Median(rawStakes) |
|
||||||
|
|
||||||
rawStakes = []effective.SlotPurchase{} |
|
||||||
validatorSpreads = map[common.Address]numeric.Dec{} |
|
||||||
for _, comm := range nowCommittee.Shards { |
|
||||||
decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) |
|
||||||
if _, err := decider.SetVoters(&comm, nowCommittee.Epoch); err != nil { |
|
||||||
return nil, errors.Wrapf( |
|
||||||
err, |
|
||||||
"committee is only available from staking epoch: %v, current epoch: %v", |
|
||||||
b.hmy.BlockChain().Config().StakingEpoch, |
|
||||||
b.hmy.BlockChain().CurrentHeader().Epoch(), |
|
||||||
) |
|
||||||
} |
|
||||||
rawStakes = b.readAndUpdateRawStakes(nowE, decider, comm, rawStakes, validatorSpreads) |
|
||||||
now.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider |
|
||||||
} |
|
||||||
now.MedianStake = effective.Median(rawStakes) |
|
||||||
|
|
||||||
return &quorum.Transition{then, now}, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetSuperCommittees ..
|
|
||||||
func (b *APIBackend) GetSuperCommittees() (*quorum.Transition, error) { |
|
||||||
nowE := b.hmy.BlockChain().CurrentHeader().Epoch() |
|
||||||
key := fmt.Sprintf("sc-%s", nowE.String()) |
|
||||||
|
|
||||||
res, err := b.SingleFlightRequest( |
|
||||||
key, func() (interface{}, error) { |
|
||||||
thenE := new(big.Int).Sub(nowE, common.Big1) |
|
||||||
thenKey := fmt.Sprintf("sc-%s", thenE.String()) |
|
||||||
b.apiCache.Forget(thenKey) |
|
||||||
return b.getSuperCommittees() |
|
||||||
}) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return res.(*quorum.Transition), err |
|
||||||
} |
|
||||||
|
|
||||||
// GetCurrentBadBlocks ..
|
|
||||||
func (b *APIBackend) GetCurrentBadBlocks() []core.BadBlock { |
|
||||||
return b.hmy.BlockChain().BadBlocks() |
|
||||||
} |
|
||||||
|
|
||||||
// GetLastCrossLinks ..
|
|
||||||
func (b *APIBackend) GetLastCrossLinks() ([]*types.CrossLink, error) { |
|
||||||
crossLinks := []*types.CrossLink{} |
|
||||||
for i := uint32(1); i < shard.Schedule.InstanceForEpoch(b.CurrentBlock().Epoch()).NumShards(); i++ { |
|
||||||
link, err := b.hmy.BlockChain().ReadShardLastCrossLink(i) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
crossLinks = append(crossLinks, link) |
|
||||||
} |
|
||||||
|
|
||||||
return crossLinks, nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetNodeMetadata ..
|
|
||||||
func (b *APIBackend) GetNodeMetadata() commonRPC.NodeMetadata { |
|
||||||
cfg := nodeconfig.GetDefaultConfig() |
|
||||||
header := b.CurrentBlock().Header() |
|
||||||
var blockEpoch *uint64 |
|
||||||
|
|
||||||
if header.ShardID() == shard.BeaconChainShardID { |
|
||||||
sched := shard.Schedule.InstanceForEpoch(header.Epoch()) |
|
||||||
b := sched.BlocksPerEpoch() |
|
||||||
blockEpoch = &b |
|
||||||
} |
|
||||||
|
|
||||||
blsKeys := []string{} |
|
||||||
if cfg.ConsensusPriKey != nil { |
|
||||||
for _, key := range cfg.ConsensusPriKey { |
|
||||||
blsKeys = append(blsKeys, key.Pub.Bytes.Hex()) |
|
||||||
} |
|
||||||
} |
|
||||||
c := commonRPC.C{} |
|
||||||
c.TotalKnownPeers, c.Connected, c.NotConnected = b.hmy.nodeAPI.PeerConnectivity() |
|
||||||
|
|
||||||
return commonRPC.NodeMetadata{ |
|
||||||
blsKeys, |
|
||||||
nodeconfig.GetVersion(), |
|
||||||
string(cfg.GetNetworkType()), |
|
||||||
*b.ChainConfig(), |
|
||||||
b.IsLeader(), |
|
||||||
b.GetShardID(), |
|
||||||
header.Epoch().Uint64(), |
|
||||||
blockEpoch, |
|
||||||
cfg.Role().String(), |
|
||||||
cfg.DNSZone, |
|
||||||
cfg.GetArchival(), |
|
||||||
b.hmy.nodeAPI.GetNodeBootTime(), |
|
||||||
nodeconfig.GetPeerID(), |
|
||||||
c, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// GetPeerInfo returns the peer info to the node, including blocked peer, connected peer, number of peers
|
|
||||||
func (b *APIBackend) GetPeerInfo() commonRPC.NodePeerInfo { |
|
||||||
|
|
||||||
topics := b.hmy.nodeAPI.ListTopic() |
|
||||||
p := make([]commonRPC.P, len(topics)) |
|
||||||
|
|
||||||
for i, t := range topics { |
|
||||||
topicPeer := b.hmy.nodeAPI.ListPeer(t) |
|
||||||
p[i].Topic = t |
|
||||||
p[i].Peers = make([]peer.ID, len(topicPeer)) |
|
||||||
copy(p[i].Peers, topicPeer) |
|
||||||
} |
|
||||||
|
|
||||||
return commonRPC.NodePeerInfo{ |
|
||||||
PeerID: nodeconfig.GetPeerID(), |
|
||||||
BlockedPeers: b.hmy.nodeAPI.ListBlockedPeer(), |
|
||||||
P: p, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// GetBlockSigners ..
|
|
||||||
func (b *APIBackend) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *internal_bls.Mask, error) { |
|
||||||
block, err := b.BlockByNumber(ctx, blockNr) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
blockWithSigners, err := b.BlockByNumber(ctx, blockNr+1) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
committee, err := b.GetValidators(block.Epoch()) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
pubkeys := make([]internal_bls.PublicKeyWrapper, len(committee.Slots)) |
|
||||||
for _, validator := range committee.Slots { |
|
||||||
wrapper := internal_bls.PublicKeyWrapper{Bytes: validator.BLSPublicKey} |
|
||||||
if wrapper.Object, err = bls.BytesToBLSPublicKey(wrapper.Bytes[:]); err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
mask, err := internal_bls.NewMask(pubkeys, nil) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
return committee.Slots, mask, nil |
|
||||||
} |
|
||||||
|
|
||||||
// IsStakingEpoch ...
|
|
||||||
func (b *APIBackend) IsStakingEpoch(epoch *big.Int) bool { |
|
||||||
return b.hmy.BlockChain().Config().IsStaking(epoch) |
|
||||||
} |
|
||||||
|
|
||||||
// GetLeaderAddress returns the one address of the leader
|
|
||||||
func (b *APIBackend) GetLeaderAddress(a common.Address, e *big.Int) string { |
|
||||||
if b.IsStakingEpoch(e) { |
|
||||||
if leader, exists := b.LeaderCache.Get(a); exists { |
|
||||||
bech32, _ := internal_common.AddressToBech32(leader.(common.Address)) |
|
||||||
return bech32 |
|
||||||
} |
|
||||||
committee, err := b.GetValidators(e) |
|
||||||
if err != nil { |
|
||||||
return "" |
|
||||||
} |
|
||||||
for _, v := range committee.Slots { |
|
||||||
addr := utils.GetAddressFromBLSPubKeyBytes(v.BLSPublicKey[:]) |
|
||||||
b.LeaderCache.Add(addr, v.EcdsaAddress) |
|
||||||
if addr == a { |
|
||||||
bech32, _ := internal_common.AddressToBech32(v.EcdsaAddress) |
|
||||||
return bech32 |
|
||||||
} |
|
||||||
} |
|
||||||
// Did not find matching address
|
|
||||||
return "missing" // FIXME: Change this to empty string
|
|
||||||
} |
|
||||||
bech32, _ := internal_common.AddressToBech32(a) |
|
||||||
return bech32 |
|
||||||
} |
|
@ -1,114 +0,0 @@ |
|||||||
package hmy |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
"sync" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/harmony-one/harmony/core" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
staking "github.com/harmony-one/harmony/staking/types" |
|
||||||
lru "github.com/hashicorp/golang-lru" |
|
||||||
"github.com/libp2p/go-libp2p-core/peer" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
leaderCacheSize = 250 // Approx number of BLS keys in committee
|
|
||||||
) |
|
||||||
|
|
||||||
// Harmony implements the Harmony full node service.
|
|
||||||
type Harmony struct { |
|
||||||
// Channel for shutting down the service
|
|
||||||
shutdownChan chan bool // Channel for shutting down the Harmony
|
|
||||||
bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
|
||||||
blockchain *core.BlockChain |
|
||||||
beaconchain *core.BlockChain |
|
||||||
txPool *core.TxPool |
|
||||||
cxPool *core.CxPool |
|
||||||
eventMux *event.TypeMux |
|
||||||
// DB interfaces
|
|
||||||
chainDb ethdb.Database // Block chain database
|
|
||||||
bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
|
|
||||||
APIBackend *APIBackend |
|
||||||
nodeAPI NodeAPI |
|
||||||
// aka network version, which is used to identify which network we are using
|
|
||||||
networkID uint64 |
|
||||||
// RPCGasCap is the global gas cap for eth-call variants.
|
|
||||||
RPCGasCap *big.Int `toml:",omitempty"` |
|
||||||
shardID uint32 |
|
||||||
} |
|
||||||
|
|
||||||
// NodeAPI is the list of functions from node used to call rpc apis.
|
|
||||||
type NodeAPI interface { |
|
||||||
AddPendingStakingTransaction(*staking.StakingTransaction) error |
|
||||||
AddPendingTransaction(newTx *types.Transaction) error |
|
||||||
Blockchain() *core.BlockChain |
|
||||||
Beaconchain() *core.BlockChain |
|
||||||
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetTransactionsCount(address, txType string) (uint64, error) |
|
||||||
GetStakingTransactionsCount(address, txType string) (uint64, error) |
|
||||||
IsCurrentlyLeader() bool |
|
||||||
ReportStakingErrorSink() types.TransactionErrorReports |
|
||||||
ReportPlainErrorSink() types.TransactionErrorReports |
|
||||||
PendingCXReceipts() []*types.CXReceiptsProof |
|
||||||
GetNodeBootTime() int64 |
|
||||||
PeerConnectivity() (int, int, int) |
|
||||||
ListPeer(topic string) []peer.ID |
|
||||||
ListTopic() []string |
|
||||||
ListBlockedPeer() []peer.ID |
|
||||||
} |
|
||||||
|
|
||||||
// New creates a new Harmony object (including the
|
|
||||||
// initialisation of the common Harmony object)
|
|
||||||
func New( |
|
||||||
nodeAPI NodeAPI, txPool *core.TxPool, |
|
||||||
cxPool *core.CxPool, eventMux *event.TypeMux, shardID uint32, |
|
||||||
) (*Harmony, error) { |
|
||||||
chainDb := nodeAPI.Blockchain().ChainDB() |
|
||||||
hmy := &Harmony{ |
|
||||||
shutdownChan: make(chan bool), |
|
||||||
bloomRequests: make(chan chan *bloombits.Retrieval), |
|
||||||
blockchain: nodeAPI.Blockchain(), |
|
||||||
beaconchain: nodeAPI.Beaconchain(), |
|
||||||
txPool: txPool, |
|
||||||
cxPool: cxPool, |
|
||||||
eventMux: eventMux, |
|
||||||
chainDb: chainDb, |
|
||||||
nodeAPI: nodeAPI, |
|
||||||
networkID: 1, // TODO(ricl): this should be from config
|
|
||||||
shardID: shardID, |
|
||||||
} |
|
||||||
cache, _ := lru.New(leaderCacheSize) |
|
||||||
hmy.APIBackend = &APIBackend{ |
|
||||||
hmy: hmy, |
|
||||||
TotalStakingCache: struct { |
|
||||||
sync.Mutex |
|
||||||
BlockHeight int64 |
|
||||||
TotalStaking *big.Int |
|
||||||
}{ |
|
||||||
BlockHeight: -1, |
|
||||||
TotalStaking: big.NewInt(0), |
|
||||||
}, |
|
||||||
LeaderCache: cache, |
|
||||||
} |
|
||||||
return hmy, nil |
|
||||||
} |
|
||||||
|
|
||||||
// TxPool ...
|
|
||||||
func (s *Harmony) TxPool() *core.TxPool { return s.txPool } |
|
||||||
|
|
||||||
// CxPool is used to store the blockHashes, where the corresponding block contains the cross shard receipts to be sent
|
|
||||||
func (s *Harmony) CxPool() *core.CxPool { return s.cxPool } |
|
||||||
|
|
||||||
// BlockChain ...
|
|
||||||
func (s *Harmony) BlockChain() *core.BlockChain { return s.blockchain } |
|
||||||
|
|
||||||
//BeaconChain ...
|
|
||||||
func (s *Harmony) BeaconChain() *core.BlockChain { return s.beaconchain } |
|
||||||
|
|
||||||
// NetVersion returns the network version, i.e. network ID identifying which network we are using
|
|
||||||
func (s *Harmony) NetVersion() uint64 { return s.networkID } |
|
@ -0,0 +1,244 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/harmony/block" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/state" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/crypto/bls" |
||||||
|
internal_bls "github.com/harmony-one/harmony/crypto/bls" |
||||||
|
internal_common "github.com/harmony-one/harmony/internal/common" |
||||||
|
"github.com/harmony-one/harmony/internal/params" |
||||||
|
"github.com/harmony-one/harmony/internal/utils" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
"github.com/pkg/errors" |
||||||
|
) |
||||||
|
|
||||||
|
// ChainConfig ...
|
||||||
|
func (hmy *Harmony) ChainConfig() *params.ChainConfig { |
||||||
|
return hmy.BlockChain.Config() |
||||||
|
} |
||||||
|
|
||||||
|
// GetShardState ...
|
||||||
|
func (hmy *Harmony) GetShardState() (*shard.State, error) { |
||||||
|
return hmy.BlockChain.ReadShardState(hmy.BlockChain.CurrentHeader().Epoch()) |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlockSigners ..
|
||||||
|
func (hmy *Harmony) GetBlockSigners( |
||||||
|
ctx context.Context, blockNr rpc.BlockNumber, |
||||||
|
) (shard.SlotList, *internal_bls.Mask, error) { |
||||||
|
blk, err := hmy.BlockByNumber(ctx, blockNr) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
blockWithSigners, err := hmy.BlockByNumber(ctx, blockNr+1) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
committee, err := hmy.GetValidators(blk.Epoch()) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
pubKeys := make([]internal_bls.PublicKeyWrapper, len(committee.Slots)) |
||||||
|
for _, validator := range committee.Slots { |
||||||
|
wrapper := internal_bls.PublicKeyWrapper{Bytes: validator.BLSPublicKey} |
||||||
|
if wrapper.Object, err = bls.BytesToBLSPublicKey(wrapper.Bytes[:]); err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
mask, err := internal_bls.NewMask(pubKeys, nil) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
return committee.Slots, mask, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetLatestChainHeaders ..
|
||||||
|
func (hmy *Harmony) GetLatestChainHeaders() *block.HeaderPair { |
||||||
|
return &block.HeaderPair{ |
||||||
|
BeaconHeader: hmy.BeaconChain.CurrentHeader(), |
||||||
|
ShardHeader: hmy.BlockChain.CurrentHeader(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// GetLastCrossLinks ..
|
||||||
|
func (hmy *Harmony) GetLastCrossLinks() ([]*types.CrossLink, error) { |
||||||
|
crossLinks := []*types.CrossLink{} |
||||||
|
for i := uint32(1); i < shard.Schedule.InstanceForEpoch(hmy.CurrentBlock().Epoch()).NumShards(); i++ { |
||||||
|
link, err := hmy.BlockChain.ReadShardLastCrossLink(i) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
crossLinks = append(crossLinks, link) |
||||||
|
} |
||||||
|
|
||||||
|
return crossLinks, nil |
||||||
|
} |
||||||
|
|
||||||
|
// CurrentBlock ...
|
||||||
|
func (hmy *Harmony) CurrentBlock() *types.Block { |
||||||
|
return types.NewBlockWithHeader(hmy.BlockChain.CurrentHeader()) |
||||||
|
} |
||||||
|
|
||||||
|
// GetBlock ...
|
||||||
|
func (hmy *Harmony) GetBlock(ctx context.Context, hash common.Hash) (*types.Block, error) { |
||||||
|
return hmy.BlockChain.GetBlockByHash(hash), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetCurrentBadBlocks ..
|
||||||
|
func (hmy *Harmony) GetCurrentBadBlocks() []core.BadBlock { |
||||||
|
return hmy.BlockChain.BadBlocks() |
||||||
|
} |
||||||
|
|
||||||
|
// GetBalance returns balance of an given address.
|
||||||
|
func (hmy *Harmony) GetBalance(ctx context.Context, address common.Address, blockNum rpc.BlockNumber) (*big.Int, error) { |
||||||
|
s, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) |
||||||
|
if s == nil || err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return s.GetBalance(address), s.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// BlockByNumber ...
|
||||||
|
func (hmy *Harmony) BlockByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*types.Block, error) { |
||||||
|
// Pending block is only known by the miner
|
||||||
|
if blockNum == rpc.PendingBlockNumber { |
||||||
|
return nil, errors.New("not implemented") |
||||||
|
} |
||||||
|
// Otherwise resolve and return the block
|
||||||
|
if blockNum == rpc.LatestBlockNumber { |
||||||
|
return hmy.BlockChain.CurrentBlock(), nil |
||||||
|
} |
||||||
|
return hmy.BlockChain.GetBlockByNumber(uint64(blockNum)), nil |
||||||
|
} |
||||||
|
|
||||||
|
// HeaderByNumber ...
|
||||||
|
func (hmy *Harmony) HeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*block.Header, error) { |
||||||
|
// Pending block is only known by the miner
|
||||||
|
if blockNum == rpc.PendingBlockNumber { |
||||||
|
return nil, errors.New("not implemented") |
||||||
|
} |
||||||
|
// Otherwise resolve and return the block
|
||||||
|
if blockNum == rpc.LatestBlockNumber { |
||||||
|
return hmy.BlockChain.CurrentBlock().Header(), nil |
||||||
|
} |
||||||
|
return hmy.BlockChain.GetHeaderByNumber(uint64(blockNum)), nil |
||||||
|
} |
||||||
|
|
||||||
|
// HeaderByHash ...
|
||||||
|
func (hmy *Harmony) HeaderByHash(ctx context.Context, blockHash common.Hash) (*block.Header, error) { |
||||||
|
header := hmy.BlockChain.GetHeaderByHash(blockHash) |
||||||
|
if header == nil { |
||||||
|
return nil, errors.New("Header is not found") |
||||||
|
} |
||||||
|
return header, nil |
||||||
|
} |
||||||
|
|
||||||
|
// StateAndHeaderByNumber ...
|
||||||
|
func (hmy *Harmony) StateAndHeaderByNumber(ctx context.Context, blockNum rpc.BlockNumber) (*state.DB, *block.Header, error) { |
||||||
|
// Pending state is only known by the miner
|
||||||
|
if blockNum == rpc.PendingBlockNumber { |
||||||
|
return nil, nil, errors.New("not implemented") |
||||||
|
} |
||||||
|
// Otherwise resolve the block number and return its state
|
||||||
|
header, err := hmy.HeaderByNumber(ctx, blockNum) |
||||||
|
if header == nil || err != nil { |
||||||
|
return nil, nil, err |
||||||
|
} |
||||||
|
stateDb, err := hmy.BlockChain.StateAt(header.Root()) |
||||||
|
return stateDb, header, err |
||||||
|
} |
||||||
|
|
||||||
|
// GetLeaderAddress returns the one address of the leader, given the coinbaseAddr.
|
||||||
|
// Note that the coinbaseAddr is overloaded with the BLS pub key hash in staking era.
|
||||||
|
func (hmy *Harmony) GetLeaderAddress(coinbaseAddr common.Address, epoch *big.Int) string { |
||||||
|
if hmy.IsStakingEpoch(epoch) { |
||||||
|
if leader, exists := hmy.leaderCache.Get(coinbaseAddr); exists { |
||||||
|
bech32, _ := internal_common.AddressToBech32(leader.(common.Address)) |
||||||
|
return bech32 |
||||||
|
} |
||||||
|
committee, err := hmy.GetValidators(epoch) |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
for _, val := range committee.Slots { |
||||||
|
addr := utils.GetAddressFromBLSPubKeyBytes(val.BLSPublicKey[:]) |
||||||
|
hmy.leaderCache.Add(addr, val.EcdsaAddress) |
||||||
|
if addr == coinbaseAddr { |
||||||
|
bech32, _ := internal_common.AddressToBech32(val.EcdsaAddress) |
||||||
|
return bech32 |
||||||
|
} |
||||||
|
} |
||||||
|
return "" // Did not find matching address
|
||||||
|
} |
||||||
|
bech32, _ := internal_common.AddressToBech32(coinbaseAddr) |
||||||
|
return bech32 |
||||||
|
} |
||||||
|
|
||||||
|
// Filter related APIs
|
||||||
|
|
||||||
|
// GetLogs ...
|
||||||
|
func (hmy *Harmony) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) { |
||||||
|
receipts := hmy.BlockChain.GetReceiptsByHash(blockHash) |
||||||
|
if receipts == nil { |
||||||
|
return nil, errors.New("Missing receipts") |
||||||
|
} |
||||||
|
logs := make([][]*types.Log, len(receipts)) |
||||||
|
for i, receipt := range receipts { |
||||||
|
logs[i] = receipt.Logs |
||||||
|
} |
||||||
|
return logs, nil |
||||||
|
} |
||||||
|
|
||||||
|
// ServiceFilter ...
|
||||||
|
func (hmy *Harmony) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { |
||||||
|
// TODO(dm): implement
|
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeNewTxsEvent subscribes new tx event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { |
||||||
|
return hmy.TxPool.SubscribeNewTxsEvent(ch) |
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeChainEvent subscribes chain event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { |
||||||
|
return hmy.BlockChain.SubscribeChainEvent(ch) |
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeChainHeadEvent subcribes chain head event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { |
||||||
|
return hmy.BlockChain.SubscribeChainHeadEvent(ch) |
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeChainSideEvent subcribes chain side event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { |
||||||
|
return hmy.BlockChain.SubscribeChainSideEvent(ch) |
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeRemovedLogsEvent subcribes removed logs event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { |
||||||
|
return hmy.BlockChain.SubscribeRemovedLogsEvent(ch) |
||||||
|
} |
||||||
|
|
||||||
|
// SubscribeLogsEvent subcribes log event.
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { |
||||||
|
return hmy.BlockChain.SubscribeLogsEvent(ch) |
||||||
|
} |
@ -0,0 +1,202 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/math" |
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
"github.com/harmony-one/harmony/api/proto" |
||||||
|
"github.com/harmony-one/harmony/block" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/state" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
"github.com/harmony-one/harmony/core/vm" |
||||||
|
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||||
|
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
lru "github.com/hashicorp/golang-lru" |
||||||
|
"github.com/libp2p/go-libp2p-core/peer" |
||||||
|
"github.com/pkg/errors" |
||||||
|
"golang.org/x/sync/singleflight" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// BloomBitsBlocks is the number of blocks a single bloom bit section vector
|
||||||
|
// contains on the server side.
|
||||||
|
BloomBitsBlocks uint64 = 4096 |
||||||
|
leaderCacheSize = 250 // Approx number of BLS keys in committee
|
||||||
|
totalStakeCacheDuration = 20 // number of blocks where the returned total stake will remain the same
|
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrFinalizedTransaction is returned if the transaction to be submitted is already on-chain
|
||||||
|
ErrFinalizedTransaction = errors.New("transaction already finalized") |
||||||
|
) |
||||||
|
|
||||||
|
// Harmony implements the Harmony full node service.
|
||||||
|
type Harmony struct { |
||||||
|
// Channel for shutting down the service
|
||||||
|
ShutdownChan chan bool // Channel for shutting down the Harmony
|
||||||
|
BloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
|
||||||
|
BlockChain *core.BlockChain |
||||||
|
BeaconChain *core.BlockChain |
||||||
|
TxPool *core.TxPool |
||||||
|
CxPool *core.CxPool // CxPool is used to store the blockHashes of blocks containing cx receipts to be sent
|
||||||
|
// DB interfaces
|
||||||
|
BloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
|
||||||
|
NodeAPI NodeAPI |
||||||
|
// ChainID is used to identify which network we are using
|
||||||
|
ChainID uint64 |
||||||
|
// RPCGasCap is the global gas cap for eth-call variants.
|
||||||
|
RPCGasCap *big.Int `toml:",omitempty"` |
||||||
|
ShardID uint32 |
||||||
|
|
||||||
|
// Internals
|
||||||
|
eventMux *event.TypeMux |
||||||
|
chainDb ethdb.Database // Block chain database
|
||||||
|
// group for units of work which can be executed with duplicate suppression.
|
||||||
|
group singleflight.Group |
||||||
|
// leaderCache to save on recomputation every epoch.
|
||||||
|
leaderCache *lru.Cache |
||||||
|
// totalStakeCache to save on recomputation for `totalStakeCacheDuration` blocks.
|
||||||
|
totalStakeCache *totalStakeCache |
||||||
|
} |
||||||
|
|
||||||
|
// NodeAPI is the list of functions from node used to call rpc apis.
|
||||||
|
type NodeAPI interface { |
||||||
|
AddPendingStakingTransaction(*staking.StakingTransaction) error |
||||||
|
AddPendingTransaction(newTx *types.Transaction) error |
||||||
|
Blockchain() *core.BlockChain |
||||||
|
Beaconchain() *core.BlockChain |
||||||
|
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
||||||
|
GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
||||||
|
GetTransactionsCount(address, txType string) (uint64, error) |
||||||
|
GetStakingTransactionsCount(address, txType string) (uint64, error) |
||||||
|
IsCurrentlyLeader() bool |
||||||
|
ReportStakingErrorSink() types.TransactionErrorReports |
||||||
|
ReportPlainErrorSink() types.TransactionErrorReports |
||||||
|
PendingCXReceipts() []*types.CXReceiptsProof |
||||||
|
GetNodeBootTime() int64 |
||||||
|
PeerConnectivity() (int, int, int) |
||||||
|
ListPeer(topic string) []peer.ID |
||||||
|
ListTopic() []string |
||||||
|
ListBlockedPeer() []peer.ID |
||||||
|
} |
||||||
|
|
||||||
|
// New creates a new Harmony object (including the
|
||||||
|
// initialisation of the common Harmony object)
|
||||||
|
func New( |
||||||
|
nodeAPI NodeAPI, txPool *core.TxPool, cxPool *core.CxPool, shardID uint32, |
||||||
|
) *Harmony { |
||||||
|
chainDb := nodeAPI.Blockchain().ChainDB() |
||||||
|
leaderCache, _ := lru.New(leaderCacheSize) |
||||||
|
totalStakeCache := newTotalStakeCache(totalStakeCacheDuration) |
||||||
|
return &Harmony{ |
||||||
|
ShutdownChan: make(chan bool), |
||||||
|
BloomRequests: make(chan chan *bloombits.Retrieval), |
||||||
|
BlockChain: nodeAPI.Blockchain(), |
||||||
|
BeaconChain: nodeAPI.Beaconchain(), |
||||||
|
TxPool: txPool, |
||||||
|
CxPool: cxPool, |
||||||
|
eventMux: new(event.TypeMux), |
||||||
|
chainDb: chainDb, |
||||||
|
NodeAPI: nodeAPI, |
||||||
|
ChainID: nodeAPI.Blockchain().Config().ChainID.Uint64(), |
||||||
|
ShardID: shardID, |
||||||
|
leaderCache: leaderCache, |
||||||
|
totalStakeCache: totalStakeCache, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// SingleFlightRequest ..
|
||||||
|
func (hmy *Harmony) SingleFlightRequest( |
||||||
|
key string, |
||||||
|
fn func() (interface{}, error), |
||||||
|
) (interface{}, error) { |
||||||
|
res, err, _ := hmy.group.Do(key, fn) |
||||||
|
return res, err |
||||||
|
} |
||||||
|
|
||||||
|
// SingleFlightForgetKey ...
|
||||||
|
func (hmy *Harmony) SingleFlightForgetKey(key string) { |
||||||
|
hmy.group.Forget(key) |
||||||
|
} |
||||||
|
|
||||||
|
// ProtocolVersion ...
|
||||||
|
func (hmy *Harmony) ProtocolVersion() int { |
||||||
|
return proto.ProtocolVersion |
||||||
|
} |
||||||
|
|
||||||
|
// IsLeader exposes if node is currently leader
|
||||||
|
func (hmy *Harmony) IsLeader() bool { |
||||||
|
return hmy.NodeAPI.IsCurrentlyLeader() |
||||||
|
} |
||||||
|
|
||||||
|
// GetNodeMetadata ..
|
||||||
|
func (hmy *Harmony) GetNodeMetadata() commonRPC.NodeMetadata { |
||||||
|
cfg := nodeconfig.GetDefaultConfig() |
||||||
|
header := hmy.CurrentBlock().Header() |
||||||
|
var blockEpoch *uint64 |
||||||
|
|
||||||
|
if header.ShardID() == shard.BeaconChainShardID { |
||||||
|
sched := shard.Schedule.InstanceForEpoch(header.Epoch()) |
||||||
|
b := sched.BlocksPerEpoch() |
||||||
|
blockEpoch = &b |
||||||
|
} |
||||||
|
|
||||||
|
blsKeys := []string{} |
||||||
|
if cfg.ConsensusPriKey != nil { |
||||||
|
for _, key := range cfg.ConsensusPriKey { |
||||||
|
blsKeys = append(blsKeys, key.Pub.Bytes.Hex()) |
||||||
|
} |
||||||
|
} |
||||||
|
c := commonRPC.C{} |
||||||
|
c.TotalKnownPeers, c.Connected, c.NotConnected = hmy.NodeAPI.PeerConnectivity() |
||||||
|
|
||||||
|
return commonRPC.NodeMetadata{ |
||||||
|
BLSPublicKey: blsKeys, |
||||||
|
Version: nodeconfig.GetVersion(), |
||||||
|
NetworkType: string(cfg.GetNetworkType()), |
||||||
|
ChainConfig: *hmy.ChainConfig(), |
||||||
|
IsLeader: hmy.IsLeader(), |
||||||
|
ShardID: hmy.ShardID, |
||||||
|
CurrentEpoch: header.Epoch().Uint64(), |
||||||
|
BlocksPerEpoch: blockEpoch, |
||||||
|
Role: cfg.Role().String(), |
||||||
|
DNSZone: cfg.DNSZone, |
||||||
|
Archival: cfg.GetArchival(), |
||||||
|
NodeBootTime: hmy.NodeAPI.GetNodeBootTime(), |
||||||
|
PeerID: nodeconfig.GetPeerID(), |
||||||
|
C: c, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// GetEVM returns a new EVM entity
|
||||||
|
func (hmy *Harmony) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) { |
||||||
|
state.SetBalance(msg.From(), math.MaxBig256) |
||||||
|
vmError := func() error { return nil } |
||||||
|
vmCtx := core.NewEVMContext(msg, header, hmy.BlockChain, nil) |
||||||
|
return vm.NewEVM(vmCtx, state, hmy.BlockChain.Config(), *hmy.BlockChain.GetVMConfig()), vmError, nil |
||||||
|
} |
||||||
|
|
||||||
|
// ChainDb ..
|
||||||
|
func (hmy *Harmony) ChainDb() ethdb.Database { |
||||||
|
return hmy.chainDb |
||||||
|
} |
||||||
|
|
||||||
|
// EventMux ..
|
||||||
|
func (hmy *Harmony) EventMux() *event.TypeMux { |
||||||
|
return hmy.eventMux |
||||||
|
} |
||||||
|
|
||||||
|
// BloomStatus ...
|
||||||
|
// TODO: this is not implemented or verified yet for harmony.
|
||||||
|
func (hmy *Harmony) BloomStatus() (uint64, uint64) { |
||||||
|
sections, _, _ := hmy.BloomIndexer.Sections() |
||||||
|
return BloomBitsBlocks, sections |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||||
|
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
||||||
|
"github.com/harmony-one/harmony/staking/network" |
||||||
|
"github.com/libp2p/go-libp2p-core/peer" |
||||||
|
) |
||||||
|
|
||||||
|
// GetCurrentUtilityMetrics ..
|
||||||
|
func (hmy *Harmony) GetCurrentUtilityMetrics() (*network.UtilityMetric, error) { |
||||||
|
return network.NewUtilityMetricSnapshot(hmy.BlockChain) |
||||||
|
} |
||||||
|
|
||||||
|
// GetPeerInfo returns the peer info to the node, including blocked peer, connected peer, number of peers
|
||||||
|
func (hmy *Harmony) GetPeerInfo() commonRPC.NodePeerInfo { |
||||||
|
|
||||||
|
topics := hmy.NodeAPI.ListTopic() |
||||||
|
p := make([]commonRPC.P, len(topics)) |
||||||
|
|
||||||
|
for i, t := range topics { |
||||||
|
topicPeer := hmy.NodeAPI.ListPeer(t) |
||||||
|
p[i].Topic = t |
||||||
|
p[i].Peers = make([]peer.ID, len(topicPeer)) |
||||||
|
copy(p[i].Peers, topicPeer) |
||||||
|
} |
||||||
|
|
||||||
|
return commonRPC.NodePeerInfo{ |
||||||
|
PeerID: nodeconfig.GetPeerID(), |
||||||
|
BlockedPeers: hmy.NodeAPI.ListBlockedPeer(), |
||||||
|
P: p, |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// GetPoolStats returns the number of pending and queued transactions
|
||||||
|
func (hmy *Harmony) GetPoolStats() (pendingCount, queuedCount int) { |
||||||
|
return hmy.TxPool.Stats() |
||||||
|
} |
||||||
|
|
||||||
|
// GetPoolNonce ...
|
||||||
|
func (hmy *Harmony) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { |
||||||
|
return hmy.TxPool.State().GetNonce(addr), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetPoolTransaction ...
|
||||||
|
func (hmy *Harmony) GetPoolTransaction(hash common.Hash) types.PoolTransaction { |
||||||
|
return hmy.TxPool.Get(hash) |
||||||
|
} |
||||||
|
|
||||||
|
// GetPendingCXReceipts ..
|
||||||
|
func (hmy *Harmony) GetPendingCXReceipts() []*types.CXReceiptsProof { |
||||||
|
return hmy.NodeAPI.PendingCXReceipts() |
||||||
|
} |
||||||
|
|
||||||
|
// GetPoolTransactions returns pool transactions.
|
||||||
|
func (hmy *Harmony) GetPoolTransactions() (types.PoolTransactions, error) { |
||||||
|
pending, err := hmy.TxPool.Pending() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
queued, err := hmy.TxPool.Queued() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
var txs types.PoolTransactions |
||||||
|
for _, batch := range pending { |
||||||
|
txs = append(txs, batch...) |
||||||
|
} |
||||||
|
for _, batch := range queued { |
||||||
|
txs = append(txs, batch...) |
||||||
|
} |
||||||
|
return txs, nil |
||||||
|
} |
@ -0,0 +1,476 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/harmony-one/harmony/consensus/quorum" |
||||||
|
"github.com/harmony-one/harmony/core/rawdb" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
internal_common "github.com/harmony-one/harmony/internal/common" |
||||||
|
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
||||||
|
"github.com/harmony-one/harmony/numeric" |
||||||
|
"github.com/harmony-one/harmony/shard" |
||||||
|
"github.com/harmony-one/harmony/shard/committee" |
||||||
|
"github.com/harmony-one/harmony/staking/availability" |
||||||
|
"github.com/harmony-one/harmony/staking/effective" |
||||||
|
staking "github.com/harmony-one/harmony/staking/types" |
||||||
|
"github.com/pkg/errors" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
zero = numeric.ZeroDec() |
||||||
|
) |
||||||
|
|
||||||
|
func (hmy *Harmony) readAndUpdateRawStakes( |
||||||
|
epoch *big.Int, |
||||||
|
decider quorum.Decider, |
||||||
|
comm shard.Committee, |
||||||
|
rawStakes []effective.SlotPurchase, |
||||||
|
validatorSpreads map[common.Address]numeric.Dec, |
||||||
|
) []effective.SlotPurchase { |
||||||
|
for i := range comm.Slots { |
||||||
|
slot := comm.Slots[i] |
||||||
|
slotAddr := slot.EcdsaAddress |
||||||
|
slotKey := slot.BLSPublicKey |
||||||
|
spread, ok := validatorSpreads[slotAddr] |
||||||
|
if !ok { |
||||||
|
snapshot, err := hmy.BlockChain.ReadValidatorSnapshotAtEpoch(epoch, slotAddr) |
||||||
|
if err != nil { |
||||||
|
continue |
||||||
|
} |
||||||
|
wrapper := snapshot.Validator |
||||||
|
spread = numeric.NewDecFromBigInt(wrapper.TotalDelegation()). |
||||||
|
QuoInt64(int64(len(wrapper.SlotPubKeys))) |
||||||
|
validatorSpreads[slotAddr] = spread |
||||||
|
} |
||||||
|
|
||||||
|
commonRPC.SetRawStake(decider, slotKey, spread) |
||||||
|
// add entry to array for median calculation
|
||||||
|
rawStakes = append(rawStakes, effective.SlotPurchase{ |
||||||
|
Addr: slotAddr, |
||||||
|
Key: slotKey, |
||||||
|
RawStake: spread, |
||||||
|
EPoSStake: spread, |
||||||
|
}) |
||||||
|
} |
||||||
|
return rawStakes |
||||||
|
} |
||||||
|
|
||||||
|
func (hmy *Harmony) getSuperCommittees() (*quorum.Transition, error) { |
||||||
|
nowE := hmy.BlockChain.CurrentHeader().Epoch() |
||||||
|
thenE := new(big.Int).Sub(nowE, common.Big1) |
||||||
|
|
||||||
|
var ( |
||||||
|
nowCommittee, prevCommittee *shard.State |
||||||
|
err error |
||||||
|
) |
||||||
|
nowCommittee, err = hmy.BlockChain.ReadShardState(nowE) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
prevCommittee, err = hmy.BlockChain.ReadShardState(thenE) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
stakedSlotsNow, stakedSlotsThen := |
||||||
|
shard.ExternalSlotsAvailableForEpoch(nowE), |
||||||
|
shard.ExternalSlotsAvailableForEpoch(thenE) |
||||||
|
|
||||||
|
then, now := |
||||||
|
quorum.NewRegistry(stakedSlotsThen), |
||||||
|
quorum.NewRegistry(stakedSlotsNow) |
||||||
|
|
||||||
|
rawStakes := []effective.SlotPurchase{} |
||||||
|
validatorSpreads := map[common.Address]numeric.Dec{} |
||||||
|
for _, comm := range prevCommittee.Shards { |
||||||
|
decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) |
||||||
|
// before staking skip computing
|
||||||
|
if hmy.BlockChain.Config().IsStaking(prevCommittee.Epoch) { |
||||||
|
if _, err := decider.SetVoters(&comm, prevCommittee.Epoch); err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
} |
||||||
|
rawStakes = hmy.readAndUpdateRawStakes(thenE, decider, comm, rawStakes, validatorSpreads) |
||||||
|
then.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider |
||||||
|
} |
||||||
|
then.MedianStake = effective.Median(rawStakes) |
||||||
|
|
||||||
|
rawStakes = []effective.SlotPurchase{} |
||||||
|
validatorSpreads = map[common.Address]numeric.Dec{} |
||||||
|
for _, comm := range nowCommittee.Shards { |
||||||
|
decider := quorum.NewDecider(quorum.SuperMajorityStake, comm.ShardID) |
||||||
|
if _, err := decider.SetVoters(&comm, nowCommittee.Epoch); err != nil { |
||||||
|
return nil, errors.Wrapf( |
||||||
|
err, |
||||||
|
"committee is only available from staking epoch: %v, current epoch: %v", |
||||||
|
hmy.BlockChain.Config().StakingEpoch, |
||||||
|
hmy.BlockChain.CurrentHeader().Epoch(), |
||||||
|
) |
||||||
|
} |
||||||
|
rawStakes = hmy.readAndUpdateRawStakes(nowE, decider, comm, rawStakes, validatorSpreads) |
||||||
|
now.Deciders[fmt.Sprintf("shard-%d", comm.ShardID)] = decider |
||||||
|
} |
||||||
|
now.MedianStake = effective.Median(rawStakes) |
||||||
|
|
||||||
|
return &quorum.Transition{Previous: then, Current: now}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsStakingEpoch ...
|
||||||
|
func (hmy *Harmony) IsStakingEpoch(epoch *big.Int) bool { |
||||||
|
return hmy.BlockChain.Config().IsStaking(epoch) |
||||||
|
} |
||||||
|
|
||||||
|
// SendStakingTx adds a staking transaction
|
||||||
|
func (hmy *Harmony) SendStakingTx(ctx context.Context, signedStakingTx *staking.StakingTransaction) error { |
||||||
|
stx, _, _, _ := rawdb.ReadStakingTransaction(hmy.chainDb, signedStakingTx.Hash()) |
||||||
|
if stx == nil { |
||||||
|
return hmy.NodeAPI.AddPendingStakingTransaction(signedStakingTx) |
||||||
|
} |
||||||
|
return ErrFinalizedTransaction |
||||||
|
} |
||||||
|
|
||||||
|
// GetStakingTransactionsHistory returns list of staking transactions hashes of address.
|
||||||
|
func (hmy *Harmony) GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) { |
||||||
|
return hmy.NodeAPI.GetStakingTransactionsHistory(address, txType, order) |
||||||
|
} |
||||||
|
|
||||||
|
// GetStakingTransactionsCount returns the number of staking transactions of address.
|
||||||
|
func (hmy *Harmony) GetStakingTransactionsCount(address, txType string) (uint64, error) { |
||||||
|
return hmy.NodeAPI.GetStakingTransactionsCount(address, txType) |
||||||
|
} |
||||||
|
|
||||||
|
// GetSuperCommittees ..
|
||||||
|
func (hmy *Harmony) GetSuperCommittees() (*quorum.Transition, error) { |
||||||
|
nowE := hmy.BlockChain.CurrentHeader().Epoch() |
||||||
|
key := fmt.Sprintf("sc-%s", nowE.String()) |
||||||
|
|
||||||
|
res, err := hmy.SingleFlightRequest( |
||||||
|
key, func() (interface{}, error) { |
||||||
|
thenE := new(big.Int).Sub(nowE, common.Big1) |
||||||
|
thenKey := fmt.Sprintf("sc-%s", thenE.String()) |
||||||
|
hmy.group.Forget(thenKey) |
||||||
|
return hmy.getSuperCommittees() |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return res.(*quorum.Transition), err |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidators returns validators for a particular epoch.
|
||||||
|
func (hmy *Harmony) GetValidators(epoch *big.Int) (*shard.Committee, error) { |
||||||
|
state, err := hmy.BlockChain.ReadShardState(epoch) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
for _, cmt := range state.Shards { |
||||||
|
if cmt.ShardID == hmy.ShardID { |
||||||
|
return &cmt, nil |
||||||
|
} |
||||||
|
} |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorSelfDelegation returns the amount of staking after applying all delegated stakes
|
||||||
|
func (hmy *Harmony) GetValidatorSelfDelegation(addr common.Address) *big.Int { |
||||||
|
wrapper, err := hmy.BlockChain.ReadValidatorInformation(addr) |
||||||
|
if err != nil || wrapper == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if len(wrapper.Delegations) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return wrapper.Delegations[0].Amount |
||||||
|
} |
||||||
|
|
||||||
|
// GetElectedValidatorAddresses returns the address of elected validators for current epoch
|
||||||
|
func (hmy *Harmony) GetElectedValidatorAddresses() []common.Address { |
||||||
|
list, _ := hmy.BlockChain.ReadShardState(hmy.BlockChain.CurrentBlock().Epoch()) |
||||||
|
return list.StakedValidators().Addrs |
||||||
|
} |
||||||
|
|
||||||
|
// GetAllValidatorAddresses returns the up to date validator candidates for next epoch
|
||||||
|
func (hmy *Harmony) GetAllValidatorAddresses() []common.Address { |
||||||
|
return hmy.BlockChain.ValidatorCandidates() |
||||||
|
} |
||||||
|
|
||||||
|
// GetValidatorInformation returns the information of validator
|
||||||
|
func (hmy *Harmony) GetValidatorInformation( |
||||||
|
addr common.Address, block *types.Block, |
||||||
|
) (*staking.ValidatorRPCEnhanced, error) { |
||||||
|
bc := hmy.BlockChain |
||||||
|
wrapper, err := bc.ReadValidatorInformationAt(addr, block.Root()) |
||||||
|
if err != nil { |
||||||
|
s, _ := internal_common.AddressToBech32(addr) |
||||||
|
return nil, errors.Wrapf(err, "not found address in current state %s", s) |
||||||
|
} |
||||||
|
|
||||||
|
now := block.Epoch() |
||||||
|
// At the last block of epoch, block epoch is e while val.LastEpochInCommittee
|
||||||
|
// is already updated to e+1. So need the >= check rather than ==
|
||||||
|
inCommittee := wrapper.LastEpochInCommittee.Cmp(now) >= 0 |
||||||
|
defaultReply := &staking.ValidatorRPCEnhanced{ |
||||||
|
CurrentlyInCommittee: inCommittee, |
||||||
|
Wrapper: *wrapper, |
||||||
|
Performance: nil, |
||||||
|
ComputedMetrics: nil, |
||||||
|
TotalDelegated: wrapper.TotalDelegation(), |
||||||
|
EPoSStatus: effective.ValidatorStatus( |
||||||
|
inCommittee, wrapper.Status, |
||||||
|
).String(), |
||||||
|
EPoSWinningStake: nil, |
||||||
|
BootedStatus: nil, |
||||||
|
ActiveStatus: wrapper.Validator.Status.String(), |
||||||
|
Lifetime: &staking.AccumulatedOverLifetime{ |
||||||
|
BlockReward: wrapper.BlockReward, |
||||||
|
Signing: wrapper.Counters, |
||||||
|
APR: zero, |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
snapshot, err := bc.ReadValidatorSnapshotAtEpoch( |
||||||
|
now, addr, |
||||||
|
) |
||||||
|
|
||||||
|
if err != nil { |
||||||
|
return defaultReply, nil |
||||||
|
} |
||||||
|
|
||||||
|
computed := availability.ComputeCurrentSigning( |
||||||
|
snapshot.Validator, wrapper, |
||||||
|
) |
||||||
|
beaconChainBlocks := uint64( |
||||||
|
hmy.BeaconChain.CurrentBlock().Header().Number().Int64(), |
||||||
|
) % shard.Schedule.BlocksPerEpoch() |
||||||
|
computed.BlocksLeftInEpoch = shard.Schedule.BlocksPerEpoch() - beaconChainBlocks |
||||||
|
|
||||||
|
if defaultReply.CurrentlyInCommittee { |
||||||
|
defaultReply.Performance = &staking.CurrentEpochPerformance{ |
||||||
|
CurrentSigningPercentage: *computed, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
stats, err := bc.ReadValidatorStats(addr) |
||||||
|
if err != nil { |
||||||
|
// when validator has no stats, default boot-status to not booted
|
||||||
|
notBooted := effective.NotBooted.String() |
||||||
|
defaultReply.BootedStatus = ¬Booted |
||||||
|
return defaultReply, nil |
||||||
|
} |
||||||
|
|
||||||
|
latestAPR := numeric.ZeroDec() |
||||||
|
l := len(stats.APRs) |
||||||
|
if l > 0 { |
||||||
|
latestAPR = stats.APRs[l-1].Value |
||||||
|
} |
||||||
|
defaultReply.Lifetime.APR = latestAPR |
||||||
|
defaultReply.Lifetime.EpochAPRs = stats.APRs |
||||||
|
|
||||||
|
// average apr cache keys
|
||||||
|
// key := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64())
|
||||||
|
// prevKey := fmt.Sprintf("apr-%s-%d", addr.Hex(), now.Uint64()-1)
|
||||||
|
|
||||||
|
// delete entry for previous epoch
|
||||||
|
// b.apiCache.Forget(prevKey)
|
||||||
|
|
||||||
|
// calculate last APRHistoryLength epochs for averaging APR
|
||||||
|
// epochFrom := bc.Config().StakingEpoch
|
||||||
|
// nowMinus := big.NewInt(0).Sub(now, big.NewInt(staking.APRHistoryLength))
|
||||||
|
// if nowMinus.Cmp(epochFrom) > 0 {
|
||||||
|
// epochFrom = nowMinus
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if len(stats.APRs) > 0 && stats.APRs[0].Epoch.Cmp(epochFrom) > 0 {
|
||||||
|
// epochFrom = stats.APRs[0].Epoch
|
||||||
|
// }
|
||||||
|
|
||||||
|
// epochToAPRs := map[int64]numeric.Dec{}
|
||||||
|
// for i := 0; i < len(stats.APRs); i++ {
|
||||||
|
// entry := stats.APRs[i]
|
||||||
|
// epochToAPRs[entry.Epoch.Int64()] = entry.Value
|
||||||
|
// }
|
||||||
|
|
||||||
|
// at this point, validator is active and has apr's for the recent 100 epochs
|
||||||
|
// compute average apr over history
|
||||||
|
// if avgAPR, err := b.SingleFlightRequest(
|
||||||
|
// key, func() (interface{}, error) {
|
||||||
|
// total := numeric.ZeroDec()
|
||||||
|
// count := 0
|
||||||
|
// for i := epochFrom.Int64(); i < now.Int64(); i++ {
|
||||||
|
// if apr, ok := epochToAPRs[i]; ok {
|
||||||
|
// total = total.Add(apr)
|
||||||
|
// }
|
||||||
|
// count++
|
||||||
|
// }
|
||||||
|
// if count == 0 {
|
||||||
|
// return nil, errors.New("no apr snapshots available")
|
||||||
|
// }
|
||||||
|
// return total.QuoInt64(int64(count)), nil
|
||||||
|
// },
|
||||||
|
// ); err != nil {
|
||||||
|
// // could not compute average apr from snapshot
|
||||||
|
// // assign the latest apr available from stats
|
||||||
|
// defaultReply.Lifetime.APR = numeric.ZeroDec()
|
||||||
|
// } else {
|
||||||
|
// defaultReply.Lifetime.APR = avgAPR.(numeric.Dec)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if defaultReply.CurrentlyInCommittee { |
||||||
|
defaultReply.ComputedMetrics = stats |
||||||
|
defaultReply.EPoSWinningStake = &stats.TotalEffectiveStake |
||||||
|
} |
||||||
|
|
||||||
|
if !defaultReply.CurrentlyInCommittee { |
||||||
|
reason := stats.BootedStatus.String() |
||||||
|
defaultReply.BootedStatus = &reason |
||||||
|
} |
||||||
|
|
||||||
|
return defaultReply, nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetMedianRawStakeSnapshot ..
|
||||||
|
func (hmy *Harmony) GetMedianRawStakeSnapshot() ( |
||||||
|
*committee.CompletedEPoSRound, error, |
||||||
|
) { |
||||||
|
blockNum := hmy.CurrentBlock().NumberU64() |
||||||
|
key := fmt.Sprintf("median-%d", blockNum) |
||||||
|
|
||||||
|
// delete cache for previous block
|
||||||
|
prevKey := fmt.Sprintf("median-%d", blockNum-1) |
||||||
|
hmy.group.Forget(prevKey) |
||||||
|
|
||||||
|
res, err := hmy.SingleFlightRequest( |
||||||
|
key, |
||||||
|
func() (interface{}, error) { |
||||||
|
// Compute for next epoch
|
||||||
|
epoch := big.NewInt(0).Add(hmy.CurrentBlock().Epoch(), big.NewInt(1)) |
||||||
|
return committee.NewEPoSRound(epoch, hmy.BlockChain) |
||||||
|
}, |
||||||
|
) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return res.(*committee.CompletedEPoSRound), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationsByValidator returns all delegation information of a validator
|
||||||
|
func (hmy *Harmony) GetDelegationsByValidator(validator common.Address) []*staking.Delegation { |
||||||
|
wrapper, err := hmy.BlockChain.ReadValidatorInformation(validator) |
||||||
|
if err != nil || wrapper == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
delegations := []*staking.Delegation{} |
||||||
|
for i := range wrapper.Delegations { |
||||||
|
delegations = append(delegations, &wrapper.Delegations[i]) |
||||||
|
} |
||||||
|
return delegations |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationsByDelegator returns all delegation information of a delegator
|
||||||
|
func (hmy *Harmony) GetDelegationsByDelegator( |
||||||
|
delegator common.Address, |
||||||
|
) ([]common.Address, []*staking.Delegation) { |
||||||
|
block := hmy.BlockChain.CurrentBlock() |
||||||
|
return hmy.GetDelegationsByDelegatorByBlock(delegator, block) |
||||||
|
} |
||||||
|
|
||||||
|
// GetDelegationsByDelegatorByBlock returns all delegation information of a delegator
|
||||||
|
func (hmy *Harmony) GetDelegationsByDelegatorByBlock( |
||||||
|
delegator common.Address, block *types.Block, |
||||||
|
) ([]common.Address, []*staking.Delegation) { |
||||||
|
addresses := []common.Address{} |
||||||
|
delegations := []*staking.Delegation{} |
||||||
|
delegationIndexes, err := hmy.BlockChain. |
||||||
|
ReadDelegationsByDelegatorAt(delegator, block.Number()) |
||||||
|
if err != nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
for i := range delegationIndexes { |
||||||
|
wrapper, err := hmy.BlockChain.ReadValidatorInformationAt( |
||||||
|
delegationIndexes[i].ValidatorAddress, block.Root(), |
||||||
|
) |
||||||
|
if err != nil || wrapper == nil { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
if uint64(len(wrapper.Delegations)) > delegationIndexes[i].Index { |
||||||
|
delegations = append(delegations, &wrapper.Delegations[delegationIndexes[i].Index]) |
||||||
|
} else { |
||||||
|
delegations = append(delegations, nil) |
||||||
|
} |
||||||
|
addresses = append(addresses, delegationIndexes[i].ValidatorAddress) |
||||||
|
} |
||||||
|
return addresses, delegations |
||||||
|
} |
||||||
|
|
||||||
|
// GetTotalStakingSnapshot ..
|
||||||
|
func (hmy *Harmony) GetTotalStakingSnapshot() *big.Int { |
||||||
|
if stake := hmy.totalStakeCache.pop(hmy.CurrentBlock().NumberU64()); stake != nil { |
||||||
|
return stake |
||||||
|
} |
||||||
|
currHeight := hmy.CurrentBlock().NumberU64() |
||||||
|
candidates := hmy.BlockChain.ValidatorCandidates() |
||||||
|
if len(candidates) == 0 { |
||||||
|
stake := big.NewInt(0) |
||||||
|
hmy.totalStakeCache.push(currHeight, stake) |
||||||
|
return stake |
||||||
|
} |
||||||
|
stakes := big.NewInt(0) |
||||||
|
for i := range candidates { |
||||||
|
snapshot, _ := hmy.BlockChain.ReadValidatorSnapshot(candidates[i]) |
||||||
|
validator, _ := hmy.BlockChain.ReadValidatorInformation(candidates[i]) |
||||||
|
if !committee.IsEligibleForEPoSAuction( |
||||||
|
snapshot, validator, |
||||||
|
) { |
||||||
|
continue |
||||||
|
} |
||||||
|
for i := range validator.Delegations { |
||||||
|
stakes.Add(stakes, validator.Delegations[i].Amount) |
||||||
|
} |
||||||
|
} |
||||||
|
hmy.totalStakeCache.push(currHeight, stakes) |
||||||
|
return stakes |
||||||
|
} |
||||||
|
|
||||||
|
// GetCurrentStakingErrorSink ..
|
||||||
|
func (hmy *Harmony) GetCurrentStakingErrorSink() types.TransactionErrorReports { |
||||||
|
return hmy.NodeAPI.ReportStakingErrorSink() |
||||||
|
} |
||||||
|
|
||||||
|
// totalStakeCache ..
|
||||||
|
type totalStakeCache struct { |
||||||
|
sync.Mutex |
||||||
|
cachedBlockHeight uint64 |
||||||
|
stake *big.Int |
||||||
|
// duration is in blocks
|
||||||
|
duration uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// newTotalStakeCache ..
|
||||||
|
func newTotalStakeCache(duration uint64) *totalStakeCache { |
||||||
|
return &totalStakeCache{ |
||||||
|
cachedBlockHeight: 0, |
||||||
|
stake: nil, |
||||||
|
duration: duration, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (c *totalStakeCache) push(currBlockHeight uint64, stake *big.Int) { |
||||||
|
c.Lock() |
||||||
|
defer c.Unlock() |
||||||
|
c.cachedBlockHeight = currBlockHeight |
||||||
|
c.stake = stake |
||||||
|
} |
||||||
|
|
||||||
|
func (c *totalStakeCache) pop(currBlockHeight uint64) *big.Int { |
||||||
|
if currBlockHeight > c.cachedBlockHeight+c.duration { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return c.stake |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package hmy |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
"github.com/harmony-one/harmony/core" |
||||||
|
"github.com/harmony-one/harmony/core/rawdb" |
||||||
|
"github.com/harmony-one/harmony/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// SendTx ...
|
||||||
|
func (hmy *Harmony) SendTx(ctx context.Context, signedTx *types.Transaction) error { |
||||||
|
tx, _, _, _ := rawdb.ReadTransaction(hmy.chainDb, signedTx.Hash()) |
||||||
|
if tx == nil { |
||||||
|
return hmy.NodeAPI.AddPendingTransaction(signedTx) |
||||||
|
} |
||||||
|
return ErrFinalizedTransaction |
||||||
|
} |
||||||
|
|
||||||
|
// ResendCx retrieve blockHash from txID and add blockHash to CxPool for resending
|
||||||
|
// Note that cross shard txn is only for regular txns, not for staking txns, so the input txn hash
|
||||||
|
// is expected to be regular txn hash
|
||||||
|
func (hmy *Harmony) ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) { |
||||||
|
blockHash, blockNum, index := hmy.BlockChain.ReadTxLookupEntry(txID) |
||||||
|
if blockHash == (common.Hash{}) { |
||||||
|
return 0, false |
||||||
|
} |
||||||
|
|
||||||
|
blk := hmy.BlockChain.GetBlockByHash(blockHash) |
||||||
|
if blk == nil { |
||||||
|
return 0, false |
||||||
|
} |
||||||
|
|
||||||
|
txs := blk.Transactions() |
||||||
|
// a valid index is from 0 to len-1
|
||||||
|
if int(index) > len(txs)-1 { |
||||||
|
return 0, false |
||||||
|
} |
||||||
|
tx := txs[int(index)] |
||||||
|
|
||||||
|
// check whether it is a valid cross shard tx
|
||||||
|
if tx.ShardID() == tx.ToShardID() || blk.Header().ShardID() != tx.ShardID() { |
||||||
|
return 0, false |
||||||
|
} |
||||||
|
entry := core.CxEntry{blockHash, tx.ToShardID()} |
||||||
|
success := hmy.CxPool.Add(entry) |
||||||
|
return blockNum, success |
||||||
|
} |
||||||
|
|
||||||
|
// GetReceipts ...
|
||||||
|
func (hmy *Harmony) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { |
||||||
|
return hmy.BlockChain.GetReceiptsByHash(hash), nil |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionsHistory returns list of transactions hashes of address.
|
||||||
|
func (hmy *Harmony) GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) { |
||||||
|
return hmy.NodeAPI.GetTransactionsHistory(address, txType, order) |
||||||
|
} |
||||||
|
|
||||||
|
// GetAccountNonce returns the nonce value of the given address for the given block number
|
||||||
|
func (hmy *Harmony) GetAccountNonce( |
||||||
|
ctx context.Context, address common.Address, blockNum rpc.BlockNumber) (uint64, error) { |
||||||
|
state, _, err := hmy.StateAndHeaderByNumber(ctx, blockNum) |
||||||
|
if state == nil || err != nil { |
||||||
|
return 0, err |
||||||
|
} |
||||||
|
return state.GetNonce(address), state.Error() |
||||||
|
} |
||||||
|
|
||||||
|
// GetTransactionsCount returns the number of regular transactions of address.
|
||||||
|
func (hmy *Harmony) GetTransactionsCount(address, txType string) (uint64, error) { |
||||||
|
return hmy.NodeAPI.GetTransactionsCount(address, txType) |
||||||
|
} |
||||||
|
|
||||||
|
// GetCurrentTransactionErrorSink ..
|
||||||
|
func (hmy *Harmony) GetCurrentTransactionErrorSink() types.TransactionErrorReports { |
||||||
|
return hmy.NodeAPI.ReportPlainErrorSink() |
||||||
|
} |
@ -1,99 +0,0 @@ |
|||||||
package apiv1 |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/ethereum/go-ethereum/rpc" |
|
||||||
"github.com/harmony-one/harmony/block" |
|
||||||
"github.com/harmony-one/harmony/consensus/quorum" |
|
||||||
"github.com/harmony-one/harmony/core" |
|
||||||
"github.com/harmony-one/harmony/core/state" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
"github.com/harmony-one/harmony/core/vm" |
|
||||||
"github.com/harmony-one/harmony/crypto/bls" |
|
||||||
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
|
||||||
"github.com/harmony-one/harmony/internal/params" |
|
||||||
"github.com/harmony-one/harmony/shard" |
|
||||||
"github.com/harmony-one/harmony/shard/committee" |
|
||||||
"github.com/harmony-one/harmony/staking/network" |
|
||||||
staking "github.com/harmony-one/harmony/staking/types" |
|
||||||
) |
|
||||||
|
|
||||||
// Backend interface provides the common API services (that are provided by
|
|
||||||
// both full and light clients) with access to necessary functions.
|
|
||||||
// implementations:
|
|
||||||
// * hmy/api_backend.go
|
|
||||||
type Backend interface { |
|
||||||
NetVersion() uint64 |
|
||||||
ProtocolVersion() int |
|
||||||
ChainDb() ethdb.Database |
|
||||||
SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) |
|
||||||
SingleFlightForgetKey(key string) |
|
||||||
EventMux() *event.TypeMux |
|
||||||
RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection
|
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) |
|
||||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) |
|
||||||
StateAndHeaderByNumber( |
|
||||||
ctx context.Context, blockNr rpc.BlockNumber, |
|
||||||
) (*state.DB, *block.Header, error) |
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) |
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
|
||||||
|
|
||||||
GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) |
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription |
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription |
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription |
|
||||||
// TxPool API
|
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error |
|
||||||
// GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
|
|
||||||
GetPoolTransactions() (types.PoolTransactions, error) |
|
||||||
GetPoolTransaction(txHash common.Hash) types.PoolTransaction |
|
||||||
GetPoolStats() (pendingCount, queuedCount int) |
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) |
|
||||||
// Get account nonce
|
|
||||||
GetAccountNonce(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) |
|
||||||
// TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
|
|
||||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription |
|
||||||
ChainConfig() *params.ChainConfig |
|
||||||
CurrentBlock() *types.Block |
|
||||||
// Get balance
|
|
||||||
GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) |
|
||||||
// Get validators for a particular epoch
|
|
||||||
GetValidators(epoch *big.Int) (*shard.Committee, error) |
|
||||||
GetShardID() uint32 |
|
||||||
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetTransactionsCount(address, txType string) (uint64, error) |
|
||||||
GetStakingTransactionsCount(address, txType string) (uint64, error) |
|
||||||
// retrieve the blockHash using txID and add blockHash to CxPool for resending
|
|
||||||
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) |
|
||||||
IsLeader() bool |
|
||||||
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error |
|
||||||
GetElectedValidatorAddresses() []common.Address |
|
||||||
GetAllValidatorAddresses() []common.Address |
|
||||||
GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) |
|
||||||
GetDelegationsByValidator(validator common.Address) []*staking.Delegation |
|
||||||
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) |
|
||||||
GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) |
|
||||||
GetValidatorSelfDelegation(addr common.Address) *big.Int |
|
||||||
GetShardState() (*shard.State, error) |
|
||||||
GetCurrentStakingErrorSink() types.TransactionErrorReports |
|
||||||
GetCurrentTransactionErrorSink() types.TransactionErrorReports |
|
||||||
GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) |
|
||||||
GetPendingCXReceipts() []*types.CXReceiptsProof |
|
||||||
GetCurrentUtilityMetrics() (*network.UtilityMetric, error) |
|
||||||
GetSuperCommittees() (*quorum.Transition, error) |
|
||||||
GetTotalStakingSnapshot() *big.Int |
|
||||||
GetCurrentBadBlocks() []core.BadBlock |
|
||||||
GetLastCrossLinks() ([]*types.CrossLink, error) |
|
||||||
GetLatestChainHeaders() *block.HeaderPair |
|
||||||
GetNodeMetadata() commonRPC.NodeMetadata |
|
||||||
GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) |
|
||||||
IsStakingEpoch(epoch *big.Int) bool |
|
||||||
GetLeaderAddress(a common.Address, e *big.Int) string |
|
||||||
GetPeerInfo() commonRPC.NodePeerInfo |
|
||||||
} |
|
@ -1,21 +0,0 @@ |
|||||||
package apiv1 |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil" |
|
||||||
) |
|
||||||
|
|
||||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
|
||||||
type SendTxArgs struct { |
|
||||||
From common.Address `json:"from"` |
|
||||||
To *common.Address `json:"to"` |
|
||||||
ShardID uint32 `json:"shardID"` |
|
||||||
Gas *hexutil.Uint64 `json:"gas"` |
|
||||||
GasPrice *hexutil.Big `json:"gasPrice"` |
|
||||||
Value *hexutil.Big `json:"value"` |
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"` |
|
||||||
// We accept "data" and "input" for backwards-compatibility reasons. "input" is the
|
|
||||||
// newer name and should be preferred by clients.
|
|
||||||
Data *hexutil.Bytes `json:"data"` |
|
||||||
Input *hexutil.Bytes `json:"input"` |
|
||||||
} |
|
@ -1,95 +0,0 @@ |
|||||||
package apiv2 |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/ethereum/go-ethereum/rpc" |
|
||||||
"github.com/harmony-one/harmony/block" |
|
||||||
"github.com/harmony-one/harmony/consensus/quorum" |
|
||||||
"github.com/harmony-one/harmony/core" |
|
||||||
"github.com/harmony-one/harmony/core/state" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
"github.com/harmony-one/harmony/core/vm" |
|
||||||
"github.com/harmony-one/harmony/crypto/bls" |
|
||||||
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
|
||||||
"github.com/harmony-one/harmony/internal/params" |
|
||||||
"github.com/harmony-one/harmony/shard" |
|
||||||
"github.com/harmony-one/harmony/shard/committee" |
|
||||||
"github.com/harmony-one/harmony/staking/network" |
|
||||||
staking "github.com/harmony-one/harmony/staking/types" |
|
||||||
) |
|
||||||
|
|
||||||
// Backend interface provides the common API services (that are provided by
|
|
||||||
// both full and light clients) with access to necessary functions.
|
|
||||||
// implementations:
|
|
||||||
// * hmy/api_backend.go
|
|
||||||
type Backend interface { |
|
||||||
NetVersion() uint64 |
|
||||||
ProtocolVersion() int |
|
||||||
SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) |
|
||||||
SingleFlightForgetKey(key string) |
|
||||||
ChainDb() ethdb.Database |
|
||||||
EventMux() *event.TypeMux |
|
||||||
RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection
|
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) |
|
||||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) |
|
||||||
StateAndHeaderByNumber( |
|
||||||
ctx context.Context, blockNr rpc.BlockNumber, |
|
||||||
) (*state.DB, *block.Header, error) |
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) |
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
|
||||||
GetEVM( |
|
||||||
ctx context.Context, msg core.Message, |
|
||||||
state *state.DB, header *block.Header, |
|
||||||
) (*vm.EVM, func() error, error) |
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription |
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription |
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription |
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error |
|
||||||
GetPoolTransactions() (types.PoolTransactions, error) |
|
||||||
GetPoolTransaction(txHash common.Hash) types.PoolTransaction |
|
||||||
GetPoolStats() (pendingCount, queuedCount int) |
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) |
|
||||||
GetAccountNonce(ctx context.Context, addr common.Address, blockNr rpc.BlockNumber) (uint64, error) |
|
||||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription |
|
||||||
ChainConfig() *params.ChainConfig |
|
||||||
CurrentBlock() *types.Block |
|
||||||
GetBalance( |
|
||||||
ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) |
|
||||||
GetValidators(epoch *big.Int) (*shard.Committee, error) |
|
||||||
GetShardID() uint32 |
|
||||||
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetTransactionsCount(address, txType string) (uint64, error) |
|
||||||
GetStakingTransactionsCount(address, txType string) (uint64, error) |
|
||||||
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) |
|
||||||
IsLeader() bool |
|
||||||
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error |
|
||||||
GetElectedValidatorAddresses() []common.Address |
|
||||||
GetAllValidatorAddresses() []common.Address |
|
||||||
GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) |
|
||||||
GetDelegationsByValidator(validator common.Address) []*staking.Delegation |
|
||||||
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) |
|
||||||
GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) |
|
||||||
GetValidatorSelfDelegation(addr common.Address) *big.Int |
|
||||||
GetShardState() (*shard.State, error) |
|
||||||
GetCurrentStakingErrorSink() types.TransactionErrorReports |
|
||||||
GetCurrentTransactionErrorSink() types.TransactionErrorReports |
|
||||||
GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) |
|
||||||
GetPendingCXReceipts() []*types.CXReceiptsProof |
|
||||||
GetCurrentUtilityMetrics() (*network.UtilityMetric, error) |
|
||||||
GetSuperCommittees() (*quorum.Transition, error) |
|
||||||
GetTotalStakingSnapshot() *big.Int |
|
||||||
GetCurrentBadBlocks() []core.BadBlock |
|
||||||
GetLastCrossLinks() ([]*types.CrossLink, error) |
|
||||||
GetLatestChainHeaders() *block.HeaderPair |
|
||||||
GetNodeMetadata() commonRPC.NodeMetadata |
|
||||||
GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) |
|
||||||
IsStakingEpoch(epoch *big.Int) bool |
|
||||||
GetLeaderAddress(a common.Address, e *big.Int) string |
|
||||||
GetPeerInfo() commonRPC.NodePeerInfo |
|
||||||
} |
|
@ -1,83 +0,0 @@ |
|||||||
package apiv2 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"context" |
|
||||||
"errors" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
) |
|
||||||
|
|
||||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
|
||||||
type SendTxArgs struct { |
|
||||||
From common.Address `json:"from"` |
|
||||||
To *common.Address `json:"to"` |
|
||||||
ShardID uint32 `json:"shardID"` |
|
||||||
Gas *hexutil.Uint64 `json:"gas"` |
|
||||||
GasPrice *hexutil.Big `json:"gasPrice"` |
|
||||||
Value *hexutil.Big `json:"value"` |
|
||||||
Nonce *hexutil.Uint64 `json:"nonce"` |
|
||||||
// We accept "data" and "input" for backwards-compatibility reasons. "input" is the
|
|
||||||
// newer name and should be preferred by clients.
|
|
||||||
Data *hexutil.Bytes `json:"data"` |
|
||||||
Input *hexutil.Bytes `json:"input"` |
|
||||||
} |
|
||||||
|
|
||||||
// setDefaults is a helper function that fills in default values for unspecified tx fields.
|
|
||||||
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { |
|
||||||
if args.Gas == nil { |
|
||||||
args.Gas = new(hexutil.Uint64) |
|
||||||
*(*uint64)(args.Gas) = 90000 |
|
||||||
} |
|
||||||
// TODO(ricl): add check for shardID
|
|
||||||
// if args.GasPrice == nil {
|
|
||||||
// TODO(ricl): port
|
|
||||||
// price, err := b.SuggestPrice(ctx)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// args.GasPrice = (*hexutil.Big)(price)
|
|
||||||
// }
|
|
||||||
if args.Value == nil { |
|
||||||
args.Value = new(hexutil.Big) |
|
||||||
} |
|
||||||
if args.Nonce == nil { |
|
||||||
nonce, err := b.GetPoolNonce(ctx, args.From) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
args.Nonce = (*hexutil.Uint64)(&nonce) |
|
||||||
} |
|
||||||
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) { |
|
||||||
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`) |
|
||||||
} |
|
||||||
if args.To == nil { |
|
||||||
// Contract creation
|
|
||||||
var input []byte |
|
||||||
if args.Data != nil { |
|
||||||
input = *args.Data |
|
||||||
} else if args.Input != nil { |
|
||||||
input = *args.Input |
|
||||||
} |
|
||||||
if len(input) == 0 { |
|
||||||
return errors.New(`contract creation without any data provided`) |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (args *SendTxArgs) toTransaction() *types.Transaction { |
|
||||||
var input []byte |
|
||||||
if args.Data != nil { |
|
||||||
input = *args.Data |
|
||||||
} else if args.Input != nil { |
|
||||||
input = *args.Input |
|
||||||
} |
|
||||||
if args.To == nil { |
|
||||||
return types.NewContractCreation(uint64(*args.Nonce), args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) |
|
||||||
} |
|
||||||
return types.NewTransaction(uint64(*args.Nonce), *args.To, args.ShardID, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input) |
|
||||||
} |
|
@ -1,145 +1,64 @@ |
|||||||
package hmyapi |
package hmyapi |
||||||
|
|
||||||
import ( |
import ( |
||||||
"context" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/ethereum/go-ethereum/rpc" |
"github.com/ethereum/go-ethereum/rpc" |
||||||
"github.com/harmony-one/harmony/block" |
"github.com/harmony-one/harmony/hmy" |
||||||
"github.com/harmony-one/harmony/consensus/quorum" |
|
||||||
"github.com/harmony-one/harmony/core" |
|
||||||
"github.com/harmony-one/harmony/core/state" |
|
||||||
"github.com/harmony-one/harmony/core/types" |
|
||||||
"github.com/harmony-one/harmony/core/vm" |
|
||||||
"github.com/harmony-one/harmony/crypto/bls" |
|
||||||
"github.com/harmony-one/harmony/internal/hmyapi/apiv1" |
"github.com/harmony-one/harmony/internal/hmyapi/apiv1" |
||||||
"github.com/harmony-one/harmony/internal/hmyapi/apiv2" |
"github.com/harmony-one/harmony/internal/hmyapi/apiv2" |
||||||
commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" |
|
||||||
"github.com/harmony-one/harmony/internal/params" |
|
||||||
"github.com/harmony-one/harmony/shard" |
|
||||||
"github.com/harmony-one/harmony/shard/committee" |
|
||||||
"github.com/harmony-one/harmony/staking/network" |
|
||||||
staking "github.com/harmony-one/harmony/staking/types" |
|
||||||
) |
) |
||||||
|
|
||||||
// Backend ..
|
|
||||||
type Backend interface { |
|
||||||
NetVersion() uint64 |
|
||||||
ProtocolVersion() int |
|
||||||
ChainDb() ethdb.Database |
|
||||||
SingleFlightRequest(key string, fn func() (interface{}, error)) (interface{}, error) |
|
||||||
SingleFlightForgetKey(key string) |
|
||||||
EventMux() *event.TypeMux |
|
||||||
RPCGasCap() *big.Int // global gas cap for hmy_call over rpc: DoS protection
|
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*block.Header, error) |
|
||||||
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) |
|
||||||
StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.DB, *block.Header, error) |
|
||||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) |
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) |
|
||||||
GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, func() error, error) |
|
||||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription |
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription |
|
||||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription |
|
||||||
// TxPool API
|
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error |
|
||||||
GetPoolTransactions() (types.PoolTransactions, error) |
|
||||||
GetPoolTransaction(txHash common.Hash) types.PoolTransaction |
|
||||||
GetPoolStats() (pendingCount, queuedCount int) |
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) |
|
||||||
GetAccountNonce(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (uint64, error) |
|
||||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription |
|
||||||
ChainConfig() *params.ChainConfig |
|
||||||
CurrentBlock() *types.Block |
|
||||||
GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) |
|
||||||
GetValidators(epoch *big.Int) (*shard.Committee, error) |
|
||||||
GetShardID() uint32 |
|
||||||
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetStakingTransactionsHistory(address, txType, order string) ([]common.Hash, error) |
|
||||||
GetTransactionsCount(address, txType string) (uint64, error) |
|
||||||
GetStakingTransactionsCount(address, txType string) (uint64, error) |
|
||||||
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool) |
|
||||||
IsLeader() bool |
|
||||||
SendStakingTx(ctx context.Context, newStakingTx *staking.StakingTransaction) error |
|
||||||
GetElectedValidatorAddresses() []common.Address |
|
||||||
GetAllValidatorAddresses() []common.Address |
|
||||||
GetValidatorInformation(addr common.Address, block *types.Block) (*staking.ValidatorRPCEnhanced, error) |
|
||||||
GetDelegationsByValidator(validator common.Address) []*staking.Delegation |
|
||||||
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) |
|
||||||
GetDelegationsByDelegatorByBlock(delegator common.Address, block *types.Block) ([]common.Address, []*staking.Delegation) |
|
||||||
GetValidatorSelfDelegation(addr common.Address) *big.Int |
|
||||||
GetShardState() (*shard.State, error) |
|
||||||
GetCurrentStakingErrorSink() types.TransactionErrorReports |
|
||||||
GetCurrentTransactionErrorSink() types.TransactionErrorReports |
|
||||||
GetMedianRawStakeSnapshot() (*committee.CompletedEPoSRound, error) |
|
||||||
GetPendingCXReceipts() []*types.CXReceiptsProof |
|
||||||
GetCurrentUtilityMetrics() (*network.UtilityMetric, error) |
|
||||||
GetSuperCommittees() (*quorum.Transition, error) |
|
||||||
GetTotalStakingSnapshot() *big.Int |
|
||||||
GetCurrentBadBlocks() []core.BadBlock |
|
||||||
GetLastCrossLinks() ([]*types.CrossLink, error) |
|
||||||
GetLatestChainHeaders() *block.HeaderPair |
|
||||||
GetNodeMetadata() commonRPC.NodeMetadata |
|
||||||
GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) |
|
||||||
IsStakingEpoch(epoch *big.Int) bool |
|
||||||
GetLeaderAddress(a common.Address, e *big.Int) string |
|
||||||
GetPeerInfo() commonRPC.NodePeerInfo |
|
||||||
} |
|
||||||
|
|
||||||
// GetAPIs returns all the APIs.
|
// GetAPIs returns all the APIs.
|
||||||
func GetAPIs(b Backend) []rpc.API { |
func GetAPIs(hmy *hmy.Harmony) []rpc.API { |
||||||
nonceLock := new(apiv1.AddrLocker) |
nonceLock := new(apiv1.AddrLocker) |
||||||
nonceLockV2 := new(apiv2.AddrLocker) |
nonceLockV2 := new(apiv2.AddrLocker) |
||||||
return []rpc.API{ |
return []rpc.API{ |
||||||
{ |
{ |
||||||
Namespace: "hmy", |
Namespace: "hmy", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv1.NewPublicHarmonyAPI(b), |
Service: apiv1.NewPublicHarmonyAPI(hmy), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmy", |
Namespace: "hmy", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv1.NewPublicBlockChainAPI(b), |
Service: apiv1.NewPublicBlockChainAPI(hmy), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmy", |
Namespace: "hmy", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv1.NewPublicTransactionPoolAPI(b, nonceLock), |
Service: apiv1.NewPublicTransactionPoolAPI(hmy, nonceLock), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmy", |
Namespace: "hmy", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv1.NewDebugAPI(b), |
Service: apiv1.NewDebugAPI(hmy), |
||||||
Public: true, // FIXME: change to false once IPC implemented
|
Public: false, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmyv2", |
Namespace: "hmyv2", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv2.NewPublicHarmonyAPI(b), |
Service: apiv2.NewPublicHarmonyAPI(hmy), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmyv2", |
Namespace: "hmyv2", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv2.NewPublicBlockChainAPI(b), |
Service: apiv2.NewPublicBlockChainAPI(hmy), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmyv2", |
Namespace: "hmyv2", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv2.NewPublicTransactionPoolAPI(b, nonceLockV2), |
Service: apiv2.NewPublicTransactionPoolAPI(hmy, nonceLockV2), |
||||||
Public: true, |
Public: true, |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
Namespace: "hmyv2", |
Namespace: "hmyv2", |
||||||
Version: "1.0", |
Version: "1.0", |
||||||
Service: apiv2.NewDebugAPI(b), |
Service: apiv2.NewDebugAPI(hmy), |
||||||
Public: true, // FIXME: change to false once IPC implemented
|
Public: false, |
||||||
}, |
}, |
||||||
} |
} |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue