Skip to content

Commit

Permalink
chore: use serde_json_pythoic for pythonic repr (#422)
Browse files Browse the repository at this point in the history
Switches to use the `serde_json_pythoic` fork of `serde_json`, which
supports a Pythonic json formatter out of the box. Also disables the
`std` feature of both crates since it's not needed.

This is a breaking change as the `serde_json` error type on class hash
computation is no longer exposed.
  • Loading branch information
xJonathanLEI committed Jul 1, 2023
1 parent c5cf196 commit fb6b8af
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 114 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion starknet-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ ethereum-types = "0.14.1"
flate2 = "1.0.25"
hex = "0.4.3"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = { version = "1.0.96", features = ["raw_value"] }
serde_json = { version = "1.0.96", default-features = false, features = ["alloc", "raw_value"] }
serde_json_pythonic = { version = "0.1.2", default-features = false, features = ["alloc", "raw_value"] }
serde_with = "2.3.2"
sha3 = "0.10.7"
thiserror = "1.0.40"
Expand Down
97 changes: 0 additions & 97 deletions starknet-core/src/serde/json/mod.rs

This file was deleted.

2 changes: 0 additions & 2 deletions starknet-core/src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ pub mod byte_array;
pub mod unsigned_field_element;

pub(crate) mod num_hex;

pub(crate) mod json;
17 changes: 13 additions & 4 deletions starknet-core/src/types/contract/legacy.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
crypto::compute_hash_on_elements,
serde::{json::to_string_pythonic, num_hex::u64 as u64_hex, unsigned_field_element::UfeHex},
serde::{num_hex::u64 as u64_hex, unsigned_field_element::UfeHex},
types::{
contract::{CompressProgramError, ComputeClassHashError},
contract::{CompressProgramError, ComputeClassHashError, JsonError},
CompressedLegacyContractClass, FieldElement, FunctionStateMutability,
LegacyContractAbiEntry, LegacyContractEntryPoint, LegacyEntryPointsByType,
LegacyEventAbiEntry, LegacyEventAbiType, LegacyFunctionAbiEntry, LegacyFunctionAbiType,
Expand All @@ -15,6 +15,7 @@ use flate2::{write::GzEncoder, Compression};
use serde::{
de::Error as DeError, ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json_pythonic::to_string_pythonic;
use serde_with::{serde_as, SerializeAs};
use std::{collections::BTreeMap, io::Write};

Expand Down Expand Up @@ -464,7 +465,11 @@ impl LegacyContractClass {
abi: &self.abi,
program: &self.program,
})
.map_err(ComputeClassHashError::Json)?;
.map_err(|err| {
ComputeClassHashError::Json(JsonError {
message: format!("{}", err),
})
})?;

Ok(starknet_keccak(serialized.as_bytes()))
}
Expand Down Expand Up @@ -514,7 +519,11 @@ impl LegacyProgram {
prime: &self.prime,
reference_manager: &self.reference_manager,
})
.map_err(CompressProgramError::Json)?;
.map_err(|err| {
CompressProgramError::Json(JsonError {
message: format!("{}", err),
})
})?;

// Use best compression level to optimize for payload size
let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::best());
Expand Down
25 changes: 19 additions & 6 deletions starknet-core/src/types/contract/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
use serde_json_pythonic::to_string_pythonic;
use serde_with::serde_as;
use starknet_crypto::{poseidon_hash_many, PoseidonHasher};

use crate::{
serde::{json::to_string_pythonic, unsigned_field_element::UfeHex},
serde::unsigned_field_element::UfeHex,
types::{EntryPointsByType, FieldElement, FlattenedSierraClass, SierraEntryPoint},
utils::{
cairo_short_string_to_felt, normalize_address, starknet_keccak, CairoShortStringToFeltError,
Expand Down Expand Up @@ -169,24 +170,34 @@ pub enum ComputeClassHashError {
#[error("invalid builtin name")]
InvalidBuiltinName,
#[error("json serialization error: {0}")]
Json(serde_json::Error),
Json(JsonError),
}

#[derive(Debug, thiserror::Error)]
pub enum CompressProgramError {
#[error("json serialization error: {0}")]
Json(serde_json::Error),
Json(JsonError),
#[error("compression io error: {0}")]
Io(std::io::Error),
}

#[derive(Debug, thiserror::Error)]
#[error("{message}")]
pub struct JsonError {
pub(crate) message: String,
}

impl SierraClass {
pub fn class_hash(&self) -> Result<FieldElement, ComputeClassHashError> {
// Technically we don't have to use the Pythonic JSON style here. Doing this just to align
// with the official `cairo-lang` CLI.
//
// TODO: add an `AbiFormatter` trait and let users choose which one to use.
let abi_str = to_string_pythonic(&self.abi).map_err(ComputeClassHashError::Json)?;
let abi_str = to_string_pythonic(&self.abi).map_err(|err| {
ComputeClassHashError::Json(JsonError {
message: format!("{}", err),
})
})?;

let mut hasher = PoseidonHasher::new();
hasher.update(PREFIX_CONTRACT_CLASS_V0_1_0);
Expand All @@ -209,8 +220,10 @@ impl SierraClass {
Ok(normalize_address(hasher.finalize()))
}

pub fn flatten(self) -> Result<FlattenedSierraClass, serde_json::Error> {
let abi = to_string_pythonic(&self.abi)?;
pub fn flatten(self) -> Result<FlattenedSierraClass, JsonError> {
let abi = to_string_pythonic(&self.abi).map_err(|err| JsonError {
message: format!("{}", err),
})?;

Ok(FlattenedSierraClass {
sierra_program: self.sierra_program,
Expand Down
16 changes: 12 additions & 4 deletions starknet-providers/src/sequencer/models/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ pub struct CompressedLegacyContractClass {
pub abi: Option<Vec<RawLegacyAbiEntry>>,
}

#[derive(Debug, thiserror::Error)]
pub enum DecompressProgramError {
#[error("json deserialization error: {0}")]
Json(serde_json::Error),
#[error("decompression io error: {0}")]
Io(std::io::Error),
}

// We need to manually implement this because `raw_value` doesn't work with `untagged`:
// https://github.com/serde-rs/serde/issues/1183
impl<'de> Deserialize<'de> for DeployedClass {
Expand All @@ -67,21 +75,21 @@ impl<'de> Deserialize<'de> for DeployedClass {
impl CompressedSierraClass {
pub fn from_flattened(
flattened_class: &FlattenedSierraClass,
) -> Result<Self, CompressProgramError> {
) -> Result<Self, DecompressProgramError> {
#[serde_as]
#[derive(Serialize)]
struct SierraProgram<'a>(#[serde_as(as = "Vec<UfeHex>")] &'a Vec<FieldElement>);

let program_json = serde_json::to_string(&SierraProgram(&flattened_class.sierra_program))
.map_err(CompressProgramError::Json)?;
.map_err(DecompressProgramError::Json)?;

// Use best compression level to optimize for payload size
let mut gzip_encoder = GzEncoder::new(Vec::new(), Compression::best());
gzip_encoder
.write_all(program_json.as_bytes())
.map_err(CompressProgramError::Io)?;
.map_err(DecompressProgramError::Io)?;

let compressed_program = gzip_encoder.finish().map_err(CompressProgramError::Io)?;
let compressed_program = gzip_encoder.finish().map_err(DecompressProgramError::Io)?;

Ok(CompressedSierraClass {
sierra_program: compressed_program,
Expand Down

0 comments on commit fb6b8af

Please sign in to comment.