fix(providers): QuorumProvider zero-parameter json Value handling (#1613)

* Correction to QuorumProvider json Value handling with zero-parameter cases

* Update to properly pass through when no parameters are specified
This commit is contained in:
Mattie Conover 2022-08-22 19:32:47 -04:00 committed by GitHub
parent 10f74f818a
commit 8218c96205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 15 deletions

View File

@ -10,10 +10,19 @@ use std::{
}; };
use thiserror::Error; use thiserror::Error;
/// Helper type that can be used to pass through the `params` value.
/// This is necessary because the wrapper provider is supposed to skip the `params` if it's of
/// size 0, see `crate::transports::common::Request`
#[derive(Debug)]
enum MockParams {
Value(Value),
Zst
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Mock transport used in test environments. /// Mock transport used in test environments.
pub struct MockProvider { pub struct MockProvider {
requests: Arc<Mutex<VecDeque<(String, Value)>>>, requests: Arc<Mutex<VecDeque<(String, MockParams)>>>,
responses: Arc<Mutex<VecDeque<Value>>>, responses: Arc<Mutex<VecDeque<Value>>>,
} }
@ -28,14 +37,19 @@ impl Default for MockProvider {
impl JsonRpcClient for MockProvider { impl JsonRpcClient for MockProvider {
type Error = MockError; type Error = MockError;
/// Pushes the `(method, input)` to the back of the `requests` queue, /// Pushes the `(method, params)` to the back of the `requests` queue,
/// pops the responses from the back of the `responses` queue /// pops the responses from the back of the `responses` queue
async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>( async fn request<T: Serialize + Send + Sync, R: DeserializeOwned>(
&self, &self,
method: &str, method: &str,
input: T, params: T,
) -> Result<R, MockError> { ) -> Result<R, MockError> {
self.requests.lock().unwrap().push_back((method.to_owned(), serde_json::to_value(input)?)); let params = if std::mem::size_of::<T>() == 0 {
MockParams::Zst
} else {
MockParams::Value(serde_json::to_value(params)?)
};
self.requests.lock().unwrap().push_back((method.to_owned(), params));
let mut data = self.responses.lock().unwrap(); let mut data = self.responses.lock().unwrap();
let element = data.pop_back().ok_or(MockError::EmptyResponses)?; let element = data.pop_back().ok_or(MockError::EmptyResponses)?;
let res: R = serde_json::from_value(element)?; let res: R = serde_json::from_value(element)?;
@ -53,7 +67,15 @@ impl MockProvider {
) -> Result<(), MockError> { ) -> Result<(), MockError> {
let (m, inp) = self.requests.lock().unwrap().pop_front().ok_or(MockError::EmptyRequests)?; let (m, inp) = self.requests.lock().unwrap().pop_front().ok_or(MockError::EmptyRequests)?;
assert_eq!(m, method); assert_eq!(m, method);
assert!(!matches!(inp, MockParams::Value(serde_json::Value::Null)));
if std::mem::size_of::<T>() == 0 {
assert!(matches!(inp, MockParams::Zst));
} else if let MockParams::Value(inp) = inp {
assert_eq!(serde_json::to_value(data).expect("could not serialize data"), inp); assert_eq!(serde_json::to_value(data).expect("could not serialize data"), inp);
} else {
unreachable!("Zero sized types must be denoted with MockParams::Zst")
}
Ok(()) Ok(())
} }

View File

@ -1,5 +1,4 @@
use std::{ use std::{
fmt,
fmt::Debug, fmt::Debug,
future::Future, future::Future,
pin::Pin, pin::Pin,
@ -166,7 +165,7 @@ impl<T: JsonRpcClientWrapper> QuorumProvider<T> {
/// This is the minimum of all provider's block numbers /// This is the minimum of all provider's block numbers
async fn get_minimum_block_number(&self) -> Result<U64, ProviderError> { async fn get_minimum_block_number(&self) -> Result<U64, ProviderError> {
let mut numbers = join_all(self.providers.iter().map(|provider| async move { let mut numbers = join_all(self.providers.iter().map(|provider| async move {
let block = provider.inner.request("eth_blockNumber", serde_json::json!(())).await?; let block = provider.inner.request("eth_blockNumber", QuorumParams::Zst).await?;
serde_json::from_value::<U64>(block).map_err(ProviderError::from) serde_json::from_value::<U64>(block).map_err(ProviderError::from)
})) }))
.await .await
@ -181,7 +180,13 @@ impl<T: JsonRpcClientWrapper> QuorumProvider<T> {
} }
/// Normalizes the request payload depending on the call /// Normalizes the request payload depending on the call
async fn normalize_request(&self, method: &str, params: &mut Value) { async fn normalize_request(&self, method: &str, q_params: &mut QuorumParams) {
let params = if let QuorumParams::Value(v) = q_params {
v
} else {
// at this time no normalization is required for calls with zero parameters.
return
};
match method { match method {
"eth_call" | "eth_call" |
"eth_createAccessList" | "eth_createAccessList" |
@ -364,8 +369,8 @@ impl From<QuorumError> for ProviderError {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait JsonRpcClientWrapper: Send + Sync + fmt::Debug { pub trait JsonRpcClientWrapper: Send + Sync + Debug {
async fn request(&self, method: &str, params: Value) -> Result<Value, ProviderError>; async fn request(&self, method: &str, params: QuorumParams) -> Result<Value, ProviderError>;
} }
type NotificationStream = type NotificationStream =
Box<dyn futures_core::Stream<Item = Box<RawValue>> + Send + Unpin + 'static>; Box<dyn futures_core::Stream<Item = Box<RawValue>> + Send + Unpin + 'static>;
@ -381,14 +386,20 @@ pub trait PubsubClientWrapper: JsonRpcClientWrapper {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C: JsonRpcClient> JsonRpcClientWrapper for C { impl<C: JsonRpcClient> JsonRpcClientWrapper for C {
async fn request(&self, method: &str, params: Value) -> Result<Value, ProviderError> { async fn request(&self, method: &str, params: QuorumParams) -> Result<Value, ProviderError> {
Ok(JsonRpcClient::request(self, method, params).await.map_err(C::Error::into)?) let fut = if let QuorumParams::Value(params) = params {
JsonRpcClient::request(self, method, params)
} else {
JsonRpcClient::request(self, method, ())
};
Ok(fut.await.map_err(C::Error::into)?)
} }
} }
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl JsonRpcClientWrapper for Box<dyn JsonRpcClientWrapper> { impl JsonRpcClientWrapper for Box<dyn JsonRpcClientWrapper> {
async fn request(&self, method: &str, params: Value) -> Result<Value, ProviderError> { async fn request(&self, method: &str, params: QuorumParams) -> Result<Value, ProviderError> {
self.as_ref().request(method, params).await self.as_ref().request(method, params).await
} }
} }
@ -396,7 +407,7 @@ impl JsonRpcClientWrapper for Box<dyn JsonRpcClientWrapper> {
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl JsonRpcClientWrapper for Box<dyn PubsubClientWrapper> { impl JsonRpcClientWrapper for Box<dyn PubsubClientWrapper> {
async fn request(&self, method: &str, params: Value) -> Result<Value, ProviderError> { async fn request(&self, method: &str, params: QuorumParams) -> Result<Value, ProviderError> {
self.as_ref().request(method, params).await self.as_ref().request(method, params).await
} }
} }
@ -437,7 +448,12 @@ where
method: &str, method: &str,
params: T, params: T,
) -> Result<R, Self::Error> { ) -> Result<R, Self::Error> {
let mut params = serde_json::to_value(params)?; let mut params = if std::mem::size_of::<T>() == 0 {
// we don't want `()` to become `"null"`.
QuorumParams::Zst
} else {
QuorumParams::Value(serde_json::to_value(params)?)
};
self.normalize_request(method, &mut params).await; self.normalize_request(method, &mut params).await;
let requests = self let requests = self
@ -556,6 +572,15 @@ where
} }
} }
/// Helper type that can be used to pass through the `params` value.
/// This is necessary because the wrapper provider is supposed to skip the `params` if it's of
/// size 0, see `crate::transports::common::Request`
#[derive(Clone)]
pub enum QuorumParams {
Value(Value),
Zst
}
#[cfg(test)] #[cfg(test)]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
mod tests { mod tests {