Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

JSON-RPC Block Storage #508

Open
mslipper opened this issue Dec 28, 2018 · 1 comment
Open

JSON-RPC Block Storage #508

mslipper opened this issue Dec 28, 2018 · 1 comment
Labels

Comments

@mslipper
Copy link
Contributor

Hey all - as discussed two weeks ago, I've compiled a bunch of info around getting the data the RPC layer needs out of Tendermint.

Overview

Ethermint needs a way of querying Tendermint blocks and their associated metadata in order to surface Web3-compatible JSON-RPC responses. We've discussed a number of ways to do this in the past, and most recently converged on using Tendermint tags as a way of indexing data for access by the RPC layer. This document proposes a detailed implementation path for that solution. Note that sending transactions and managing account information is deliberately left out-of-scope since these are much larger discussions that do not block the solution proposed herein.

Architecture

We'll need to create some Ethermint-specific tags to support certain JSON-RPC methods, and persist additional data in some cases to support advanced features like bloom filters over logs. Below, see a list of tags we'll need to create:

  1. eth.blockhash: represents the Ethereum-style block hash to which a given transaction or block belongs.
  2. eth.txhash: represents the Ethereum-style transaction hash to which a given transaction belongs.
  3. eth.topics: represents an Ethereum log topic associated with a given transaction.

Below, see the lists of additional metadata we'll need to persist with each block and transaction.

Block Metadata Persistence

  1. logsBloom: A bloom filter of logs emitted in this block.
  2. hash: The Ethereum-style hash of the block.

Transaction Metadata Persistence

  1. blockHash: The Ethereum-style block hash of the transaction's block.
  2. hash: The Ethereum-style hash of the transaction.

Note: Ethereum draws a distinction between transactions and transaction receipts. In Geth, receipts are stored separately from transactions. Thus, this requires the following additional fields to be stored alongside each transaction to facilitate transaction receipt lookups:

  1. logs: An array of logs generated by the transaction.
  2. logsBloom: A bloom filter of the logs emitted in this transaction.

If we don't want to persist the Ethereum-style hashes with the blocks and transactions themselves, we can calculate them on-the-fly in the RPC layer.

Filters and Stateful RPC

Ethereum supports stateful RPC methods via a two-call flow:

  1. The user registers a filter via one of the eth_new*Filter methods, which returns a filter ID.
  2. The user calls eth_getFilter* with the filter ID to get the changes that have occurred since the previous call to eth_getFilter*.

Supporting filters of this kind won't require any changes to Tendermint. Instead, the Ethermint RPC layer will maintain a pointer to the last-filtered block number for each registered filter. When a request to eth_getFilter* is received, then the RPC layer will perform a lookup using the tags outlined above. For example, a filter defined with the following parameters:

params: [{
  "fromBlock": "0x1",
  "toBlock": "0x2",
  "address": "0x8888f1f195afa192cfee860698584c030f4c9db1",
  "topics": ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", null, ["0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc"]]
}]

would yield the following query to the Tendermint storage engine upon receipt of a getFilterChanges call:

eth.topics CONTAINS 0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b OR (eth.topics CONTAINS 0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b OR eth.topics CONTAINS 0x0000000000000000000000000aff3454fce5edbc8cca8697c15331677e6ebccc) AND block.height >= 1 AND block.height <= 2 AND tx.sender = 0x8888f1f195afa192cfee860698584c030f4c9db1

Note: The above query isn't canonical, it's here to illustrate the idea.

I'm concerned about the performance characteristics of running many CONTAINS operations - lots of Ethereum developers set fromBlock to 1 and toBlock to latest, which would precipitate a scan of the entire blockchain to find matching transactions. Furthermore, it only operates on substring matches, so we'd need to parse each returned block in Ethermint and manually perform log-index-based matching to be in full compliance with the JSON-RPC API. Here's the relevant info from the spec:

A note on specifying topic filters:

Topics are order-dependent. A transaction with a log with topics [A, B] will be matched by the following topic filters:

    [] "anything"
    [A] "A in first position (and anything after)"
    [null, B] "anything in first position AND B in second position (and anything after)"
    [A, B] "A in first position AND B in second position (and anything after)"
    [[A, B], [A, B]] "(A OR B) in first position AND (A OR B) in second position (and anything after)"

Index of RPC Methods

Method Tags Needed Notes
web3_clientVersion N/A Unrelated to Tendermint.
web3_sha3 N/A Unrelated to Tendermint.
net_version N/A Unrelated to Tendermint.
net_peerCount N/A Unrelated to Tendermint.
net_listening N/A Unrelated to Tendermint.
eth_protocolVersion N/A Unrelated to Tendermint.
eth_syncing N/A "The sync status object may need to be different depending on the details of Tendermint's sync protocol. However, the 'synced' result is simply a boolean, and can easily be derived from Tendermint's internal sync state."
eth_coinbase N/A "This can be a config. Since there's no mining on Ethermint, this method doesn't have any meaning."
eth_mining N/A Will always be false.
eth_hashrate N/A Will always be zero.
eth_gasPrice N/A "Geth calculates this using its internal 'Gas Price Oracle,' which takes a percentile of the last N blocks. We can do the same."
eth_accounts N/A "This can be returned by whatever Ethermint uses as its 'owned account' abstraction. If we want to force users to write their own wallet applications, this can be an empty array."
eth_blockNumber N/A "This is the block height, which Tendermint already caches for us."
eth_getBalance N/A "This is kept in the state trie, which is a separate discussion."
eth_getStorageAt N/A "This is kept in the state trie, which is a separate discussion."
eth_getTransactionCount N/A This can be calculated by counting the transactions returned by a query for the transaction's sender.
eth_getBlockTransactionCountByHash eth.blockhash This can be calculated by counting the number of transactions returned by a query for a given block hash.
eth_getBlockTransactionCountByNumber N/A This can be calculated by counting the number of transactions returned by a query for a given block number.
eth_getUncleCountByBlockHash N/A "Will always be zero, since Ethermint does not have the concept of uncles."
eth_getUncleCountByBlockNumber N/A "Will always be zero, since Ethermint does not have the concept of uncles."
eth_getCode N/A "This is kept in the state trie, which is a separate discussion."
eth_sign N/A This is calculated based on your unlocked accounts. Ethermint can do the same.
eth_sendTransaction N/A Out-of-scope for this discussion.
eth_sendRawTransaction N/A Out-of-scope for this discussion.
eth_call N/A Out-of-scope for this discussion.
eth_estimateGas N/A Out-of-scope for this discussion.
eth_getBlockByHash eth.blockhash "This can be calculated by querying for a given block, then all of its transactions."
eth_getBlockByNumber N/A "This can be calculated by querying for a given block, then all of its transactions."
eth_getTransactionByHash eth.txhash This can be calculated by querying for a given transaction by its Ethereum-style hash.
eth_getTransactionByBlockHashAndIndex eth.blockhash
eth_getTransactionbyBlockNumberAndIndex N/A
eth_getTransactionReceipt eth.txhash
eth_getUncleByBlockHashAndIndex N/A "Will always return null, since Ethermint has no concept of uncles."
eth_getUncleByBlockNumberAndIndex N/A "Will always return null, since Ethermint has no concept of uncles."
eth_newFilter N/A See note regarding filters.
eth_newBlockFilter N/A See note regarding filters.
eth_newPendingTransactionFilter N/A See note regarding filters.
eth_uninstallFilter N/A See note regarding filters.
eth_getFilterChanges N/A See note regarding filters.
eth_getFilterLogs N/A See note regarding filters.
eth_getLogs eth.topics See note regarding filters.
eth_getWork N/A May not be relevant to Ethermint.
eth_submitWork N/A Not relevant to Ethermint.
eth_submitHashrate N/A Not relevant to Ethermint.
eth_getProof N/A "Generated from the state trie, which is a separate discussion."
@alexanderbez
Copy link
Contributor

Great composition @mslipper! Let us discuss these in detail during our ethermint weekly. Namely, all the outstanding items. We should also discuss logs and receipts in particular.

/cc @cwgoes

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants