From e5f8073d75f1f72975f1852777a852727c00a56d Mon Sep 17 00:00:00 2001
From: Thomas Jay Rush <jrush@quickblocks.io>
Date: Mon, 14 Sep 2020 02:59:07 -0400
Subject: [PATCH] 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>
---
 README.md                            |   9 +-
 cmd/rpcdaemon/README.md              | 166 +++++++++++++++++++++++++++
 cmd/rpcdaemon/Readme.md              |  41 -------
 cmd/rpcdaemon/commands/daemon.go     |   2 +
 cmd/rpcdaemon/commands/eth_api.go    |  39 +++++--
 cmd/rpcdaemon/commands/get_uncles.go |  83 ++++++++++++++
 6 files changed, 287 insertions(+), 53 deletions(-)
 create mode 100644 cmd/rpcdaemon/README.md
 delete mode 100644 cmd/rpcdaemon/Readme.md
 create mode 100644 cmd/rpcdaemon/commands/get_uncles.go

diff --git a/README.md b/README.md
index 83bf73dce2..c05de3cdb1 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/cmd/rpcdaemon/README.md b/cmd/rpcdaemon/README.md
new file mode 100644
index 0000000000..7963962c58
--- /dev/null
+++ b/cmd/rpcdaemon/README.md
@@ -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         |
diff --git a/cmd/rpcdaemon/Readme.md b/cmd/rpcdaemon/Readme.md
deleted file mode 100644
index 6d3c6976fd..0000000000
--- a/cmd/rpcdaemon/Readme.md
+++ /dev/null
@@ -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/)
\ No newline at end of file
diff --git a/cmd/rpcdaemon/commands/daemon.go b/cmd/rpcdaemon/commands/daemon.go
index 782c73821b..bf1561f1a3 100644
--- a/cmd/rpcdaemon/commands/daemon.go
+++ b/cmd/rpcdaemon/commands/daemon.go
@@ -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 {
diff --git a/cmd/rpcdaemon/commands/eth_api.go b/cmd/rpcdaemon/commands/eth_api.go
index 48b9c4bde0..86a410e36c 100644
--- a/cmd/rpcdaemon/commands/eth_api.go
+++ b/cmd/rpcdaemon/commands/eth_api.go
@@ -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
+}
diff --git a/cmd/rpcdaemon/commands/get_uncles.go b/cmd/rpcdaemon/commands/get_uncles.go
new file mode 100644
index 0000000000..4cc5697da3
--- /dev/null
+++ b/cmd/rpcdaemon/commands/get_uncles.go
@@ -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
+}
-- 
GitLab