Merge pull request #215 from ledgerwatch/210_rpcdemon_getBlockByNumber

rpcdemon get block by number
This commit is contained in:
Alex Sharov 2019-12-03 10:35:11 +07:00 committed by GitHub
commit 4678b6b36f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 195 additions and 0 deletions

1
cmd/rpcdaemon/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.http

31
cmd/rpcdaemon/Readme.md Normal file
View File

@ -0,0 +1,31 @@
## Getting Started
In order to build and run turbo-geth node together with RPC daemon, you need to do the following:
1. Clone turbo-geth repo
2. Build it by running `make`
3. Start it (it will start syncing to the mainnet) like this:
````
./build/bin/geth --remote-db-listen-addr localhost:9999
````
4. Look out for this in the console/log file:
````
INFO [11-30|18:34:12.687] Remote DB interface listening on address=localhost:9999
````
5. In another terminal/tab, build RPC daemon:
````
make rpcdaemon
````
6. Run it:
````
./build/bin/rpcdaemon --rpcapi eth
````
By default, it will connect to the turbo-geth node on the `localhost:9999`, but this can be changed via command line parameters. Note that it does not matter in which order you start these two processes, RPC daemon will only try to connect to turbo-geth node when serving its first RPC request, and then it will reconnect if connection is lost (for example, if you restart turbo-geth)
7. Try `eth_blockNumber` call. In another console/tab, use `curl` to make RPC call:
````
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber", "params": [], "id":1}' localhost:854
````
8. It should return something like this (depending on how far your turbo-geth node has synced):
````
{"jsonrpc":"2.0","id":1,"result":823909}
````

View File

@ -1,9 +1,11 @@
package commands
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"math/big"
"net"
"os"
"os/signal"
@ -11,8 +13,12 @@ import (
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"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 +35,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 +96,162 @@ 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
additionalFields := make(map[string]interface{})
if err := api.db.View(func(tx *remote.Tx) error {
block = GetBlockByNumber(tx, uint64(number.Int64()))
additionalFields["totalDifficulty"] = ReadTd(tx, block.Hash(), uint64(number.Int64()))
return nil
}); err != nil {
api.db.Close()
api.db = nil
return nil, err
}
if block != nil {
response, err := api.rpcMarshalBlock(block, true, fullTx, additionalFields)
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, additional map[string]interface{}) (map[string]interface{}, error) {
fields, err := ethapi.RPCMarshalBlock(b, inclTx, fullTx)
if err != nil {
return nil, err
}
for k, v := range additional {
fields[k] = v
}
return fields, err
}
// ReadTd reimplemented rawdb.ReadTd
func ReadTd(tx *remote.Tx, hash common.Hash, number uint64) *hexutil.Big {
bucket := tx.Bucket(dbutils.HeaderPrefix)
if bucket == nil {
return nil
}
data := bucket.Get(dbutils.HeaderTDKey(number, hash))
if len(data) == 0 {
return nil
}
td := new(big.Int)
if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
return nil
}
return (*hexutil.Big)(td)
}
// 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)