add GetHeaderByNumberRLPHex and GetProof to rpc (#3951)

* add GetHeaderByNumberRLPHex

* mod comment

* fix compile

* fix

* add space

* add GetProof
pull/3990/head
zhiqiangxu 3 years ago committed by Leo Chen
parent 31a3e129a4
commit ede115d81d
No known key found for this signature in database
GPG Key ID: 9A0417092EBC5E96
  1. 143
      rpc/blockchain.go
  2. 2
      rpc/metrics.go

@ -6,8 +6,12 @@ import (
"math/big"
"time"
"encoding/hex"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
@ -663,6 +667,145 @@ func (s *PublicBlockchainService) GetHeaderByNumber(
return nil, err
}
// Result structs for GetProof
type AccountResult struct {
Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"`
Balance *hexutil.Big `json:"balance"`
CodeHash common.Hash `json:"codeHash"`
Nonce hexutil.Uint64 `json:"nonce"`
StorageHash common.Hash `json:"storageHash"`
StorageProof []StorageResult `json:"storageProof"`
}
type StorageResult struct {
Key string `json:"key"`
Value *hexutil.Big `json:"value"`
Proof []string `json:"proof"`
}
// GetHeaderByNumberRLPHex returns block header at given number by `hex(rlp(header))`
func (s *PublicBlockchainService) GetProof(
ctx context.Context, address common.Address, storageKeys []string, blockNumber BlockNumber) (ret *AccountResult, err error) {
timer := DoMetricRPCRequest(GetProof)
defer DoRPCRequestDuration(GetProof, timer)
defer func() {
if ret == nil || err != nil {
DoMetricRPCQueryInfo(GetProof, FailedNumber)
}
}()
err = s.wait(ctx)
if err != nil {
return
}
// Process number based on version
blockNum := blockNumber.EthBlockNumber()
// Ensure valid block number
if s.version != Eth && isBlockGreaterThanLatest(s.hmy, blockNum) {
err = ErrRequestedBlockTooHigh
return
}
// Fetch Header
header, err := s.hmy.HeaderByNumber(ctx, blockNum)
if header == nil && err != nil {
return
}
state, err := s.hmy.BeaconChain.StateAt(header.Root())
if state == nil || err != nil {
return
}
storageTrie := state.StorageTrie(address)
storageHash := types.EmptyRootHash
codeHash := state.GetCodeHash(address)
storageProof := make([]StorageResult, len(storageKeys))
// if we have a storageTrie, (which means the account exists), we can update the storagehash
if storageTrie != nil {
storageHash = storageTrie.Hash()
} else {
// no storageTrie means the account does not exist, so the codeHash is the hash of an empty bytearray.
codeHash = crypto.Keccak256Hash(nil)
}
// create the proof for the storageKeys
for i, key := range storageKeys {
if storageTrie != nil {
proof, storageError := state.GetStorageProof(address, common.HexToHash(key))
if storageError != nil {
err = storageError
return
}
storageProof[i] = StorageResult{key, (*hexutil.Big)(state.GetState(address, common.HexToHash(key)).Big()), toHexSlice(proof)}
} else {
storageProof[i] = StorageResult{key, &hexutil.Big{}, []string{}}
}
}
// create the accountProof
accountProof, err := state.GetProof(address)
if err != nil {
return
}
ret, err = &AccountResult{
Address: address,
AccountProof: toHexSlice(accountProof),
Balance: (*hexutil.Big)(state.GetBalance(address)),
CodeHash: codeHash,
Nonce: hexutil.Uint64(state.GetNonce(address)),
StorageHash: storageHash,
StorageProof: storageProof,
}, state.Error()
return
}
// toHexSlice creates a slice of hex-strings based on []byte.
func toHexSlice(b [][]byte) []string {
r := make([]string, len(b))
for i := range b {
r[i] = hexutil.Encode(b[i])
}
return r
}
// GetHeaderByNumberRLPHex returns block header at given number by `hex(rlp(header))`
func (s *PublicBlockchainService) GetHeaderByNumberRLPHex(
ctx context.Context, blockNumber BlockNumber,
) (string, error) {
timer := DoMetricRPCRequest(GetHeaderByNumberRLPHex)
defer DoRPCRequestDuration(GetHeaderByNumberRLPHex, timer)
err := s.wait(ctx)
if err != nil {
DoMetricRPCQueryInfo(GetHeaderByNumberRLPHex, FailedNumber)
return "", err
}
// Process number based on version
blockNum := blockNumber.EthBlockNumber()
// Ensure valid block number
if s.version != Eth && isBlockGreaterThanLatest(s.hmy, blockNum) {
DoMetricRPCQueryInfo(GetHeaderByNumberRLPHex, FailedNumber)
return "", ErrRequestedBlockTooHigh
}
// Fetch Header
header, err := s.hmy.HeaderByNumber(ctx, blockNum)
if header != nil && err == nil {
// Response output is the same for all versions
val, _ := rlp.EncodeToBytes(header)
return hex.EncodeToString(val), nil
}
return "", err
}
// GetCurrentUtilityMetrics ..
func (s *PublicBlockchainService) GetCurrentUtilityMetrics(
ctx context.Context,

@ -19,6 +19,8 @@ const (
GetLatestChainHeaders = "GetLatestChainHeaders"
GetLastCrossLinks = "GetLastCrossLinks"
GetHeaderByNumber = "GetHeaderByNumber"
GetHeaderByNumberRLPHex = "GetHeaderByNumberRLPHex"
GetProof = "GetProof"
GetCurrentUtilityMetrics = "GetCurrentUtilityMetrics"
GetSuperCommittees = "GetSuperCommittees"
GetCurrentBadBlocks = "GetCurrentBadBlocks"

Loading…
Cancel
Save