Skip to content

Commit

Permalink
feat(rpc): remove #[serde(Serialize, Deserialize)] from our dtos …
Browse files Browse the repository at this point in the history
…in favor of our `SerializeForVersion` and `DeserializeForVersion` traits
  • Loading branch information
t00ts committed Dec 18, 2024
1 parent e163ef5 commit 180ed9c
Show file tree
Hide file tree
Showing 23 changed files with 1,832 additions and 499 deletions.
6 changes: 5 additions & 1 deletion crates/rpc/src/dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub trait DeserializeForVersion: Sized {
fn deserialize(value: Value) -> Result<Self, serde_json::Error>;
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Value {
data: serde_json::Value,
pub version: RpcVersion,
Expand All @@ -56,6 +56,10 @@ impl Value {
self.data.is_null()
}

pub fn json_value(&self) -> serde_json::Value {
self.data.clone()
}

pub fn deserialize<T: DeserializeForVersion>(self) -> Result<T, serde_json::Error> {
T::deserialize(self)
}
Expand Down
21 changes: 20 additions & 1 deletion crates/rpc/src/dto/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use pathfinder_common::{ContractAddress, L1TransactionHash};
use primitive_types::H256;
use primitive_types::{H160, H256};
use serde::de::Error;

use super::serialize::SerializeForVersion;
Expand Down Expand Up @@ -211,6 +211,12 @@ impl SerializeForVersion for BlockNumber {
}
}

impl DeserializeForVersion for u64 {
fn deserialize(value: Value) -> Result<Self, serde_json::Error> {
value.deserialize_serde()
}
}

impl SerializeForVersion for U64Hex {
fn serialize(&self, serializer: Serializer) -> Result<serialize::Ok, serialize::Error> {
serializer.serialize_str(&hex_str::bytes_to_hex_str_stripped(&self.0.to_be_bytes()))
Expand Down Expand Up @@ -257,6 +263,19 @@ impl DeserializeForVersion for H256 {
}
}

impl DeserializeForVersion for pathfinder_common::EthereumAddress {
fn deserialize(value: Value) -> Result<Self, serde_json::Error> {
let hex_str: String = value.deserialize_serde()?;
let bytes = hex_str::bytes_from_hex_str_stripped::<20>(&hex_str).map_err(|e| {
serde_json::Error::custom(format!(
"failed to parse hex string as ethereum address: {}",
e
))
})?;
Ok(Self(H160::from(bytes)))
}
}

impl SerializeForVersion for Address<'_> {
fn serialize(&self, serializer: Serializer) -> Result<serialize::Ok, serialize::Error> {
serializer.serialize(&Felt(&self.0 .0))
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/src/felt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl From<RpcFelt> for Felt {
/// This can be easily accomplished by marking a field with `#[serde_as(as =
/// "RpcFelt251")]`.
#[derive(serde::Serialize)]
pub struct RpcFelt251(RpcFelt);
pub struct RpcFelt251(pub RpcFelt);

mod serialization {
//! Blanket [serde::Serialize] and [serde_with::SerializeAs] implementations
Expand Down
4 changes: 3 additions & 1 deletion crates/rpc/src/jsonrpc/router/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,9 @@ async fn handle_request(
panic!("subscription id overflow");
}
Ok(Some(RpcResponse {
output: Ok(serde_json::to_value(subscription_id).unwrap()),
output: Ok(subscription_id
.serialize(serialize::Serializer::new(state.version))
.unwrap()),
id: req_id,
version: state.version,
}))
Expand Down
38 changes: 34 additions & 4 deletions crates/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ impl Default for SyncState {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize)]
pub(crate) struct SubscriptionId(pub u32);

impl SubscriptionId {
Expand All @@ -240,6 +240,22 @@ impl SubscriptionId {
}
}

impl crate::dto::serialize::SerializeForVersion for SubscriptionId {
fn serialize(
&self,
serializer: crate::dto::serialize::Serializer,
) -> Result<crate::dto::serialize::Ok, crate::dto::serialize::Error> {
serializer.serialize_u64(self.0 as u64)
}
}

impl crate::dto::DeserializeForVersion for SubscriptionId {
fn deserialize(value: crate::dto::Value) -> Result<Self, serde_json::Error> {
let id: u32 = value.deserialize_serde()?;
Ok(Self(id))
}
}

#[cfg(test)]
pub mod test_utils {
use std::collections::HashMap;
Expand Down Expand Up @@ -816,9 +832,12 @@ pub mod test_utils {

#[cfg(test)]
mod tests {
use dto::serialize::SerializeForVersion;
use dto::DeserializeForVersion;
use serde_json::json;

use super::*;
use crate::dto::serialize::Serializer;

#[test]
fn roundtrip_syncing() {
Expand All @@ -840,11 +859,22 @@ mod tests {
];

for (line, input, expected) in examples {
let parsed = serde_json::from_str::<Syncing>(input).unwrap();
let output = serde_json::to_string(&parsed).unwrap();
println!("input: {input}");

let parsed = Syncing::deserialize(crate::dto::Value::new(
serde_json::from_str(input).unwrap(),
RpcVersion::V07,
))
.unwrap();
let output = parsed.serialize(Serializer::new(RpcVersion::V07)).unwrap();

assert_eq!(parsed, expected, "example from line {line}");
assert_eq!(&output, input, "example from line {line}");

// Compare parsed JSON values instead of strings
let output_value: serde_json::Value =
serde_json::from_str(&output.to_string()).unwrap();
let input_value: serde_json::Value = serde_json::from_str(input).unwrap();
assert_eq!(output_value, input_value, "example from line {line}");
}
}

Expand Down
33 changes: 24 additions & 9 deletions crates/rpc/src/method/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,32 @@ impl From<CallError> for ApplicationError {
}
}

#[derive(serde::Deserialize, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
#[derive(Debug, PartialEq, Eq)]
pub struct Input {
pub request: FunctionCall,
pub block_id: BlockId,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
#[derive(Debug, PartialEq, Eq)]
pub struct FunctionCall {
pub contract_address: ContractAddress,
pub entry_point_selector: EntryPoint,
pub calldata: Vec<CallParam>,
}

// TODO: Not used yet, just an example for now.
impl crate::dto::DeserializeForVersion for FunctionCall {
fn deserialize(value: crate::dto::Value) -> Result<Self, serde_json::Error> {
value.deserialize_map(|value| {
Ok(Self {
contract_address: value.deserialize_serde("contract_address")?,
entry_point_selector: value.deserialize_serde("entry_point_selector")?,
calldata: value
.deserialize_array("calldata", crate::dto::Value::deserialize_serde)?,
})
})
}
}

impl crate::dto::DeserializeForVersion for Input {
fn deserialize(value: crate::dto::Value) -> Result<Self, serde_json::Error> {
value.deserialize_map(|value| {
Expand Down Expand Up @@ -181,15 +191,18 @@ mod tests {
use serde_json::json;

use super::*;
use crate::dto::DeserializeForVersion;

#[test]
fn positional_args() {
let positional = json!([
let positional_json = json!([
{ "contract_address": "0xabcde", "entry_point_selector": "0xee", "calldata": ["0x1234", "0x2345"] },
{ "block_hash": "0xbbbbbbbb" }
]);

let input = serde_json::from_value::<Input>(positional).unwrap();
let positional = crate::dto::Value::new(positional_json, crate::RpcVersion::V08);

let input = Input::deserialize(positional).unwrap();
let expected = Input {
request: FunctionCall {
contract_address: contract_address!("0xabcde"),
Expand All @@ -203,12 +216,14 @@ mod tests {

#[test]
fn named_args() {
let named = json!({
let named_json = json!({
"request": { "contract_address": "0xabcde", "entry_point_selector": "0xee", "calldata": ["0x1234", "0x2345"] },
"block_id": { "block_hash": "0xbbbbbbbb" }
});

let input = serde_json::from_value::<Input>(named).unwrap();
let named = crate::dto::Value::new(named_json, crate::RpcVersion::V08);

let input = Input::deserialize(named).unwrap();
let expected = Input {
request: FunctionCall {
contract_address: contract_address!("0xabcde"),
Expand Down
13 changes: 10 additions & 3 deletions crates/rpc/src/method/estimate_message_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl crate::dto::DeserializeForVersion for EstimateMessageFeeInput {
}
}

#[derive(serde::Deserialize, Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub struct MsgFromL1 {
pub from_address: EthereumAddress,
pub to_address: ContractAddress,
Expand All @@ -44,8 +44,15 @@ pub struct MsgFromL1 {

impl crate::dto::DeserializeForVersion for MsgFromL1 {
fn deserialize(value: crate::dto::Value) -> Result<Self, serde_json::Error> {
// TODO: Replace this when DeserializeForVersion is available project-wide
value.deserialize_serde()
value.deserialize_map(|value| {
Ok(Self {
from_address: value.deserialize("from_address")?,
to_address: value.deserialize("to_address").map(ContractAddress)?,
entry_point_selector: value.deserialize("entry_point_selector").map(EntryPoint)?,
payload: value
.deserialize_array("payload", |value| value.deserialize().map(CallParam))?,
})
})
}
}

Expand Down
2 changes: 0 additions & 2 deletions crates/rpc/src/method/get_block_with_receipts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ pub enum Output {
Pending(Arc<PendingBlock>),
}

#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Input {
pub block_id: BlockId,
}
Expand Down
2 changes: 0 additions & 2 deletions crates/rpc/src/method/get_block_with_tx_hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use crate::context::RpcContext;

crate::error::generate_rpc_error_subset!(Error: BlockNotFound);

#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Input {
pub block_id: BlockId,
}
Expand Down
2 changes: 0 additions & 2 deletions crates/rpc/src/method/get_block_with_txs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use crate::context::RpcContext;

crate::error::generate_rpc_error_subset!(Error: BlockNotFound);

#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Input {
pub block_id: BlockId,
}
Expand Down
32 changes: 29 additions & 3 deletions crates/rpc/src/method/get_messages_status.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anyhow::Context;
use pathfinder_common::{L1TransactionHash, TransactionHash};
use pathfinder_ethereum::EthereumApi;
use serde::{Deserialize, Serialize};

use crate::context::RpcContext;
use crate::method::get_transaction_status;
Expand All @@ -23,15 +22,42 @@ impl crate::dto::DeserializeForVersion for Input {
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(Clone, Debug)]
enum FinalityStatus {
Received,
Rejected,
AcceptedOnL2,
AcceptedOnL1,
}

impl crate::dto::serialize::SerializeForVersion for FinalityStatus {
fn serialize(
&self,
serializer: crate::dto::serialize::Serializer,
) -> Result<crate::dto::serialize::Ok, crate::dto::serialize::Error> {
let status_str = match self {
FinalityStatus::Received => "RECEIVED",
FinalityStatus::Rejected => "REJECTED",
FinalityStatus::AcceptedOnL2 => "ACCEPTED_ON_L2",
FinalityStatus::AcceptedOnL1 => "ACCEPTED_ON_L1",
};
serializer.serialize_str(status_str)
}
}

impl crate::dto::DeserializeForVersion for FinalityStatus {
fn deserialize(value: crate::dto::Value) -> Result<Self, serde_json::Error> {
let status_str: String = value.deserialize_serde()?;
match status_str.as_str() {
"RECEIVED" => Ok(Self::Received),
"REJECTED" => Ok(Self::Rejected),
"ACCEPTED_ON_L2" => Ok(Self::AcceptedOnL2),
"ACCEPTED_ON_L1" => Ok(Self::AcceptedOnL1),
_ => Err(serde::de::Error::custom("Invalid finality status")),
}
}
}

#[derive(Clone, Debug)]
pub struct L1HandlerTransactionStatus {
transaction_hash: TransactionHash,
Expand Down
Loading

0 comments on commit 180ed9c

Please sign in to comment.