mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-24 04:27:16 +00:00
Merge pull request #215 from ledgerwatch/210_rpcdemon_getBlockByNumber
rpcdemon get block by number
This commit is contained in:
commit
4678b6b36f
1
cmd/rpcdaemon/.gitignore
vendored
Normal file
1
cmd/rpcdaemon/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.http
|
31
cmd/rpcdaemon/Readme.md
Normal file
31
cmd/rpcdaemon/Readme.md
Normal 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}
|
||||
````
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user