forked from privacy-scaling-explorations/zkevm-circuits
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GethClient and support for minimum required JSON-RPC endpoints by…
… bus-mapping. (privacy-scaling-explorations#171) * Add ProviderError * Add rpc module * Add ethrs-providers to deps and url & tokio to dev-deps * Add `GethQueries` and getBlockByHash fn * Change `GethQueries` by `GethClient` * Add `eth_getBlockByNumber` support * Update `serde` version to `1.0.130`. * Impl `Serialize` for `GethExecStep` & `GethExecStepInternal` Since the `json_rpc` lib that we use requires the response types to be: `Serialize + Deserialize`, it has been needed to impl `Serialize` for the types that were lacking it. - GethExecStepInternal now derives `Serialize`. - Implemented `From<GethExecStep> for GethExecStepInternal` which is used in the next point. - Implement `Serialize` for `GethExecStep` serializing it as a `GethExecStepInternal` using the previous `From` impl mentioned. * Add `ResultGethExecTrace` & `ResultGethExecStep` Geth return value for `debug_traceBlockByHash` and `debug_traceBlockByNumber` isn't the expected. While the expected format was to get from these calls `Vec<GethExecTrace>`, you are given a similar vector but that contains a `result` Json field that makes no sense previous to each trace. Therefore it has been needed to simulate this as a new type and derive `Serialize` and `Deserialize` for it so that the response from this call can be parsed from the JSON and turned into usable data structures. * Add support for debug_traceBlockByHash/Number Following @ed255 suggestions the following extra JSON-RPC methods have been provided support: - debug_traceBlockByHash - debug_traceBlockByNumber Resolves: privacy-scaling-explorations#23 * Fix clippy complaints * Fix review suggestions from @ed255 - Renames ResultGethExecTraces and ResultGethExecTrace. - Fix `From<GethExecStep` impl for `GethExecStepInternal`. Co-authored-by: Eduard S. <[email protected]>
- Loading branch information
Showing
5 changed files
with
257 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,13 +9,16 @@ authors = ["CPerezz <[email protected]>"] | |
ff = "0.11" | ||
pasta_curves = "0.1" | ||
itertools = "0.10" | ||
serde = {version = "1.0.127", features = ["derive"] } | ||
serde = {version = "1.0.130", features = ["derive"] } | ||
lazy_static = "1.4" | ||
serde_json = "1.0.66" | ||
hex = "0.4" | ||
geth-utils = { path = "../geth-utils" } | ||
web3 = {version = "0.17", default-features = false} | ||
uint = "0.9.1" | ||
ethers-providers = "0.5.5" | ||
|
||
[dev-dependencies] | ||
url = "2.2.2" | ||
tokio = { version = "1.13", features = ["macros"] } | ||
pretty_assertions = "1.0.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
//! Module which contains all the RPC calls that are needed at any point to query a Geth node in order to get a Block, Tx or Trace info. | ||
use crate::eth_types::{ | ||
Block, GethExecTrace, Hash, ResultGethExecTraces, Transaction, U64, | ||
}; | ||
use crate::Error; | ||
use ethers_providers::JsonRpcClient; | ||
|
||
/// Serialize a type. | ||
/// | ||
/// # Panics | ||
/// | ||
/// If the type returns an error during serialization. | ||
pub fn serialize<T: serde::Serialize>(t: &T) -> serde_json::Value { | ||
serde_json::to_value(t).expect("Types never fail to serialize.") | ||
} | ||
|
||
/// Struct used to define the input that you want to provide to the `eth_getBlockByNumber` call as it mixes numbers with string literals. | ||
#[derive(Debug)] | ||
pub enum BlockNumber { | ||
/// Specific block number | ||
Num(U64), | ||
/// Earliest block | ||
Earliest, | ||
/// Latest block | ||
Latest, | ||
/// Pending block | ||
Pending, | ||
} | ||
|
||
impl From<u64> for BlockNumber { | ||
fn from(num: u64) -> Self { | ||
BlockNumber::Num(U64::from(num)) | ||
} | ||
} | ||
|
||
impl BlockNumber { | ||
/// Serializes a BlockNumber as a [`Value`](serde_json::Value) to be able to throw it into a JSON-RPC request. | ||
pub fn serialize(self) -> serde_json::Value { | ||
match self { | ||
BlockNumber::Num(num) => serialize(&num), | ||
BlockNumber::Earliest => serialize(&"earliest"), | ||
BlockNumber::Latest => serialize(&"latest"), | ||
BlockNumber::Pending => serialize(&"pending"), | ||
} | ||
} | ||
} | ||
|
||
/// Placeholder structure designed to contain the methods that the BusMapping needs in order to enable Geth queries. | ||
pub struct GethClient<P: JsonRpcClient>(P); | ||
|
||
impl<P: JsonRpcClient> GethClient<P> { | ||
/// Generates a new `GethClient` instance. | ||
pub fn new(provider: P) -> Self { | ||
Self(provider) | ||
} | ||
|
||
/// Calls `eth_getBlockByHash` via JSON-RPC returning a [`Block`] returning all the block information including it's transaction's details. | ||
pub async fn get_block_by_hash( | ||
&self, | ||
hash: Hash, | ||
) -> Result<Block<Transaction>, Error> { | ||
let hash = serialize(&hash); | ||
let flag = serialize(&true); | ||
self.0 | ||
.request("eth_getBlockByHash", [hash, flag]) | ||
.await | ||
.map_err(|e| Error::JSONRpcError(e.into())) | ||
} | ||
|
||
/// Calls `eth_getBlockByNumber` via JSON-RPC returning a [`Block`] returning all the block information including it's transaction's details. | ||
pub async fn get_block_by_number( | ||
&self, | ||
block_num: BlockNumber, | ||
) -> Result<Block<Transaction>, Error> { | ||
let num = block_num.serialize(); | ||
let flag = serialize(&true); | ||
self.0 | ||
.request("eth_getBlockByNumber", [num, flag]) | ||
.await | ||
.map_err(|e| Error::JSONRpcError(e.into())) | ||
} | ||
|
||
/// Calls `debug_traceBlockByHash` via JSON-RPC returning a [`Vec<GethExecTrace>`] with each GethTrace corresponding to 1 transaction of the block. | ||
pub async fn trace_block_by_hash( | ||
&self, | ||
hash: Hash, | ||
) -> Result<Vec<GethExecTrace>, Error> { | ||
let hash = serialize(&hash); | ||
let resp: ResultGethExecTraces = self | ||
.0 | ||
.request("debug_traceBlockByHash", [hash]) | ||
.await | ||
.map_err(|e| Error::JSONRpcError(e.into()))?; | ||
Ok(resp.0.into_iter().map(|step| step.result).collect()) | ||
} | ||
|
||
/// Calls `debug_traceBlockByNumber` via JSON-RPC returning a [`Vec<GethExecTrace>`] with each GethTrace corresponding to 1 transaction of the block. | ||
pub async fn trace_block_by_number( | ||
&self, | ||
block_num: BlockNumber, | ||
) -> Result<Vec<GethExecTrace>, Error> { | ||
let num = block_num.serialize(); | ||
let resp: ResultGethExecTraces = self | ||
.0 | ||
.request("debug_traceBlockByNumber", [num]) | ||
.await | ||
.map_err(|e| Error::JSONRpcError(e.into()))?; | ||
Ok(resp.0.into_iter().map(|step| step.result).collect()) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod rpc_tests { | ||
use super::*; | ||
use ethers_providers::Http; | ||
use std::str::FromStr; | ||
use url::Url; | ||
|
||
// The test is ignored as the values used depend on the Geth instance used each time you run the tests. | ||
// And we can't assume that everyone will have a Geth client synced with mainnet to have unified "test-vectors". | ||
#[ignore] | ||
#[tokio::test] | ||
async fn test_get_block_by_hash() { | ||
let transport = Http::new(Url::parse("http://localhost:8545").unwrap()); | ||
|
||
let hash = Hash::from_str("0xe4f7aa19a76fcf31a6adff3b400300849e39dd84076765fb3af09d05ee9d787a").unwrap(); | ||
let prov = GethClient::new(transport); | ||
let block_by_hash = prov.get_block_by_hash(hash).await.unwrap(); | ||
assert!(hash == block_by_hash.hash.unwrap()); | ||
} | ||
|
||
// The test is ignored as the values used depend on the Geth instance used each time you run the tests. | ||
// And we can't assume that everyone will have a Geth client synced with mainnet to have unified "test-vectors". | ||
#[ignore] | ||
#[tokio::test] | ||
async fn test_get_block_by_number() { | ||
let transport = Http::new(Url::parse("http://localhost:8545").unwrap()); | ||
|
||
let hash = Hash::from_str("0xe4f7aa19a76fcf31a6adff3b400300849e39dd84076765fb3af09d05ee9d787a").unwrap(); | ||
let prov = GethClient::new(transport); | ||
let block_by_num_latest = | ||
prov.get_block_by_number(BlockNumber::Latest).await.unwrap(); | ||
assert!(hash == block_by_num_latest.hash.unwrap()); | ||
let block_by_num = prov.get_block_by_number(1u64.into()).await.unwrap(); | ||
assert!( | ||
block_by_num.transactions[0].hash | ||
== block_by_num_latest.transactions[0].hash | ||
); | ||
} | ||
|
||
// The test is ignored as the values used depend on the Geth instance used each time you run the tests. | ||
// And we can't assume that everyone will have a Geth client synced with mainnet to have unified "test-vectors". | ||
#[ignore] | ||
#[tokio::test] | ||
async fn test_trace_block_by_hash() { | ||
let transport = Http::new(Url::parse("http://localhost:8545").unwrap()); | ||
|
||
let hash = Hash::from_str("0xe2d191e9f663a3a950519eadeadbd614965b694a65a318a0b8f053f2d14261ff").unwrap(); | ||
let prov = GethClient::new(transport); | ||
let trace_by_hash = prov.trace_block_by_hash(hash).await.unwrap(); | ||
// Since we called in the test block the same transaction twice the len should be the same and != 0. | ||
assert!( | ||
trace_by_hash[0].struct_logs.len() | ||
== trace_by_hash[1].struct_logs.len() | ||
); | ||
assert!(!trace_by_hash[0].struct_logs.is_empty()); | ||
} | ||
|
||
// The test is ignored as the values used depend on the Geth instance used each time you run the tests. | ||
// And we can't assume that everyone will have a Geth client synced with mainnet to have unified "test-vectors". | ||
#[ignore] | ||
#[tokio::test] | ||
async fn test_trace_block_by_number() { | ||
let transport = Http::new(Url::parse("http://localhost:8545").unwrap()); | ||
let prov = GethClient::new(transport); | ||
let trace_by_hash = prov.trace_block_by_number(5.into()).await.unwrap(); | ||
// Since we called in the test block the same transaction twice the len should be the same and != 0. | ||
assert!( | ||
trace_by_hash[0].struct_logs.len() | ||
== trace_by_hash[1].struct_logs.len() | ||
); | ||
assert!(!trace_by_hash[0].struct_logs.is_empty()); | ||
} | ||
} |