Hmy api updates for validator explorer and basic functionality

pull/1745/head
flicker-harmony 5 years ago
parent 8b5a3235fd
commit 0fb9a76fd8
  1. 2
      internal/hmyapi/backend.go
  2. 177
      internal/hmyapi/blockchain.go
  3. 18
      internal/hmyapi/transactionpool.go
  4. 11
      internal/hmyapi/types.go
  5. 18
      node/node_explorer.go

@ -68,7 +68,7 @@ type Backend interface {
GetCommittee(epoch *big.Int) (*shard.Committee, error)
GetShardID() uint32
// Get transactions history for an address
GetTransactionsHistory(address string) ([]common.Hash, error)
GetTransactionsHistory(address, txType, order string) ([]common.Hash, error)
// retrieve the blockHash using txID and add blockHash to CxPool for resending
ResendCx(ctx context.Context, txID common.Hash) (uint64, bool)
IsLeader() bool

@ -4,8 +4,6 @@ import (
"context"
"fmt"
"github.com/harmony-one/harmony/common/denominations"
"math/big"
"time"
@ -13,16 +11,21 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/rpc"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
internal_bls "github.com/harmony-one/harmony/crypto/bls"
internal_common "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/utils"
)
const (
defaultGasPrice = denominations.Nano
defaultFromAddress = "0x0000000000000000000000000000000000000000"
defaultGasPrice = denominations.Nano
defaultFromAddress = "0x0000000000000000000000000000000000000000"
defaultBlocksPeriod = 15000
)
// PublicBlockChainAPI provides an API to access the Harmony blockchain.
@ -36,12 +39,28 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI {
return &PublicBlockChainAPI{b}
}
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
// BlockArgs is struct to include optional block formatting params.
type BlockArgs struct {
WithSigners bool `json:"withSigners"`
InclTx bool `json:"inclTx"`
FullTx bool `json:"fullTx"`
Signers []string `json:"signers"`
}
// GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx in BlockArgs is true all
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned. When withSigners in BlocksArgs is true
// it shows block signers for this block in list of one addresses.
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, blockArgs BlockArgs) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr)
blockArgs.InclTx = true
if blockArgs.WithSigners {
blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNr)
if err != nil {
return nil, err
}
}
if block != nil {
response, err := RPCMarshalBlock(block, true, fullTx)
response, err := RPCMarshalBlock(block, blockArgs)
if err == nil && blockNr == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
@ -55,14 +74,47 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
// detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, blockArgs BlockArgs) (map[string]interface{}, error) {
block, err := s.b.GetBlock(ctx, blockHash)
blockArgs.InclTx = true
if blockArgs.WithSigners {
blockArgs.Signers, err = s.GetBlockSigners(ctx, rpc.BlockNumber(block.NumberU64()))
if err != nil {
return nil, err
}
}
if block != nil {
return RPCMarshalBlock(block, true, fullTx)
return RPCMarshalBlock(block, blockArgs)
}
return nil, err
}
// GetBlocks method returns blocks in range blockStart, blockEnd just like GetBlockByNumber but all at once.
func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.BlockNumber, blockEnd rpc.BlockNumber, blockArgs BlockArgs) ([]map[string]interface{}, error) {
result := make([]map[string]interface{}, 0)
for i := blockStart; i < blockEnd; i++ {
block, err := s.b.BlockByNumber(ctx, i)
blockArgs.InclTx = true
if blockArgs.WithSigners {
blockArgs.Signers, err = s.GetBlockSigners(ctx, i)
if err != nil {
return nil, err
}
}
if block != nil {
rpcBlock, err := RPCMarshalBlock(block, blockArgs)
if err == nil && i == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
rpcBlock[field] = nil
}
result = append(result, rpcBlock)
}
}
}
return result, nil
}
// GetCommittee returns committee for a particular epoch.
func (s *PublicBlockChainAPI) GetCommittee(ctx context.Context, epoch int64) (map[string]interface{}, error) {
committee, err := s.b.GetCommittee(big.NewInt(epoch))
@ -93,11 +145,112 @@ func (s *PublicBlockChainAPI) GetCommittee(ctx context.Context, epoch int64) (ma
return result, nil
}
// GetBlockSigners returns signers for a particular block.
func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) ([]string, error) {
epoch := s.GetEpoch(ctx)
committee, err := s.b.GetCommittee(big.NewInt(int64(epoch)))
pubkeys := make([]*bls.PublicKey, len(committee.NodeList))
result := make([]string, 0)
for i, validator := range committee.NodeList {
pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
}
mask, err := internal_bls.NewMask(pubkeys, nil)
if err != nil {
return result, err
}
block, err := s.b.BlockByNumber(ctx, blockNr+1)
if err != nil {
return result, err
}
err = mask.SetMask(block.Header().LastCommitBitmap())
if err != nil {
return result, err
}
for _, validator := range committee.NodeList {
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
if err != nil {
return result, err
}
blsPublicKey := new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(blsPublicKey)
if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok {
result = append(result, oneAddress)
}
}
return result, nil
}
// IsBlockSigner returns true if validator with address signed blockNr block.
func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.BlockNumber, address string) (bool, error) {
epoch := s.GetEpoch(ctx)
committee, err := s.b.GetCommittee(big.NewInt(int64(epoch)))
pubkeys := make([]*bls.PublicKey, len(committee.NodeList))
for i, validator := range committee.NodeList {
pubkeys[i] = new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(pubkeys[i])
}
mask, err := internal_bls.NewMask(pubkeys, nil)
if err != nil {
return false, err
}
block, err := s.b.BlockByNumber(ctx, blockNr+1)
if err != nil {
return false, err
}
err = mask.SetMask(block.Header().LastCommitBitmap())
if err != nil {
return false, err
}
for _, validator := range committee.NodeList {
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
if err != nil {
return false, err
}
if oneAddress != address {
continue
}
blsPublicKey := new(bls.PublicKey)
validator.BlsPublicKey.ToLibBLSPublicKey(blsPublicKey)
if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok {
return true, nil
}
}
return false, nil
}
// GetSignedBlocks returns how many blocks a particular validator signed for last defaultBlocksPeriod (3 hours ~ 1500 blocks).
func (s *PublicBlockChainAPI) GetSignedBlocks(ctx context.Context, address string) uint64 {
header := s.LatestHeader(ctx)
totalSigned := uint64(0)
lastBlock := uint64(0)
if header.BlockNumber >= defaultBlocksPeriod {
lastBlock = header.BlockNumber - defaultBlocksPeriod + 1
}
for i := header.BlockNumber; i >= lastBlock; i-- {
signed, err := s.IsBlockSigner(ctx, rpc.BlockNumber(i), address)
if err == nil && signed {
totalSigned++
}
}
return totalSigned
}
// GetEpoch returns current epoch.
func (s *PublicBlockChainAPI) GetEpoch(ctx context.Context) uint64 {
return s.LatestHeader(ctx).Epoch
}
// GetLeader returns current shard leader.
func (s *PublicBlockChainAPI) GetLeader(ctx context.Context) string {
return s.LatestHeader(ctx).Leader
}
// GetShardingStructure returns an array of sharding structures.
func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[string]interface{}, error) {
// Get header and number of shards.
header := s.b.CurrentBlock().Header()
numShard := core.ShardingSchedule.InstanceForEpoch(header.Epoch()).NumShards()
epoch := s.GetEpoch(ctx)
numShard := core.ShardingSchedule.InstanceForEpoch(big.NewInt(int64(epoch))).NumShards()
// Return shareding structure for each case.
return core.ShardingSchedule.GetShardingStructure(int(numShard), int(s.b.GetShardID())), nil

@ -23,6 +23,8 @@ type TxHistoryArgs struct {
PageIndex int `json:"pageIndex"`
PageSize int `json:"pageSize"`
FullTx bool `json:"fullTx"`
TxType string `json:"txType"`
Order string `json:"order"`
}
// PublicTransactionPoolAPI exposes methods for the RPC interface
@ -40,19 +42,17 @@ func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransa
func (s *PublicTransactionPoolAPI) GetTransactionsHistory(ctx context.Context, args TxHistoryArgs) (map[string]interface{}, error) {
address := args.Address
result := []common.Hash{}
if strings.HasPrefix(address, "one1") {
hashes, err := s.b.GetTransactionsHistory(address)
var err error
if strings.HasPrefix(args.Address, "one1") {
address = args.Address
} else {
addr := internal_common.ParseAddr(args.Address)
address, err = internal_common.AddressToBech32(addr)
if err != nil {
return nil, err
}
result = ReturnWithPagination(hashes, args)
}
addr := internal_common.ParseAddr(address)
oneAddress, err := internal_common.AddressToBech32(addr)
if err != nil {
return nil, err
}
hashes, err := s.b.GetTransactionsHistory(oneAddress)
hashes, err := s.b.GetTransactionsHistory(address, args.TxType, args.Order)
if err != nil {
return nil, err
}

@ -192,12 +192,13 @@ type RPCBlock struct {
Transactions []interface{} `json:"transactions"`
Uncles []common.Hash `json:"uncles"`
TotalDifficulty *big.Int `json:"totalDifficulty"`
Signers []string `json:"signers"`
}
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
func RPCMarshalBlock(b *types.Block, blockArgs BlockArgs) (map[string]interface{}, error) {
head := b.Header() // copies the header once
fields := map[string]interface{}{
"number": (*hexutil.Big)(head.Number()),
@ -218,11 +219,11 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter
"receiptsRoot": head.ReceiptHash(),
}
if inclTx {
if blockArgs.InclTx {
formatTx := func(tx *types.Transaction) (interface{}, error) {
return tx.Hash(), nil
}
if fullTx {
if blockArgs.FullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
return newRPCTransactionFromBlockHash(b, tx.Hash()), nil
}
@ -244,7 +245,9 @@ func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]inter
uncleHashes[i] = uncle.Hash()
}
fields["uncles"] = uncleHashes
if blockArgs.WithSigners {
fields["signers"] = blockArgs.Signers
}
return fields, nil
}

@ -2,6 +2,7 @@ package node
import (
"encoding/binary"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
@ -143,7 +144,7 @@ func (node *Node) commitBlockForExplorer(block *types.Block) {
}
// GetTransactionsHistory returns list of transactions hashes of address.
func (node *Node) GetTransactionsHistory(address string) ([]common.Hash, error) {
func (node *Node) GetTransactionsHistory(address, txType, order string) ([]common.Hash, error) {
addressData := &explorer.Address{}
key := explorer.GetAddressKey(address)
bytes, err := explorer.GetStorageInstance(node.SelfPeer.IP, node.SelfPeer.Port, false).GetDB().Get([]byte(key))
@ -154,10 +155,21 @@ func (node *Node) GetTransactionsHistory(address string) ([]common.Hash, error)
utils.Logger().Error().Err(err).Msg("[Explorer] Cannot convert address data from DB")
return nil, err
}
if order == "DESC" {
sort.Slice(addressData.TXs[:], func(i, j int) bool {
return addressData.TXs[i].Timestamp > addressData.TXs[j].Timestamp
})
} else {
sort.Slice(addressData.TXs[:], func(i, j int) bool {
return addressData.TXs[i].Timestamp < addressData.TXs[j].Timestamp
})
}
hashes := make([]common.Hash, 0)
for _, tx := range addressData.TXs {
hash := common.HexToHash(tx.ID)
hashes = append(hashes, hash)
if txType == "" || txType == "ALL" || txType == tx.Type {
hash := common.HexToHash(tx.ID)
hashes = append(hashes, hash)
}
}
return hashes, nil
}

Loading…
Cancel
Save