web3-proxy/web3_proxy/src/jsonrpc.rs

416 lines
14 KiB
Rust
Raw Normal View History

use crate::errors::{Web3ProxyError, Web3ProxyResult};
use crate::response_cache::JsonRpcResponseEnum;
use derive_more::From;
2023-04-11 01:01:36 +03:00
use ethers::prelude::ProviderError;
use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Serialize};
2022-12-24 04:32:58 +03:00
use serde_json::json;
use serde_json::value::{to_raw_value, RawValue};
use std::borrow::Cow;
2022-05-12 02:50:52 +03:00
use std::fmt;
2023-06-07 23:57:38 +03:00
use std::sync::Arc;
2022-05-12 02:50:52 +03:00
pub trait JsonRpcParams = Clone + fmt::Debug + serde::Serialize + Send + Sync + 'static;
pub trait JsonRpcResultData = serde::Serialize + serde::de::DeserializeOwned + fmt::Debug + Send;
// TODO: &str here instead of String should save a lot of allocations
// TODO: generic type for params?
#[derive(Clone, Deserialize, Serialize)]
2022-05-12 02:50:52 +03:00
pub struct JsonRpcRequest {
pub jsonrpc: String,
2022-07-22 22:30:39 +03:00
/// id could be a stricter type, but many rpcs do things against the spec
2022-05-12 02:50:52 +03:00
pub id: Box<RawValue>,
pub method: String,
/// TODO: skip serializing if serde_json::Value::Null
pub params: serde_json::Value,
2022-05-12 02:50:52 +03:00
}
#[derive(From)]
pub enum JsonRpcId {
None,
Number(u64),
String(String),
}
impl JsonRpcId {
pub fn to_raw_value(self) -> Box<RawValue> {
// TODO: is this a good way to do this? we should probably use references
match self {
Self::None => {
to_raw_value(&json!(None::<Option<()>>)).expect("null id should always work")
}
Self::Number(x) => {
serde_json::from_value(json!(x)).expect("number id should always work")
}
Self::String(x) => serde_json::from_str(&x).expect("string id should always work"),
}
}
}
impl JsonRpcRequest {
pub fn new(id: JsonRpcId, method: String, params: serde_json::Value) -> anyhow::Result<Self> {
let x = Self {
jsonrpc: "2.0".to_string(),
id: id.to_raw_value(),
method,
params,
};
Ok(x)
}
}
2022-05-12 02:50:52 +03:00
impl fmt::Debug for JsonRpcRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: the default formatter takes forever to write. this is too quiet though
2022-05-17 05:26:47 +03:00
// TODO: how should we include params in this? maybe just the length?
2022-05-12 02:50:52 +03:00
f.debug_struct("JsonRpcRequest")
.field("id", &self.id)
.field("method", &self.method)
.field("params", &self.params)
.finish()
2022-05-12 02:50:52 +03:00
}
}
/// Requests can come in multiple formats
#[derive(Debug, From)]
2022-05-12 02:50:52 +03:00
pub enum JsonRpcRequestEnum {
Batch(Vec<JsonRpcRequest>),
Single(JsonRpcRequest),
}
impl JsonRpcRequestEnum {
2023-06-13 07:51:08 +03:00
pub fn first_id(&self) -> Option<Box<RawValue>> {
match self {
2023-06-13 08:26:10 +03:00
Self::Batch(x) => x.first().map(|x| x.id.clone()),
2023-06-13 07:51:08 +03:00
Self::Single(x) => Some(x.id.clone()),
}
}
}
2022-05-12 02:50:52 +03:00
impl<'de> Deserialize<'de> for JsonRpcRequestEnum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
2022-05-12 02:50:52 +03:00
#[serde(field_identifier, rename_all = "lowercase")]
enum Field {
JsonRpc,
Id,
Method,
Params,
}
struct JsonRpcBatchVisitor;
impl<'de> Visitor<'de> for JsonRpcBatchVisitor {
type Value = JsonRpcRequestEnum;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("JsonRpcRequestEnum")
}
fn visit_seq<V>(self, mut seq: V) -> Result<JsonRpcRequestEnum, V::Error>
where
V: SeqAccess<'de>,
{
// TODO: what size should we use as the default?
let mut batch: Vec<JsonRpcRequest> =
Vec::with_capacity(seq.size_hint().unwrap_or(10));
while let Ok(Some(s)) = seq.next_element::<JsonRpcRequest>() {
batch.push(s);
}
Ok(JsonRpcRequestEnum::Batch(batch))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
// TODO: i feel like this should be easier
2022-08-11 00:29:50 +03:00
let mut jsonrpc = None;
2022-05-12 02:50:52 +03:00
let mut id = None;
let mut method = None;
let mut params = None;
while let Some(key) = map.next_key()? {
match key {
Field::JsonRpc => {
// throw away the value
2022-05-12 02:54:08 +03:00
// TODO: should we check that it's 2.0?
// TODO: how do we skip over this value entirely?
2022-08-11 00:29:50 +03:00
jsonrpc = Some(map.next_value()?);
2022-05-12 02:50:52 +03:00
}
Field::Id => {
if id.is_some() {
return Err(de::Error::duplicate_field("id"));
}
id = Some(map.next_value()?);
}
Field::Method => {
if method.is_some() {
return Err(de::Error::duplicate_field("method"));
}
method = Some(map.next_value()?);
}
Field::Params => {
if params.is_some() {
return Err(de::Error::duplicate_field("params"));
}
params = Some(map.next_value()?);
}
}
}
// some providers don't follow the spec and dont include the jsonrpc key
// i think "2.0" should be a fine default to handle these incompatible clones
let jsonrpc = jsonrpc.unwrap_or_else(|| "2.0".to_string());
// TODO: Errors returned by the try operator get shown in an ugly way
2022-05-12 02:50:52 +03:00
let id = id.ok_or_else(|| de::Error::missing_field("id"))?;
let method = method.ok_or_else(|| de::Error::missing_field("method"))?;
2022-05-12 06:54:42 +03:00
2022-08-11 00:29:50 +03:00
let single = JsonRpcRequest {
jsonrpc,
id,
method,
params: params.unwrap_or_default(),
2022-08-11 00:29:50 +03:00
};
2022-05-12 02:50:52 +03:00
Ok(JsonRpcRequestEnum::Single(single))
}
}
let batch_visitor = JsonRpcBatchVisitor {};
deserializer.deserialize_any(batch_visitor)
}
}
// TODO: impl Error on this?
2022-05-12 02:50:52 +03:00
/// All jsonrpc errors use this structure
2022-11-28 09:10:34 +03:00
#[derive(Debug, Deserialize, Serialize, Clone)]
2022-05-12 02:50:52 +03:00
pub struct JsonRpcErrorData {
/// The error code
pub code: i64,
/// The error message
pub message: Cow<'static, str>,
2022-05-12 02:50:52 +03:00
/// Additional data
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
impl From<&'static str> for JsonRpcErrorData {
fn from(value: &'static str) -> Self {
Self {
code: -32000,
message: value.into(),
data: None,
}
}
}
impl From<String> for JsonRpcErrorData {
fn from(value: String) -> Self {
Self {
code: -32000,
message: value.into(),
data: None,
}
}
}
2022-05-12 02:50:52 +03:00
/// A complete response
2022-12-24 04:32:58 +03:00
/// TODO: better Debug response
#[derive(Clone, Debug, Deserialize, Serialize)]
2022-05-12 02:50:52 +03:00
pub struct JsonRpcForwardedResponse {
// TODO: jsonrpc a &str?
pub jsonrpc: &'static str,
2022-05-12 02:50:52 +03:00
pub id: Box<RawValue>,
#[serde(skip_serializing_if = "Option::is_none")]
2023-06-07 23:57:38 +03:00
pub result: Option<Arc<RawValue>>,
2022-05-12 02:50:52 +03:00
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<JsonRpcErrorData>,
}
2022-11-20 01:05:51 +03:00
impl JsonRpcRequest {
pub fn num_bytes(&self) -> usize {
// TODO: not sure how to do this without wasting a ton of allocations
serde_json::to_string(self)
.expect("this should always be valid json")
.len()
}
}
2022-05-22 21:11:42 +03:00
impl JsonRpcForwardedResponse {
pub fn from_anyhow_error(
err: anyhow::Error,
code: Option<i64>,
id: Option<Box<RawValue>>,
) -> Self {
let message = format!("{:?}", err);
Self::from_string(message, code, id)
}
pub fn from_str(message: &str, code: Option<i64>, id: Option<Box<RawValue>>) -> Self {
Self::from_string(message.to_string(), code, id)
}
pub fn from_string(message: String, code: Option<i64>, id: Option<Box<RawValue>>) -> Self {
2022-08-06 04:17:25 +03:00
// TODO: this is too verbose. plenty of errors are valid, like users giving an invalid address. no need to log that
2022-09-07 01:55:17 +03:00
// TODO: can we somehow get the initial request here? if we put that into a tracing span, will things slow down a ton?
2022-05-29 20:28:41 +03:00
JsonRpcForwardedResponse {
jsonrpc: "2.0",
id: id.unwrap_or_default(),
2022-05-29 20:28:41 +03:00
result: None,
error: Some(JsonRpcErrorData {
code: code.unwrap_or(-32099),
message: message.into(),
// TODO: accept data as an argument
2022-05-29 20:28:41 +03:00
data: None,
}),
}
}
2023-06-07 23:57:38 +03:00
pub fn from_raw_response(result: Arc<RawValue>, id: Box<RawValue>) -> Self {
2022-05-29 04:23:58 +03:00
JsonRpcForwardedResponse {
jsonrpc: "2.0",
2022-05-29 04:23:58 +03:00
id,
// TODO: since we only use the result here, should that be all we return from try_send_request?
result: Some(result),
2022-05-29 04:23:58 +03:00
error: None,
}
}
pub fn from_value(result: serde_json::Value, id: Box<RawValue>) -> Self {
let partial_response = to_raw_value(&result).expect("Value to RawValue should always work");
2023-06-07 23:57:38 +03:00
// TODO: an Arc is a waste here. change JsonRpcForwardedResponse to take an enum?
let partial_response = partial_response.into();
JsonRpcForwardedResponse {
jsonrpc: "2.0",
id,
result: Some(partial_response),
error: None,
}
}
// TODO: delete this. its on JsonRpcErrorData
pub fn from_ethers_error(e: ProviderError, id: Box<RawValue>) -> Web3ProxyResult<Self> {
2022-05-22 21:11:42 +03:00
// TODO: move turning ClientError into json to a helper function?
let code;
let message: String;
let data;
match e {
2023-04-11 01:01:36 +03:00
ProviderError::JsonRpcClientError(err) => {
if let Some(err) = err.as_error_response() {
code = err.code;
message = err.message.clone();
data = err.data.clone();
} else if let Some(err) = err.as_serde_error() {
// this is not an rpc error. keep it as an error
return Err(Web3ProxyError::BadResponse(err.to_string().into()));
2022-06-04 00:45:44 +03:00
} else {
return Err(anyhow::anyhow!("unexpected ethers error! {:?}", err).into());
2022-05-22 21:11:42 +03:00
}
}
2022-06-04 00:45:44 +03:00
e => return Err(e.into()),
2022-05-22 21:11:42 +03:00
}
2022-06-04 00:45:44 +03:00
Ok(Self {
jsonrpc: "2.0",
2022-05-22 21:11:42 +03:00
id,
result: None,
error: Some(JsonRpcErrorData {
code,
message: message.into(),
2022-05-22 21:11:42 +03:00
data,
}),
2022-06-04 00:45:44 +03:00
})
2022-05-22 21:11:42 +03:00
}
2022-07-09 05:33:53 +03:00
pub fn try_from_response_result(
2023-06-07 23:57:38 +03:00
result: Result<Arc<RawValue>, ProviderError>,
2022-07-09 05:33:53 +03:00
id: Box<RawValue>,
) -> Web3ProxyResult<Self> {
2022-07-09 05:33:53 +03:00
match result {
Ok(response) => Ok(Self::from_raw_response(response, id)),
2022-07-09 05:33:53 +03:00
Err(e) => Self::from_ethers_error(e, id),
}
}
2022-11-20 01:05:51 +03:00
2023-06-07 23:57:38 +03:00
pub fn from_response_data(data: JsonRpcResponseEnum<Arc<RawValue>>, id: Box<RawValue>) -> Self {
match data {
JsonRpcResponseEnum::Result { value, .. } => Self::from_raw_response(value, id),
JsonRpcResponseEnum::RpcError {
error_data: value, ..
} => JsonRpcForwardedResponse {
jsonrpc: "2.0",
id,
result: None,
error: Some(value),
},
}
2022-11-20 01:05:51 +03:00
}
2022-05-22 21:11:42 +03:00
}
2022-05-12 02:50:52 +03:00
/// JSONRPC Responses can include one or many response objects.
#[derive(Clone, Debug, From, Serialize)]
2022-05-12 02:50:52 +03:00
#[serde(untagged)]
pub enum JsonRpcForwardedResponseEnum {
Single(JsonRpcForwardedResponse),
Batch(Vec<JsonRpcForwardedResponse>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_deserialize_single() {
let input = r#"{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}"#;
// test deserializing it directly to a single request object
let output: JsonRpcRequest = serde_json::from_str(input).unwrap();
assert_eq!(output.id.to_string(), "1");
assert_eq!(output.method, "eth_blockNumber");
assert_eq!(output.params.to_string(), "[]");
2022-05-12 02:50:52 +03:00
// test deserializing it into an enum
let output: JsonRpcRequestEnum = serde_json::from_str(input).unwrap();
assert!(matches!(output, JsonRpcRequestEnum::Single(_)));
}
#[test]
fn this_deserialize_batch() {
let input = r#"[{"jsonrpc":"2.0","method":"eth_getCode","params":["0x5ba1e12693dc8f9c48aad8770482f4739beed696","0xe0e6a4"],"id":27},{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0x5ba1e12693dc8f9c48aad8770482f4739beed696","0xe0e6a4"],"id":28},{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x5ba1e12693dc8f9c48aad8770482f4739beed696","0xe0e6a4"],"id":29}]"#;
// test deserializing it directly to a batch of request objects
let output: Vec<JsonRpcRequest> = serde_json::from_str(input).unwrap();
assert_eq!(output.len(), 3);
assert_eq!(output[0].id.to_string(), "27");
assert_eq!(output[0].method, "eth_getCode");
assert_eq!(
output[0].params.to_string(),
2022-05-12 02:50:52 +03:00
r#"["0x5ba1e12693dc8f9c48aad8770482f4739beed696","0xe0e6a4"]"#
);
assert_eq!(output[1].id.to_string(), "28");
assert_eq!(output[2].id.to_string(), "29");
// test deserializing it into an enum
let output: JsonRpcRequestEnum = serde_json::from_str(input).unwrap();
assert!(matches!(output, JsonRpcRequestEnum::Batch(_)));
}
}