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:
parent
10f74f818a
commit
8218c96205
|
@ -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_eq!(serde_json::to_value(data).expect("could not serialize data"), inp);
|
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);
|
||||||
|
} else {
|
||||||
|
unreachable!("Zero sized types must be denoted with MockParams::Zst")
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue