initial implementation of eth_GetBlockByNumber, for now I did copy much functions from eth source because we may need max flexibility for benchmarks of rpcdaemon

This commit is contained in:
alex.sharov 2019-12-01 16:52:11 +07:00
parent f7b6b524fe
commit 570d3b79d7
2 changed files with 155 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package commands
import (
"bytes"
"context"
"encoding/binary"
"fmt"
@ -11,8 +12,11 @@ import (
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/ethdb/remote"
"github.com/ledgerwatch/turbo-geth/internal/ethapi"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/rlp"
"github.com/ledgerwatch/turbo-geth/rpc"
)
@ -29,6 +33,7 @@ func splitAndTrim(input string) []string {
// EthAPI is a collection of functions that are exposed in the
type EthAPI interface {
BlockNumber(ctx context.Context) (uint64, error)
GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error)
}
// APIImpl is implementation of the EthAPI interface based on remote Db access
@ -89,6 +94,138 @@ func (api *APIImpl) BlockNumber(ctx context.Context) (uint64, error) {
return blockNumber, nil
}
// GetBlockByNumber see https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getblockbynumber
// see internal/ethapi.PublicBlockChainAPI.GetBlockByNumber
func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
if err := api.ensureConnected(); err != nil {
return nil, err
}
var block *types.Block
if err := api.db.View(func(tx *remote.Tx) error {
block = GetBlockByNumber(tx, uint64(number.Int64()))
return nil
}); err != nil {
api.db.Close()
api.db = nil
return nil, err
}
fmt.Printf("Debug: %#v %#v %#v\n", block.Number(), block.Hash(), block.Size())
if block != nil {
response, err := api.rpcMarshalBlock(block, true, fullTx)
if err == nil && number == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
response[field] = nil
}
}
return response, err
}
return nil, nil
}
// rpcMarshalBlock reimplementation of ethapi.rpcMarshalBlock
func (api *APIImpl) rpcMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
fields, err := ethapi.RPCMarshalBlock(b, inclTx, fullTx)
if err != nil {
return nil, err
}
// TODO: implement .GetTd method and uncomment next line
//fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(b.Hash()))
return fields, err
}
// ReadCanonicalHash reimplementation of rawdb.ReadCanonicalHash
func ReadCanonicalHash(tx *remote.Tx, number uint64) common.Hash {
bucket := tx.Bucket(dbutils.HeaderPrefix)
if bucket == nil {
return common.Hash{}
//return fmt.Errorf("bucket %s not found", dbutils.HeaderPrefix)
}
data := bucket.Get(dbutils.HeaderHashKey(number))
if len(data) == 0 {
return common.Hash{}
}
return common.BytesToHash(data)
}
// GetBlockByNumber reimplementation of chain.GetBlockByNumber
func GetBlockByNumber(tx *remote.Tx, number uint64) *types.Block {
hash := ReadCanonicalHash(tx, number)
if hash == (common.Hash{}) {
return nil
}
return ReadBlock(tx, hash, number)
}
// ReadBlock reimplementation of rawdb.ReadBlock
func ReadBlock(tx *remote.Tx, hash common.Hash, number uint64) *types.Block {
header := ReadHeader(tx, hash, number)
if header == nil {
return nil
}
body := ReadBody(tx, hash, number)
if body == nil {
return nil
}
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
}
// ReadBlock reimplementation of rawdb.ReadBlock
func ReadHeaderRLP(tx *remote.Tx, hash common.Hash, number uint64) rlp.RawValue {
bucket := tx.Bucket(dbutils.HeaderPrefix)
if bucket == nil {
//return fmt.Errorf("bucket %s not found", dbutils.HeaderPrefix)
log.Error("Bucket not founc", "error", dbutils.HeaderPrefix)
return rlp.RawValue{}
}
return bucket.Get(dbutils.HeaderKey(number, hash))
}
// ReadHeader reimplementation of rawdb.ReadHeader
func ReadHeader(tx *remote.Tx, hash common.Hash, number uint64) *types.Header {
data := ReadHeaderRLP(tx, hash, number)
if len(data) == 0 {
return nil
}
header := new(types.Header)
if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
log.Error("Invalid block header RLP", "hash", hash, "err", err)
return nil
}
return header
}
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func ReadBodyRLP(tx *remote.Tx, hash common.Hash, number uint64) rlp.RawValue {
bucket := tx.Bucket(dbutils.BlockBodyPrefix)
if bucket == nil {
//return fmt.Errorf("bucket %s not found", dbutils.HeaderPrefix)
log.Error("Bucket not founc", "error", dbutils.BlockBodyPrefix)
return rlp.RawValue{}
}
return bucket.Get(dbutils.BlockBodyKey(number, hash))
}
// ReadBody reimplementation of rawdb.ReadBody
func ReadBody(tx *remote.Tx, hash common.Hash, number uint64) *types.Body {
data := ReadBodyRLP(tx, hash, number)
if len(data) == 0 {
return nil
}
body := new(types.Body)
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
log.Error("Invalid block body RLP", "hash", hash, "err", err)
return nil
}
// Post-processing
body.SendersToTxs()
return body
}
func daemon(cfg Config) {
vhosts := splitAndTrim(cfg.rpcVirtualHost)
cors := splitAndTrim(cfg.rpcCORSDomain)

18
cmd/rpcdaemon/test.http Normal file
View File

@ -0,0 +1,18 @@
###
# curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber", "params": ["0x1b4", true], "id":1}' localhost:8545
POST localhost:8545
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": [
"0x1b4",
true
],
"id": 1
}
###