|
|
@ -4,10 +4,12 @@ import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
"fmt" |
|
|
|
"fmt" |
|
|
|
"math/big" |
|
|
|
"math/big" |
|
|
|
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common" |
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil" |
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil" |
|
|
|
"github.com/ethereum/go-ethereum/rpc" |
|
|
|
"github.com/ethereum/go-ethereum/rpc" |
|
|
|
|
|
|
|
"golang.org/x/time/rate" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/harmony-one/harmony/hmy" |
|
|
|
"github.com/harmony-one/harmony/hmy" |
|
|
|
"github.com/harmony-one/harmony/internal/chain" |
|
|
|
"github.com/harmony-one/harmony/internal/chain" |
|
|
@ -21,23 +23,43 @@ import ( |
|
|
|
v2 "github.com/harmony-one/harmony/rpc/v2" |
|
|
|
v2 "github.com/harmony-one/harmony/rpc/v2" |
|
|
|
"github.com/harmony-one/harmony/shard" |
|
|
|
"github.com/harmony-one/harmony/shard" |
|
|
|
stakingReward "github.com/harmony-one/harmony/staking/reward" |
|
|
|
stakingReward "github.com/harmony-one/harmony/staking/reward" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lru "github.com/hashicorp/golang-lru" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// PublicBlockchainService provides an API to access the Harmony blockchain.
|
|
|
|
// PublicBlockchainService provides an API to access the Harmony blockchain.
|
|
|
|
// It offers only methods that operate on public data that is freely available to anyone.
|
|
|
|
// It offers only methods that operate on public data that is freely available to anyone.
|
|
|
|
type PublicBlockchainService struct { |
|
|
|
type PublicBlockchainService struct { |
|
|
|
hmy *hmy.Harmony |
|
|
|
hmy *hmy.Harmony |
|
|
|
version Version |
|
|
|
version Version |
|
|
|
|
|
|
|
limiter *rate.Limiter |
|
|
|
|
|
|
|
blockCache *lru.Cache |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
|
|
DefaultRateLimiterWaitTimeout = 5 * time.Second |
|
|
|
|
|
|
|
blockCacheLimit = 256 |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// NewPublicBlockchainAPI creates a new API for the RPC interface
|
|
|
|
// NewPublicBlockchainAPI creates a new API for the RPC interface
|
|
|
|
func NewPublicBlockchainAPI(hmy *hmy.Harmony, version Version) rpc.API { |
|
|
|
func NewPublicBlockchainAPI(hmy *hmy.Harmony, version Version, limiterEnable bool, limit int) rpc.API { |
|
|
|
return rpc.API{ |
|
|
|
blockCache, _ := lru.New(blockCacheLimit) |
|
|
|
Namespace: version.Namespace(), |
|
|
|
if limiterEnable { |
|
|
|
Version: APIVersion, |
|
|
|
return rpc.API{ |
|
|
|
Service: &PublicBlockchainService{hmy, version}, |
|
|
|
Namespace: version.Namespace(), |
|
|
|
Public: true, |
|
|
|
Version: APIVersion, |
|
|
|
|
|
|
|
Service: &PublicBlockchainService{hmy, version, rate.NewLimiter(rate.Limit(limit), 1), blockCache}, |
|
|
|
|
|
|
|
Public: true, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return rpc.API{ |
|
|
|
|
|
|
|
Namespace: version.Namespace(), |
|
|
|
|
|
|
|
Version: APIVersion, |
|
|
|
|
|
|
|
Service: &PublicBlockchainService{hmy, version, nil, blockCache}, |
|
|
|
|
|
|
|
Public: true, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChainId returns the chain id of the chain - required by MetaMask
|
|
|
|
// ChainId returns the chain id of the chain - required by MetaMask
|
|
|
@ -122,14 +144,33 @@ func (s *PublicBlockchainService) BlockNumber(ctx context.Context) (interface{}, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *PublicBlockchainService) wait(ctx context.Context) error { |
|
|
|
|
|
|
|
if s.limiter != nil { |
|
|
|
|
|
|
|
deadlineCtx, cancel := context.WithTimeout(ctx, DefaultRateLimiterWaitTimeout) |
|
|
|
|
|
|
|
defer cancel() |
|
|
|
|
|
|
|
return s.limiter.Wait(deadlineCtx) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GetBlockByNumber returns the requested block. When blockNum is -1 the chain head is returned. When fullTx is true all
|
|
|
|
// GetBlockByNumber returns the requested block. When blockNum is -1 the chain head is returned. When fullTx is true all
|
|
|
|
// transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
|
|
|
|
// 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.
|
|
|
|
// When withSigners in BlocksArgs is true it shows block signers for this block in list of one addresses.
|
|
|
|
func (s *PublicBlockchainService) GetBlockByNumber( |
|
|
|
func (s *PublicBlockchainService) GetBlockByNumber( |
|
|
|
ctx context.Context, blockNumber BlockNumber, opts interface{}, |
|
|
|
ctx context.Context, blockNumber BlockNumber, opts interface{}, |
|
|
|
) (response StructuredResponse, err error) { |
|
|
|
) (response StructuredResponse, err error) { |
|
|
|
// Process arguments based on version
|
|
|
|
|
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
|
|
|
|
if block, ok := s.blockCache.Get(uint64(blockNum)); ok { |
|
|
|
|
|
|
|
return block.(StructuredResponse), nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process arguments based on version
|
|
|
|
var blockArgs *rpc_common.BlockArgs |
|
|
|
var blockArgs *rpc_common.BlockArgs |
|
|
|
blockArgs, ok := opts.(*rpc_common.BlockArgs) |
|
|
|
blockArgs, ok := opts.(*rpc_common.BlockArgs) |
|
|
|
if !ok { |
|
|
|
if !ok { |
|
|
@ -186,6 +227,7 @@ func (s *PublicBlockchainService) GetBlockByNumber( |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s.blockCache.Add(uint64(blockNum), response) |
|
|
|
return response, err |
|
|
|
return response, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -198,6 +240,12 @@ func (s *PublicBlockchainService) GetBlockByNumber( |
|
|
|
func (s *PublicBlockchainService) GetBlockByHash( |
|
|
|
func (s *PublicBlockchainService) GetBlockByHash( |
|
|
|
ctx context.Context, blockHash common.Hash, opts interface{}, |
|
|
|
ctx context.Context, blockHash common.Hash, opts interface{}, |
|
|
|
) (response StructuredResponse, err error) { |
|
|
|
) (response StructuredResponse, err error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Process arguments based on version
|
|
|
|
// Process arguments based on version
|
|
|
|
var blockArgs *rpc_common.BlockArgs |
|
|
|
var blockArgs *rpc_common.BlockArgs |
|
|
|
blockArgs, ok := opts.(*rpc_common.BlockArgs) |
|
|
|
blockArgs, ok := opts.(*rpc_common.BlockArgs) |
|
|
@ -260,6 +308,12 @@ func (s *PublicBlockchainService) GetBlocks( |
|
|
|
ctx context.Context, blockNumberStart BlockNumber, |
|
|
|
ctx context.Context, blockNumberStart BlockNumber, |
|
|
|
blockNumberEnd BlockNumber, blockArgs *rpc_common.BlockArgs, |
|
|
|
blockNumberEnd BlockNumber, blockArgs *rpc_common.BlockArgs, |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
blockStart := blockNumberStart.Int64() |
|
|
|
blockStart := blockNumberStart.Int64() |
|
|
|
blockEnd := blockNumberEnd.Int64() |
|
|
|
blockEnd := blockNumberEnd.Int64() |
|
|
|
|
|
|
|
|
|
|
@ -469,6 +523,11 @@ func (s *PublicBlockchainService) GetLeader(ctx context.Context) (string, error) |
|
|
|
func (s *PublicBlockchainService) GetShardingStructure( |
|
|
|
func (s *PublicBlockchainService) GetShardingStructure( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get header and number of shards.
|
|
|
|
// Get header and number of shards.
|
|
|
|
epoch := s.hmy.CurrentBlock().Epoch() |
|
|
|
epoch := s.hmy.CurrentBlock().Epoch() |
|
|
|
numShard := shard.Schedule.InstanceForEpoch(epoch).NumShards() |
|
|
|
numShard := shard.Schedule.InstanceForEpoch(epoch).NumShards() |
|
|
@ -487,6 +546,12 @@ func (s *PublicBlockchainService) GetShardID(ctx context.Context) (int, error) { |
|
|
|
func (s *PublicBlockchainService) GetBalanceByBlockNumber( |
|
|
|
func (s *PublicBlockchainService) GetBalanceByBlockNumber( |
|
|
|
ctx context.Context, address string, blockNumber BlockNumber, |
|
|
|
ctx context.Context, address string, blockNumber BlockNumber, |
|
|
|
) (interface{}, error) { |
|
|
|
) (interface{}, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Process number based on version
|
|
|
|
// Process number based on version
|
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
|
|
|
|
|
|
|
@ -512,6 +577,12 @@ func (s *PublicBlockchainService) GetBalanceByBlockNumber( |
|
|
|
|
|
|
|
|
|
|
|
// LatestHeader returns the latest header information
|
|
|
|
// LatestHeader returns the latest header information
|
|
|
|
func (s *PublicBlockchainService) LatestHeader(ctx context.Context) (StructuredResponse, error) { |
|
|
|
func (s *PublicBlockchainService) LatestHeader(ctx context.Context) (StructuredResponse, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Fetch Header
|
|
|
|
// Fetch Header
|
|
|
|
header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber) |
|
|
|
header, err := s.hmy.HeaderByNumber(ctx, rpc.LatestBlockNumber) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -535,6 +606,12 @@ func (s *PublicBlockchainService) GetLatestChainHeaders( |
|
|
|
func (s *PublicBlockchainService) GetLastCrossLinks( |
|
|
|
func (s *PublicBlockchainService) GetLastCrossLinks( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
} |
|
|
|
} |
|
|
@ -561,6 +638,11 @@ func (s *PublicBlockchainService) GetLastCrossLinks( |
|
|
|
func (s *PublicBlockchainService) GetHeaderByNumber( |
|
|
|
func (s *PublicBlockchainService) GetHeaderByNumber( |
|
|
|
ctx context.Context, blockNumber BlockNumber, |
|
|
|
ctx context.Context, blockNumber BlockNumber, |
|
|
|
) (StructuredResponse, error) { |
|
|
|
) (StructuredResponse, error) { |
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Process number based on version
|
|
|
|
// Process number based on version
|
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
blockNum := blockNumber.EthBlockNumber() |
|
|
|
|
|
|
|
|
|
|
@ -583,6 +665,11 @@ func (s *PublicBlockchainService) GetHeaderByNumber( |
|
|
|
func (s *PublicBlockchainService) GetCurrentUtilityMetrics( |
|
|
|
func (s *PublicBlockchainService) GetCurrentUtilityMetrics( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) (StructuredResponse, error) { |
|
|
|
) (StructuredResponse, error) { |
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
} |
|
|
|
} |
|
|
@ -601,6 +688,11 @@ func (s *PublicBlockchainService) GetCurrentUtilityMetrics( |
|
|
|
func (s *PublicBlockchainService) GetSuperCommittees( |
|
|
|
func (s *PublicBlockchainService) GetSuperCommittees( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) (StructuredResponse, error) { |
|
|
|
) (StructuredResponse, error) { |
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
} |
|
|
|
} |
|
|
@ -619,6 +711,12 @@ func (s *PublicBlockchainService) GetSuperCommittees( |
|
|
|
func (s *PublicBlockchainService) GetCurrentBadBlocks( |
|
|
|
func (s *PublicBlockchainService) GetCurrentBadBlocks( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
) ([]StructuredResponse, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Fetch bad blocks and format
|
|
|
|
// Fetch bad blocks and format
|
|
|
|
badBlocks := []StructuredResponse{} |
|
|
|
badBlocks := []StructuredResponse{} |
|
|
|
for _, blk := range s.hmy.GetCurrentBadBlocks() { |
|
|
|
for _, blk := range s.hmy.GetCurrentBadBlocks() { |
|
|
@ -651,6 +749,12 @@ func (s *PublicBlockchainService) GetCirculatingSupply( |
|
|
|
func (s *PublicBlockchainService) GetStakingNetworkInfo( |
|
|
|
func (s *PublicBlockchainService) GetStakingNetworkInfo( |
|
|
|
ctx context.Context, |
|
|
|
ctx context.Context, |
|
|
|
) (StructuredResponse, error) { |
|
|
|
) (StructuredResponse, error) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err := s.wait(ctx) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
|
|
|
|
return nil, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
if !isBeaconShard(s.hmy) { |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
return nil, ErrNotBeaconShard |
|
|
|
} |
|
|
|
} |
|
|
|