[rpc] refactor and add caching mechanism in public blockchain service

pull/3845/head
Jacky Wang 3 years ago committed by Leo Chen
parent 27c5aa468f
commit 132ea783b6
  1. 316
      rpc/blockchain.go
  2. 19
      rpc/common/block.go
  3. 71
      rpc/eth/block.go
  4. 49
      rpc/eth/types.go
  5. 68
      rpc/v1/block.go
  6. 73
      rpc/v1/types.go
  7. 68
      rpc/v2/block.go
  8. 70
      rpc/v2/types.go

@ -7,16 +7,12 @@ import (
"strconv"
"time"
"github.com/harmony-one/harmony/internal/chain"
"github.com/prometheus/client_golang/prometheus"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/time/rate"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/hmy"
"github.com/harmony-one/harmony/internal/chain"
internal_common "github.com/harmony-one/harmony/internal/common"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils"
@ -27,47 +23,59 @@ import (
v2 "github.com/harmony-one/harmony/rpc/v2"
"github.com/harmony-one/harmony/shard"
stakingReward "github.com/harmony-one/harmony/staking/reward"
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/time/rate"
)
// PublicBlockchainService provides an API to access the Harmony blockchain.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicBlockchainService struct {
hmy *hmy.Harmony
version Version
limiter *rate.Limiter
blockCache *lru.Cache
hmy *hmy.Harmony
version Version
limiter *rate.Limiter
rpcBlockFactory rpc_common.BlockFactory
}
const (
DefaultRateLimiterWaitTimeout = 5 * time.Second
blockCacheLimit = 2048
rpcGetBlocksLimit = 1024
)
// NewPublicBlockchainAPI creates a new API for the RPC interface
func NewPublicBlockchainAPI(hmy *hmy.Harmony, version Version, limiterEnable bool, limit int) rpc.API {
blockCache, _ := lru.New(blockCacheLimit)
var limiter *rate.Limiter
if limiterEnable {
limiter := rate.NewLimiter(rate.Limit(limit), 1)
strLimit := fmt.Sprintf("%d", int64(limiter.Limit()))
rpcRateLimitCounterVec.With(prometheus.Labels{
"rate_limit": strLimit,
}).Add(float64(0))
return rpc.API{
Namespace: version.Namespace(),
Version: APIVersion,
Service: &PublicBlockchainService{hmy, version, limiter, blockCache},
Public: true,
}
} else {
return rpc.API{
Namespace: version.Namespace(),
Version: APIVersion,
Service: &PublicBlockchainService{hmy, version, nil, blockCache},
Public: true,
}
}
s := &PublicBlockchainService{
hmy: hmy,
version: version,
limiter: limiter,
}
switch version {
case V1:
s.rpcBlockFactory = v1.NewBlockFactory(s.helper())
case V2:
s.rpcBlockFactory = v2.NewBlockFactory(s.helper())
case Eth:
s.rpcBlockFactory = eth.NewBlockFactory(s.helper())
default:
// This shall not happen for legitimate code.
}
return rpc.API{
Namespace: version.Namespace(),
Version: APIVersion,
Service: s,
Public: true,
}
}
@ -174,9 +182,16 @@ func (s *PublicBlockchainService) wait(ctx context.Context) error {
// When withSigners in BlocksArgs is true it shows block signers for this block in list of one addresses.
func (s *PublicBlockchainService) GetBlockByNumber(
ctx context.Context, blockNumber BlockNumber, opts interface{},
) (response StructuredResponse, err error) {
) (response interface{}, err error) {
timer := DoMetricRPCRequest(GetBlockByNumber)
defer DoRPCRequestDuration(GetBlockByNumber, timer)
err = s.wait(ctx)
if err != nil {
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, err
}
// Process arguments based on version
var blockArgs *rpc_common.BlockArgs
blockArgs, ok := opts.(*rpc_common.BlockArgs)
@ -188,80 +203,35 @@ func (s *PublicBlockchainService) GetBlockByNumber(
}
}
blockNum := blockNumber.EthBlockNumber()
if blockNum != rpc.PendingBlockNumber {
realBlockNum := uint64(blockNum)
if blockNum == rpc.LatestBlockNumber {
realBlockNum = s.hmy.CurrentBlock().NumberU64()
}
cacheKey := combineCacheKey(realBlockNum, s.version, blockArgs)
if block, ok := s.blockCache.Get(cacheKey); ok {
return block.(StructuredResponse), nil
}
if blockNumber.EthBlockNumber() == rpc.PendingBlockNumber {
return nil, errors.New("pending block number not implemented")
}
err = s.wait(ctx)
if err != nil {
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, err
var blockNum uint64
if blockNumber.EthBlockNumber() == rpc.LatestBlockNumber {
blockNum = s.hmy.BlockChain.CurrentHeader().Number().Uint64()
} else {
blockNum = uint64(blockNumber.EthBlockNumber().Int64())
}
// Some Ethereum tools (such as Truffle) rely on being able to query for future blocks without the chain returning errors.
// These tools implement retry mechanisms that will query & retry for a given block until it has been finalized.
// Throwing an error like "requested block number greater than current block number" breaks this retry functionality.
// Disable isBlockGreaterThanLatest checks for Ethereum RPC:s, but keep them in place for legacy hmy_ RPC:s for now to ensure backwards compatibility
if s.version != Eth && isBlockGreaterThanLatest(s.hmy, blockNum) {
blk := s.hmy.BlockChain.GetBlockByNumber(blockNum)
if blk == nil {
// Some Ethereum tools (such as Truffle) rely on being able to query for future blocks without the chain returning errors.
// These tools implement retry mechanisms that will query & retry for a given block until it has been finalized.
// Throwing an error like "requested block number greater than current block number" breaks this retry functionality.
// Disable isBlockGreaterThanLatest checks for Ethereum RPC:s, but keep them in place for legacy hmy_ RPC:s for now to ensure backwards compatibility
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, ErrRequestedBlockTooHigh
}
blk, err := s.hmy.BlockByNumber(ctx, blockNum)
if blk == nil || err != nil {
return nil, err
}
if blockArgs.WithSigners {
blockArgs.Signers, err = s.GetBlockSigners(ctx, blockNumber)
if err != nil {
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, err
if s.version == Eth {
return nil, ErrRequestedBlockTooHigh
}
return nil, errors.New("block unknown")
}
// Format the response according to version
leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
var rpcBlock interface{}
switch s.version {
case V1:
rpcBlock, err = v1.NewBlock(blk, blockArgs, leader)
case V2:
rpcBlock, err = v2.NewBlock(blk, blockArgs, leader)
case Eth:
rpcBlock, err = eth.NewBlock(blk, blockArgs, leader)
default:
return nil, ErrUnknownRPCVersion
}
rpcBlock, err := s.rpcBlockFactory.NewBlock(blk, blockArgs)
if err != nil {
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, err
}
response, err = NewStructuredResponse(rpcBlock)
if err != nil {
return nil, err
}
// Pending blocks need to nil out a few fields
if blockNum == rpc.PendingBlockNumber {
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
}
}
if blockNum != rpc.PendingBlockNumber {
cacheKey := combineCacheKey(blk.NumberU64(), s.version, blockArgs)
s.blockCache.Add(cacheKey, response)
}
return response, err
return rpcBlock, err
}
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
@ -269,7 +239,7 @@ func (s *PublicBlockchainService) GetBlockByNumber(
// it shows block signers for this block in list of one addresses.
func (s *PublicBlockchainService) GetBlockByHash(
ctx context.Context, blockHash common.Hash, opts interface{},
) (response StructuredResponse, err error) {
) (response interface{}, err error) {
timer := DoMetricRPCRequest(GetBlockByHash)
defer DoRPCRequestDuration(GetBlockByHash, timer)
@ -292,42 +262,24 @@ func (s *PublicBlockchainService) GetBlockByHash(
// Fetch the block
blk, err := s.hmy.GetBlock(ctx, blockHash)
if blk != nil && err == nil {
if blockArgs.WithSigners {
blockArgs.Signers, err = s.GetBlockSigners(ctx, BlockNumber(blk.NumberU64()))
if err != nil {
DoMetricRPCQueryInfo(GetBlockByHash, FailedNumber)
return nil, err
}
}
if err != nil || blk == nil {
DoMetricRPCQueryInfo(GetBlockByHash, FailedNumber)
return nil, err
}
// Format the response according to version
leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch())
var rpcBlock interface{}
switch s.version {
case V1:
rpcBlock, err = v1.NewBlock(blk, blockArgs, leader)
case V2:
rpcBlock, err = v2.NewBlock(blk, blockArgs, leader)
case Eth:
rpcBlock, err = eth.NewBlock(blk, blockArgs, leader)
default:
return nil, ErrUnknownRPCVersion
}
if err != nil {
DoMetricRPCQueryInfo(GetBlockByHash, FailedNumber)
return nil, err
}
return NewStructuredResponse(rpcBlock)
// Format the response according to version
rpcBlock, err := s.rpcBlockFactory.NewBlock(blk, blockArgs)
if err != nil {
DoMetricRPCQueryInfo(GetBlockByNumber, FailedNumber)
return nil, err
}
DoMetricRPCQueryInfo(GetBlockByHash, FailedNumber)
return nil, err
return rpcBlock, err
}
// GetBlockByNumberNew is an alias for GetBlockByNumber using rpc_common.BlockArgs
func (s *PublicBlockchainService) GetBlockByNumberNew(
ctx context.Context, blockNum BlockNumber, blockArgs *rpc_common.BlockArgs,
) (StructuredResponse, error) {
) (interface{}, error) {
timer := DoMetricRPCRequest(GetBlockByNumberNew)
defer DoRPCRequestDuration(GetBlockByNumberNew, timer)
@ -341,7 +293,7 @@ func (s *PublicBlockchainService) GetBlockByNumberNew(
// GetBlockByHashNew is an alias for GetBlocksByHash using rpc_common.BlockArgs
func (s *PublicBlockchainService) GetBlockByHashNew(
ctx context.Context, blockHash common.Hash, blockArgs *rpc_common.BlockArgs,
) (StructuredResponse, error) {
) (interface{}, error) {
timer := DoMetricRPCRequest(GetBlockByHashNew)
defer DoRPCRequestDuration(GetBlockByHashNew, timer)
@ -356,7 +308,7 @@ func (s *PublicBlockchainService) GetBlockByHashNew(
func (s *PublicBlockchainService) GetBlocks(
ctx context.Context, blockNumberStart BlockNumber,
blockNumberEnd BlockNumber, blockArgs *rpc_common.BlockArgs,
) ([]StructuredResponse, error) {
) ([]interface{}, error) {
timer := DoMetricRPCRequest(GetBlocks)
defer DoRPCRequestDuration(GetBlocks, timer)
@ -370,7 +322,7 @@ func (s *PublicBlockchainService) GetBlocks(
}
// Fetch blocks within given range
result := []StructuredResponse{}
result := make([]interface{}, 0)
for i := blockStart; i <= blockEnd; i++ {
select {
case <-ctx.Done():
@ -923,6 +875,122 @@ func (s *PublicBlockchainService) SetNodeToBackupMode(ctx context.Context, isBac
func combineCacheKey(number uint64, version Version, blockArgs *rpc_common.BlockArgs) string {
// no need format blockArgs.Signers[] as a part of cache key
// because it's not input from rpc caller, it's caculate with blockArgs.WithSigners
// because it's not input from rpc caller, it's calculate with blockArgs.WithSigners
return strconv.FormatUint(number, 10) + strconv.FormatInt(int64(version), 10) + strconv.FormatBool(blockArgs.WithSigners) + strconv.FormatBool(blockArgs.FullTx) + strconv.FormatBool(blockArgs.InclStaking)
}
const (
blockCacheSize = 2048
signersCacheSize = blockCacheSize
stakingTxsCacheSize = blockCacheSize
leaderCacheSize = blockCacheSize
)
type (
// bcServiceHelper is the helper for block factory. Implements
// rpc_common.BlockDataProvider
bcServiceHelper struct {
version Version
hmy *hmy.Harmony
cache *bcServiceCache
}
bcServiceCache struct {
signersCache *lru.Cache // numberU64 -> []string
stakingTxsCache *lru.Cache // numberU64 -> interface{} (v1.StakingTransactions / v2.StakingTransactions)
leaderCache *lru.Cache // numberUint64 -> string
}
)
func (s *PublicBlockchainService) helper() *bcServiceHelper {
signerCache, _ := lru.New(signersCacheSize)
stakingTxsCache, _ := lru.New(stakingTxsCacheSize)
leaderCache, _ := lru.New(leaderCacheSize)
cache := &bcServiceCache{
signersCache: signerCache,
stakingTxsCache: stakingTxsCache,
leaderCache: leaderCache,
}
return &bcServiceHelper{
version: s.version,
hmy: s.hmy,
cache: cache,
}
}
func (helper *bcServiceHelper) GetLeader(b *types.Block) string {
x, ok := helper.cache.leaderCache.Get(b.NumberU64())
if ok && x != nil {
return x.(string)
}
leader := helper.hmy.GetLeaderAddress(b.Coinbase(), b.Epoch())
helper.cache.leaderCache.Add(b.NumberU64(), leader)
return leader
}
func (helper *bcServiceHelper) GetSigners(b *types.Block) ([]string, error) {
x, ok := helper.cache.signersCache.Get(b.NumberU64())
if ok && x != nil {
return x.([]string), nil
}
signers, err := getSigners(helper.hmy, b.NumberU64())
if err != nil {
return nil, errors.Wrap(err, "getSigners")
}
helper.cache.signersCache.Add(b.NumberU64(), signers)
return signers, nil
}
func (helper *bcServiceHelper) GetStakingTxs(b *types.Block) (interface{}, error) {
x, ok := helper.cache.stakingTxsCache.Get(b.NumberU64())
if ok && x != nil {
return x, nil
}
var (
rpcStakings interface{}
err error
)
switch helper.version {
case V1:
rpcStakings, err = v1.StakingTransactionsFromBlock(b)
case V2:
rpcStakings, err = v2.StakingTransactionsFromBlock(b)
case Eth:
err = errors.New("staking transaction data is unsupported to Eth service")
default:
err = fmt.Errorf("unsupported version %v", helper.version)
}
if err != nil {
return nil, err
}
helper.cache.stakingTxsCache.Add(b.NumberU64(), rpcStakings)
return rpcStakings, nil
}
func (helper *bcServiceHelper) GetStakingTxHashes(b *types.Block) []common.Hash {
stkTxs := b.StakingTransactions()
res := make([]common.Hash, 0, len(stkTxs))
for _, tx := range stkTxs {
res = append(res, tx.Hash())
}
return res
}
func getSigners(hmy *hmy.Harmony, number uint64) ([]string, error) {
slots, mask, err := hmy.GetBlockSigners(context.Background(), rpc.BlockNumber(number))
if err != nil {
return nil, err
}
signers := make([]string, 0, len(slots))
for _, validator := range slots {
oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress)
if err != nil {
return nil, err
}
if ok, err := mask.KeyEnabled(validator.BLSPublicKey); err == nil && ok {
signers = append(signers, oneAddress)
}
}
return signers, nil
}

@ -0,0 +1,19 @@
package common
import (
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/types"
)
// BlockFactory is the interface of factory for RPC block data
type BlockFactory interface {
NewBlock(b *types.Block, args *BlockArgs) (interface{}, error)
}
// BlockDataProvider helps with providing data for RPC blocks
type BlockDataProvider interface {
GetLeader(b *types.Block) string
GetSigners(b *types.Block) ([]string, error)
GetStakingTxs(b *types.Block) (interface{}, error)
GetStakingTxHashes(b *types.Block) []common.Hash
}

@ -0,0 +1,71 @@
package eth
import (
"github.com/harmony-one/harmony/core/types"
internal_common "github.com/harmony-one/harmony/internal/common"
rpc_common "github.com/harmony-one/harmony/rpc/common"
"github.com/pkg/errors"
)
// BlockFactory is the factory for v1 rpc block
type BlockFactory struct {
provider rpc_common.BlockDataProvider
}
// NewBlockFactory return the block factory with the given provider
func NewBlockFactory(provider rpc_common.BlockDataProvider) *BlockFactory {
return &BlockFactory{provider}
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func (fac *BlockFactory) NewBlock(b *types.Block, args *rpc_common.BlockArgs) (interface{}, error) {
if args.FullTx {
return fac.newBlockWithFullTx(b, args)
}
return fac.newBlockWithTxHash(b, args)
}
func (fac *BlockFactory) newBlockWithTxHash(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithTxHash, error) {
blk := blockWithTxHashFromBlock(b)
leader := fac.provider.GetLeader(b)
ethLeader, err := internal_common.ParseAddr(leader)
if err != nil {
return nil, errors.Wrap(err, "parse leader address")
}
blk.Block.Miner = ethLeader
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}
func (fac *BlockFactory) newBlockWithFullTx(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithFullTx, error) {
blk, err := blockWithFullTxFromBlock(b)
if err != nil {
return nil, errors.Wrap(err, "blockWithFullTxFromBlock")
}
leader := fac.provider.GetLeader(b)
ethLeader, err := internal_common.ParseAddr(leader)
if err != nil {
return nil, errors.Wrap(err, "parse leader address")
}
blk.Block.Miner = ethLeader
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}

@ -9,8 +9,6 @@ import (
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/harmony-one/harmony/core/types"
hmytypes "github.com/harmony-one/harmony/core/types"
internal_common "github.com/harmony-one/harmony/internal/common"
rpc_common "github.com/harmony-one/harmony/rpc/common"
)
// Block represents a basic block which is further amended by BlockWithTxHash or BlockWithFullTx
@ -157,22 +155,7 @@ func NewReceipt(tx *types.EthTransaction, blockHash common.Hash, blockNumber, bl
return fields, nil
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leaderAddress string) (interface{}, error) {
leader, err := internal_common.ParseAddr(leaderAddress)
if err != nil {
return nil, err
}
if blockArgs.FullTx {
return NewBlockWithFullTx(b, blockArgs, leader)
}
return NewBlockWithTxHash(b, blockArgs, leader)
}
func newBlock(b *types.Block, leader common.Address) *Block {
func newBlock(b *types.Block) *Block {
head := b.Header()
vrfAndProof := head.Vrf()
@ -191,7 +174,6 @@ func newBlock(b *types.Block, leader common.Address) *Block {
UncleHash: hmytypes.CalcUncleHash(b.Uncles()),
LogsBloom: head.Bloom(),
StateRoot: head.Root(),
Miner: leader,
Difficulty: (*hexutil.Big)(big.NewInt(0)), // Legacy comment from hmy -> eth RPC porting: "Remove this because we don't have it in our header"
ExtraData: hexutil.Bytes(head.Extra()),
Size: hexutil.Uint64(b.Size()),
@ -206,13 +188,8 @@ func newBlock(b *types.Block, leader common.Address) *Block {
}
}
// NewBlockWithTxHash ..
func NewBlockWithTxHash(b *types.Block, blockArgs *rpc_common.BlockArgs, leader common.Address) (*BlockWithTxHash, error) {
if blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash")
}
blk := newBlock(b, leader)
func blockWithTxHashFromBlock(b *types.Block) *BlockWithTxHash {
blk := newBlock(b)
blkWithTxs := &BlockWithTxHash{
Block: blk,
Transactions: []common.Hash{},
@ -221,20 +198,11 @@ func NewBlockWithTxHash(b *types.Block, blockArgs *rpc_common.BlockArgs, leader
for _, tx := range b.Transactions() {
blkWithTxs.Transactions = append(blkWithTxs.Transactions, tx.ConvertToEth().Hash())
}
if blockArgs.WithSigners {
blkWithTxs.Signers = blockArgs.Signers
}
return blkWithTxs, nil
return blkWithTxs
}
// NewBlockWithFullTx ..
func NewBlockWithFullTx(b *types.Block, blockArgs *rpc_common.BlockArgs, leader common.Address) (*BlockWithFullTx, error) {
if !blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx")
}
blk := newBlock(b, leader)
func blockWithFullTxFromBlock(b *types.Block) (*BlockWithFullTx, error) {
blk := newBlock(b)
blkWithTxs := &BlockWithFullTx{
Block: blk,
Transactions: []*Transaction{},
@ -247,11 +215,6 @@ func NewBlockWithFullTx(b *types.Block, blockArgs *rpc_common.BlockArgs, leader
}
blkWithTxs.Transactions = append(blkWithTxs.Transactions, fmtTx)
}
if blockArgs.WithSigners {
blkWithTxs.Signers = blockArgs.Signers
}
return blkWithTxs, nil
}

@ -0,0 +1,68 @@
package v1
import (
"github.com/harmony-one/harmony/core/types"
rpc_common "github.com/harmony-one/harmony/rpc/common"
"github.com/pkg/errors"
)
// BlockFactory is the factory for v1 rpc block
type BlockFactory struct {
provider rpc_common.BlockDataProvider
}
// NewBlockFactory return the block factory with the given provider
func NewBlockFactory(provider rpc_common.BlockDataProvider) *BlockFactory {
return &BlockFactory{provider}
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func (fac *BlockFactory) NewBlock(b *types.Block, args *rpc_common.BlockArgs) (interface{}, error) {
if args.FullTx {
return fac.newBlockWithFullTx(b, args)
}
return fac.newBlockWithTxHash(b, args)
}
func (fac *BlockFactory) newBlockWithTxHash(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithTxHash, error) {
blk := blockWithTxHashFromBlock(b)
blk.Miner = fac.provider.GetLeader(b)
if args.InclStaking {
blk.StakingTxs = fac.provider.GetStakingTxHashes(b)
}
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}
func (fac *BlockFactory) newBlockWithFullTx(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithFullTx, error) {
blk, err := blockWithFullTxFromBlock(b)
if err != nil {
return nil, errors.Wrap(err, "blockWithFullTxFromBlock")
}
blk.Miner = fac.provider.GetLeader(b)
if args.InclStaking {
txs, err := fac.provider.GetStakingTxs(b)
if err != nil {
return nil, errors.Wrap(err, "GetStakingTxs")
}
blk.StakingTxs = txs.([]*StakingTransaction)
}
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}

@ -5,13 +5,14 @@ import (
"math/big"
"strings"
"github.com/pkg/errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls"
internal_common "github.com/harmony-one/harmony/internal/common"
rpc_common "github.com/harmony-one/harmony/rpc/common"
staking "github.com/harmony-one/harmony/staking/types"
)
@ -573,24 +574,7 @@ func NewStakingTransaction(tx *staking.StakingTransaction, blockHash common.Hash
return result, nil
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leader string) (interface{}, error) {
if blockArgs.FullTx {
return NewBlockWithFullTx(b, blockArgs, leader)
}
return NewBlockWithTxHash(b, blockArgs, leader)
}
// NewBlockWithTxHash ..
func NewBlockWithTxHash(
b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
) (*BlockWithTxHash, error) {
if blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash")
}
func blockWithTxHashFromBlock(b *types.Block) *BlockWithTxHash {
head := b.Header()
vrfAndProof := head.Vrf()
@ -610,7 +594,6 @@ func NewBlockWithTxHash(
MixHash: head.MixDigest(),
LogsBloom: head.Bloom(),
StateRoot: head.Root(),
Miner: leader,
Difficulty: 0, // Remove this because we don't have it in our header
ExtraData: hexutil.Bytes(head.Extra()),
Size: hexutil.Uint64(b.Size()),
@ -631,27 +614,10 @@ func NewBlockWithTxHash(
blk.Transactions = append(blk.Transactions, tx.Hash())
blk.EthTransactions = append(blk.EthTransactions, tx.ConvertToEth().Hash())
}
if blockArgs.InclStaking {
for _, stx := range b.StakingTransactions() {
blk.StakingTxs = append(blk.StakingTxs, stx.Hash())
}
}
if blockArgs.WithSigners {
blk.Signers = blockArgs.Signers
}
return blk, nil
return blk
}
// NewBlockWithFullTx ..
func NewBlockWithFullTx(
b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
) (*BlockWithFullTx, error) {
if !blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx")
}
func blockWithFullTxFromBlock(b *types.Block) (*BlockWithFullTx, error) {
head := b.Header()
vrfAndProof := head.Vrf()
@ -671,7 +637,6 @@ func NewBlockWithFullTx(
MixHash: head.MixDigest(),
LogsBloom: head.Bloom(),
StateRoot: head.Root(),
Miner: leader,
Difficulty: 0, // Remove this because we don't have it in our header
ExtraData: hexutil.Bytes(head.Extra()),
Size: hexutil.Uint64(b.Size()),
@ -694,20 +659,6 @@ func NewBlockWithFullTx(
}
blk.Transactions = append(blk.Transactions, fmtTx)
}
if blockArgs.InclStaking {
for _, stx := range b.StakingTransactions() {
fmtStx, err := NewStakingTransactionFromBlockHash(b, stx.Hash())
if err != nil {
return nil, err
}
blk.StakingTxs = append(blk.StakingTxs, fmtStx)
}
}
if blockArgs.WithSigners {
blk.Signers = blockArgs.Signers
}
return blk, nil
}
@ -732,6 +683,20 @@ func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, e
return NewTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
}
// StakingTransactionsFromBlock return rpc staking transactions from a block
func StakingTransactionsFromBlock(b *types.Block) ([]*StakingTransaction, error) {
rawStakings := b.StakingTransactions()
rpcStakings := make([]*StakingTransaction, 0, len(rawStakings))
for idx, raw := range rawStakings {
rpcStk, err := NewStakingTransaction(raw, b.Hash(), b.NumberU64(), b.Time().Uint64(), uint64(idx))
if err != nil {
return nil, errors.Wrapf(err, "failed to parse staking transaction %v", raw.Hash())
}
rpcStakings = append(rpcStakings, rpcStk)
}
return rpcStakings, nil
}
// NewStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
func NewStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) (*StakingTransaction, error) {
for idx, tx := range b.StakingTransactions() {

@ -0,0 +1,68 @@
package v2
import (
"github.com/harmony-one/harmony/core/types"
rpc_common "github.com/harmony-one/harmony/rpc/common"
"github.com/pkg/errors"
)
// BlockFactory is the factory for v1 rpc block
type BlockFactory struct {
provider rpc_common.BlockDataProvider
}
// NewBlockFactory return the block factory with the given provider
func NewBlockFactory(provider rpc_common.BlockDataProvider) *BlockFactory {
return &BlockFactory{provider}
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func (fac *BlockFactory) NewBlock(b *types.Block, args *rpc_common.BlockArgs) (interface{}, error) {
if args.FullTx {
return fac.newBlockWithFullTx(b, args)
}
return fac.newBlockWithTxHash(b, args)
}
func (fac *BlockFactory) newBlockWithTxHash(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithTxHash, error) {
blk := blockWithTxHashFromBlock(b)
blk.Miner = fac.provider.GetLeader(b)
if args.InclStaking {
blk.StakingTxs = fac.provider.GetStakingTxHashes(b)
}
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}
func (fac *BlockFactory) newBlockWithFullTx(b *types.Block, args *rpc_common.BlockArgs) (*BlockWithFullTx, error) {
blk, err := blockWithFullTxFromBlock(b)
if err != nil {
return nil, errors.Wrap(err, "blockWithFullTxFromBlock")
}
blk.Miner = fac.provider.GetLeader(b)
if args.InclStaking {
txs, err := fac.provider.GetStakingTxs(b)
if err != nil {
return nil, errors.Wrap(err, "GetStakingTxs")
}
blk.StakingTxs = txs.([]*StakingTransaction)
}
if args.WithSigners {
signers, err := fac.provider.GetSigners(b)
if err != nil {
return nil, errors.Wrap(err, "GetSigners")
}
blk.Signers = signers
}
return blk, nil
}

@ -13,7 +13,6 @@ import (
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls"
internal_common "github.com/harmony-one/harmony/internal/common"
rpc_common "github.com/harmony-one/harmony/rpc/common"
staking "github.com/harmony-one/harmony/staking/types"
)
@ -589,24 +588,8 @@ func NewStakingTransaction(
return result, nil
}
// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leader string) (interface{}, error) {
if blockArgs.FullTx {
return NewBlockWithFullTx(b, blockArgs, leader)
}
return NewBlockWithTxHash(b, blockArgs, leader)
}
// NewBlockWithTxHash return a block with only the transaction hash that will serialize to the RPC representation
func NewBlockWithTxHash(
b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
) (*BlockWithTxHash, error) {
if blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash")
}
// blockWithTxHashFromBlock return a block with only the transaction hash that will serialize to the RPC representation
func blockWithTxHashFromBlock(b *types.Block) *BlockWithTxHash {
head := b.Header()
vrfAndProof := head.Vrf()
@ -626,7 +609,6 @@ func NewBlockWithTxHash(
MixHash: head.MixDigest(),
LogsBloom: head.Bloom(),
StateRoot: head.Root(),
Miner: leader,
Difficulty: 0, // Remove this because we don't have it in our header
ExtraData: hexutil.Bytes(head.Extra()),
Size: uint64(b.Size()),
@ -648,26 +630,11 @@ func NewBlockWithTxHash(
blk.EthTransactions = append(blk.EthTransactions, tx.ConvertToEth().Hash())
}
if blockArgs.InclStaking {
for _, stx := range b.StakingTransactions() {
blk.StakingTxs = append(blk.StakingTxs, stx.Hash())
}
}
if blockArgs.WithSigners {
blk.Signers = blockArgs.Signers
}
return blk, nil
return blk
}
// NewBlockWithFullTx return a block with the transaction that will serialize to the RPC representation
func NewBlockWithFullTx(
b *types.Block, blockArgs *rpc_common.BlockArgs, leader string,
) (*BlockWithFullTx, error) {
if !blockArgs.FullTx {
return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx")
}
func blockWithFullTxFromBlock(b *types.Block) (*BlockWithFullTx, error) {
head := b.Header()
vrfAndProof := head.Vrf()
@ -687,7 +654,6 @@ func NewBlockWithFullTx(
MixHash: head.MixDigest(),
LogsBloom: head.Bloom(),
StateRoot: head.Root(),
Miner: leader,
Difficulty: 0, // Remove this because we don't have it in our header
ExtraData: hexutil.Bytes(head.Extra()),
Size: uint64(b.Size()),
@ -710,20 +676,6 @@ func NewBlockWithFullTx(
}
blk.Transactions = append(blk.Transactions, fmtTx)
}
if blockArgs.InclStaking {
for _, stx := range b.StakingTransactions() {
fmtStx, err := NewStakingTransactionFromBlockHash(b, stx.Hash())
if err != nil {
return nil, err
}
blk.StakingTxs = append(blk.StakingTxs, fmtStx)
}
}
if blockArgs.WithSigners {
blk.Signers = blockArgs.Signers
}
return blk, nil
}
@ -748,6 +700,20 @@ func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, e
return NewTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index)
}
// StakingTransactionsFromBlock return rpc staking transactions from a block
func StakingTransactionsFromBlock(b *types.Block) ([]*StakingTransaction, error) {
rawStakings := b.StakingTransactions()
rpcStakings := make([]*StakingTransaction, 0, len(rawStakings))
for idx, raw := range rawStakings {
rpcStk, err := NewStakingTransaction(raw, b.Hash(), b.NumberU64(), b.Time().Uint64(), uint64(idx), true)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse staking transaction %v", raw.Hash())
}
rpcStakings = append(rpcStakings, rpcStk)
}
return rpcStakings, nil
}
// NewStakingTransactionFromBlockHash returns a staking transaction that will serialize to the RPC representation.
func NewStakingTransactionFromBlockHash(b *types.Block, hash common.Hash) (*StakingTransaction, error) {
for idx, tx := range b.StakingTransactions() {

Loading…
Cancel
Save