1084 various rpc fixes (#1111)

* Fixes issue #1110 - eth_getStorageAt returning inconsistent values

* Adds support for various eth_getUncle calls

* Adding a couple of comments

* Adding support for eth_getTransactionCount

* Cleaning up README's

* Cleaning up README for rpcdaemon

Co-authored-by: tjayrush <jrush@greathill.com>
This commit is contained in:
Thomas Jay Rush 2020-09-14 02:59:07 -04:00 committed by GitHub
parent 3a4eb3db3e
commit e5f8073d75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 287 additions and 53 deletions

View File

@ -125,7 +125,9 @@ Run RPC daemon
> ./build/bin/rpcdaemon --private.api.addr=localhost:9090
```
Supported JSON-RPC calls ([eth](./cmd/rpcdaemon/commands/eth_api.go), [debug](./cmd/rpcdaemon/commands/debug_api.go), [net](./cmd/rpcdaemon/commands/net_api.go)):
Supported JSON-RPC calls ([eth](./cmd/rpcdaemon/commands/eth_api.go), [debug](./cmd/rpcdaemon/commands/debug_api.go), [net](./cmd/rpcdaemon/commands/net_api.go), [web3](./cmd/rpcdaemon/commands/web3_api.go)):
For a more detailed status, [see this table](./cmd/rpcdaemon/README.md#rpc-implementation-status).
```
eth_coinbase
@ -138,6 +140,11 @@ eth_getBlockTransactionCountByHash
eth_getBlockTransactionCountByNumber
eth_getBalance
eth_getCode
eth_GetTransactionCount
eth_GetUncleByBlockNumberAndIndex
eth_GetUncleByBlockHashAndIndex
eth_GetUncleCountByBlockNumber
eth_GetUncleCountByBlockHash
eth_getLogs
eth_getStorageAt
eth_getTransactionReceipt

166
cmd/rpcdaemon/README.md Normal file
View File

@ -0,0 +1,166 @@
In turbo-geth RPC calls are extracted out of the main binary into a separate daemon.
This daemon can use both local or remote DBs. That means, that this RPC daemon
doesn't have to be running on the same machine as the main turbo-geth binary or
it can run from a snapshot of a database for read-only calls. [Docs](./cmd/rpcdaemon/Readme.md)
### Get started
**For local DB**
```
> make rpcdaemon
> ./build/bin/rpcdaemon --chaindata ~/Library/TurboGeth/tg/chaindata --http.api=eth,debug,net
```
**For remote DB**
Run turbo-geth in one terminal window
```
> ./build/bin/tg --private.api.addr=localhost:9090
```
Run RPC daemon
```
> ./build/bin/rpcdaemon --private.api.addr=localhost:9090
```
### Test
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:8545
```
It should return something like this (depending on how far your turbo-geth node has synced):
```
{"jsonrpc":"2.0","id":1,"result":823909}
```
### For Developers
**Code generation**: `go.mod` stores right version of generators, use `mage grpc` to install it and generate code.
`protoc` version not managed but recommended version is 3.x, [install instruction](https://grpc.io/docs/protoc-installation/)
### RPC Implementation Status
eth_getBalance
eth_getCode
eth_getTransactionCount
eth_getStorageAt
eth_call
When requests are made that act on the state of ethereum, the last default block parameter determines the height of the block.
The following options are possible for the defaultBlock parameter:
HEX String - an integer block number
String "earliest" for the earliest/genesis block
String "latest" - for the latest mined block
String "pending" - for the pending state/transactions
Curl Examples Explained
The curl options below might return a response where the node complains about the content type, this is because the --data option sets the content type to application/x-www-form-urlencoded . If your node does complain, manually set the header by placing -H “Content-Type: application/json” at the start of the call.
The examples also do not include the URL/IP & port combination which must be the last argument given to curl e.x. 127.0.0.1:8545
| Command | Available | Notes |
| --------------------------------------------- | --------- | ------------------------------------------------- |
| _**------------ Web3 ------------**_ | |
| web3_clientVersion | Y |
| web3_sha3 | Y |
| | |
| _**------------ Net ------------**_ | |
| net_listening | - |
| net_peerCount\* | Y | Returns a count of 25 as work continues on Sentry |
| net_version | Y |
| | |
| _**------------ System ------------**_ | |
| eth_blockNumber | Y |
| eth_chainID | - |
| eth_protocolVersion | - |
| eth_syncing | Y |
| eth_estimateGas | Y |
| eth_gasPrice | - |
| | |
| _**------------ Blocks/Uncles ------------**_ | |
| eth_getBlockByHash | Y |
| eth_getBlockByNumber | Y |
| eth_getBlockTransactionCountByHash | Y |
| eth_getBlockTransactionCountByNumber | Y |
| eth_getUncleByBlockHashAndIndex | Y |
| eth_getUncleByBlockNumberAndIndex | Y |
| eth_getUncleCountByBlockHash | Y |
| eth_getUncleCountByBlockNumber | Y |
| | |
| _**------------ Transactions ------------**_ | |
| eth_getTransactionByHash | Y |
| eth_getTransactionByBlockHashAndIndex | Y |
| eth_getTransactionByBlockNumberAndIndex | Y |
| eth_getTransactionReceipt | Y |
| eth_getLogs | Y |
| | |
| _**------------ State ------------**_ | |
| eth_getBalance | Y |
| eth_getCode | Y |
| eth_getStorageAt | Y |
| | |
| _**------------ Filters ------------**_ | |
| eth_newFilter | - |
| eth_newBlockFilter | - |
| eth_newPendingTransactionFilter | - |
| eth_getFilterChanges | - |
| eth_getFilterLogs | - |
| eth_uninstallFilter | - |
| | |
| _**------------ Accounts ------------**_ | |
| eth_accounts | |
| eth_call | Y |
| eth_getTransactionCount | Y |
| eth_sendRawTransaction | Y |
| eth_sendTransaction | - |
| eth_sign | - |
| eth_signTransaction | - |
| eth_signTypedData | - |
| | |
| _**------------ ????? ------------**_ | |
| eth_getProof | - |
| | |
| _**------------ Mining ------------**_ | |
| eth_mining | - |
| eth_coinbase | Y |
| eth_hashrate | - |
| eth_submitHashrate | - |
| eth_getWork | - |
| eth_submitWork | - |
| | |
| _**------------ Debug ------------**_ | |
| debug_accountRange | Y |
| debug_getModifiedAccountsByNumber | Y |
| debug_getModifiedAccountsByHash | Y |
| debug_storageRangeAt | Y |
| debug_traceTransaction | Y |
| | |
| _\*\*------------ trace_ ------------\*\*\_ | |
| trace_filter | Y |
| | |
| _\*\*------------ Retired ------------\*\*\_ | |
| eth_getCompilers | N |
| eth_compileLLL | N |
| eth_compileSolidity | N |
| eth_compileSerpent | N |
| | |
| db_putString | N |
| db_getString | N |
| db_putHex | N |
| db_getHex | N |
| | |
| shh_post | N |
| shh_version | N |
| shh_newIdentity | N |
| shh_hasIdentity | N |
| shh_newGroup | N |
| shh_addToGroup | N |
| shh_newFilter | N |
| shh_uninstallFilter | N |
| shh_getFilterChanges | N |
| shh_getMessages | N |

View File

@ -1,41 +0,0 @@
In turbo-geth RPC calls are extracted out of the main binary into a separate daemon.
This daemon can use both local or remote DBs. That means, that this RPC daemon
doesn't have to be running on the same machine as the main turbo-geth binary or
it can run from a snapshot of a database for read-only calls. [Docs](./cmd/rpcdaemon/Readme.md)
### Get started
**For local DB**
```
> make rpcdaemon
> ./build/bin/rpcdaemon --chaindata ~/Library/TurboGeth/tg/chaindata --http.api=eth,debug,net
```
**For remote DB**
Run turbo-geth in one terminal window
```
> ./build/bin/tg --private.api.addr=localhost:9090
```
Run RPC daemon
```
> ./build/bin/rpcdaemon --private.api.addr=localhost:9090
```
### Test
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:8545
````
It should return something like this (depending on how far your turbo-geth node has synced):
````
{"jsonrpc":"2.0","id":1,"result":823909}
````
### For Developers
**Code generation**: `go.mod` stores right version of generators, use `mage grpc` to install it and generate code.
`protoc` version not managed but recommended version is 3.*, [install instruction](https://grpc.io/docs/protoc-installation/)

View File

@ -63,6 +63,7 @@ func (api *APIImpl) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx
return response, err
}
// GetHeaderByNumber returns a block's header by number
func (api *APIImpl) GetHeaderByNumber(_ context.Context, number rpc.BlockNumber) (*types.Header, error) {
header := rawdb.ReadHeaderByNumber(api.dbReader, uint64(number.Int64()))
if header == nil {
@ -72,6 +73,7 @@ func (api *APIImpl) GetHeaderByNumber(_ context.Context, number rpc.BlockNumber)
return header, nil
}
// GetHeaderByHash returns a block's header by hash
func (api *APIImpl) GetHeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
header := rawdb.ReadHeaderByHash(api.dbReader, hash)
if header == nil {

View File

@ -40,6 +40,11 @@ type EthAPI interface {
GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, txIndex hexutil.Uint) (*RPCTransaction, error)
GetStorageAt(ctx context.Context, address common.Address, index string, blockNrOrHash rpc.BlockNumberOrHash) (string, error)
GetCode(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (hexutil.Bytes, error)
GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error)
GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error)
GetUncleByBlockHashAndIndex(ctx context.Context, hash common.Hash, index hexutil.Uint) (map[string]interface{}, error)
GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint
GetUncleCountByBlockHash(ctx context.Context, hash common.Hash) *hexutil.Uint
}
// APIImpl is implementation of the EthAPI interface based on remote Db access
@ -211,30 +216,27 @@ func (api *APIImpl) GetTransactionByBlockNumberAndIndex(ctx context.Context, blo
return newRPCTransaction(txs[txIndex], block.Hash(), block.NumberU64(), uint64(txIndex)), nil
}
// GetStorageAt returns a 32-byte long, zero-left-padded value at storage location 'index' of address 'address'. Returns '0x' if no value
func (api *APIImpl) GetStorageAt(ctx context.Context, address common.Address, index string, blockNrOrHash rpc.BlockNumberOrHash) (string, error) {
var empty []byte
blockNumber, _, err := rpchelper.GetBlockNumber(blockNrOrHash, api.dbReader)
if err != nil {
return "", err
return hexutil.Encode(common.LeftPadBytes(empty[:], 32)), err
}
reader := adapter.NewStateReader(api.db, blockNumber)
acc, err := reader.ReadAccountData(address)
if err != nil {
return "", err
}
if acc == nil {
return "", fmt.Errorf("account not found")
if acc == nil || err != nil {
return hexutil.Encode(common.LeftPadBytes(empty[:], 32)), err
}
location := common.HexToHash(index)
res, err := reader.ReadAccountStorage(address, acc.Incarnation, &location)
if err != nil {
return "", err
res = empty
}
return hexutil.Encode(res), nil
return hexutil.Encode(common.LeftPadBytes(res[:], 32)), err
}
// GetCode returns the code stored at the given address in the state for the given block number.
@ -255,3 +257,18 @@ func (api *APIImpl) GetCode(ctx context.Context, address common.Address, blockNr
}
return res, nil
}
// GetTransactionCount returns the number of transactions the given address has sent for the given block number
func (api *APIImpl) GetTransactionCount(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
blockNumber, _, err := rpchelper.GetBlockNumber(blockNrOrHash, api.dbReader)
if err != nil {
return nil, err
}
nonce := hexutil.Uint64(0)
reader := adapter.NewStateReader(api.db, blockNumber)
acc, err := reader.ReadAccountData(address)
if acc == nil || err != nil {
return &nonce, err
}
return (*hexutil.Uint64)(&acc.Nonce), err
}

View File

@ -0,0 +1,83 @@
package commands
import (
"context"
"fmt"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/hexutil"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/ledgerwatch/turbo-geth/rpc"
"github.com/ledgerwatch/turbo-geth/turbo/adapter/ethapi"
)
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (api *APIImpl) GetUncleByBlockNumberAndIndex(ctx context.Context, number rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) {
blockNum, err := getBlockNumber(number, api.dbReader)
if err != nil {
return nil, err
}
block := rawdb.ReadBlockByNumber(api.dbReader, blockNum)
if block == nil {
return nil, fmt.Errorf("block not found: %d", blockNum)
}
hash := block.Hash()
additionalFields := make(map[string]interface{})
additionalFields["totalDifficulty"] = (*hexutil.Big)(rawdb.ReadTd(api.dbReader, block.Hash(), blockNum))
uncles := block.Uncles()
if index >= hexutil.Uint(len(uncles)) {
log.Debug("Requested uncle not found", "number", block.Number(), "hash", hash, "index", index)
return nil, nil
}
uncle := types.NewBlockWithHeader(uncles[index])
return ethapi.RPCMarshalBlock(uncle, false, false, additionalFields)
}
// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true
// all transactions in the block are returned in full detail, otherwise only the transaction hash is returned.
func (api *APIImpl) GetUncleByBlockHashAndIndex(ctx context.Context, hash common.Hash, index hexutil.Uint) (map[string]interface{}, error) {
block := rawdb.ReadBlockByHash(api.dbReader, hash)
if block == nil {
return nil, fmt.Errorf("block not found: %x", hash)
}
number := block.NumberU64()
additionalFields := make(map[string]interface{})
additionalFields["totalDifficulty"] = (*hexutil.Big)(rawdb.ReadTd(api.dbReader, hash, number))
uncles := block.Uncles()
if index >= hexutil.Uint(len(uncles)) {
log.Debug("Requested uncle not found", "number", block.Number(), "hash", hash, "index", index)
return nil, nil
}
uncle := types.NewBlockWithHeader(uncles[index])
return ethapi.RPCMarshalBlock(uncle, false, false, additionalFields)
}
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
func (api *APIImpl) GetUncleCountByBlockNumber(ctx context.Context, number rpc.BlockNumber) *hexutil.Uint {
n := hexutil.Uint(0)
blockNum, err := getBlockNumber(number, api.dbReader)
if err != nil {
return &n
}
block := rawdb.ReadBlockByNumber(api.dbReader, blockNum)
if block != nil {
n = hexutil.Uint(len(block.Uncles()))
}
return &n
}
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
func (api *APIImpl) GetUncleCountByBlockHash(ctx context.Context, hash common.Hash) *hexutil.Uint {
n := hexutil.Uint(0)
block := rawdb.ReadBlockByHash(api.dbReader, hash)
if block != nil {
n = hexutil.Uint(len(block.Uncles()))
}
return &n
}