From 65828e96773560f15742d1e50328bb24a04e025a Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 28 Oct 2024 16:40:33 +0200 Subject: [PATCH] Use hex for human-readable serde encoding and binary otherwise --- Cargo.lock | 12 +- crates/subspace-core-primitives/Cargo.toml | 2 + crates/subspace-core-primitives/src/hashes.rs | 59 +++++-- crates/subspace-core-primitives/src/lib.rs | 88 ++++++++++- .../subspace-core-primitives/src/objects.rs | 1 - crates/subspace-core-primitives/src/pieces.rs | 145 ++++++++++++++++-- .../src/pieces/serde.rs | 100 ------------ crates/subspace-core-primitives/src/pos.rs | 49 +++++- .../subspace-core-primitives/src/pos/serde.rs | 100 ------------ crates/subspace-core-primitives/src/pot.rs | 131 +++++++++++++++- .../subspace-core-primitives/src/sectors.rs | 2 +- .../subspace-core-primitives/src/segments.rs | 49 +++++- .../subspace-core-primitives/src/solutions.rs | 94 +++++++++++- 13 files changed, 575 insertions(+), 257 deletions(-) delete mode 100644 crates/subspace-core-primitives/src/pieces/serde.rs delete mode 100644 crates/subspace-core-primitives/src/pos/serde.rs diff --git a/Cargo.lock b/Cargo.lock index 53b3c329e94..7fbe2bd374c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -11040,6 +11040,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" version = "0.11.15" @@ -12461,6 +12470,7 @@ dependencies = [ "rayon", "scale-info", "serde", + "serde-big-array", "static_assertions", "uint 0.10.0", ] diff --git a/crates/subspace-core-primitives/Cargo.toml b/crates/subspace-core-primitives/Cargo.toml index dd4340cbb40..80f3efb0381 100644 --- a/crates/subspace-core-primitives/Cargo.toml +++ b/crates/subspace-core-primitives/Cargo.toml @@ -20,6 +20,7 @@ parity-scale-codec = { version = "3.6.12", default-features = false, features = rayon = { version = "1.10.0", optional = true } scale-info = { version = "2.11.2", default-features = false, features = ["derive"] } serde = { version = "1.0.110", optional = true, default-features = false, features = ["alloc", "derive"] } +serde-big-array = "0.5.1" static_assertions = "1.1.0" uint = { version = "0.10.0", default-features = false } @@ -39,6 +40,7 @@ parallel = [ ] serde = [ "dep:serde", + "bytes/serde", "hex/serde", ] std = [ diff --git a/crates/subspace-core-primitives/src/hashes.rs b/crates/subspace-core-primitives/src/hashes.rs index 29e702763e9..80b3c328519 100644 --- a/crates/subspace-core-primitives/src/hashes.rs +++ b/crates/subspace-core-primitives/src/hashes.rs @@ -19,11 +19,12 @@ use crate::ScalarBytes; use core::array::TryFromSliceError; use core::fmt; use derive_more::{AsMut, AsRef, Deref, DerefMut, From, Into}; -use hex::FromHex; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; /// BLAKE3 hash output transparent wrapper #[derive( @@ -46,9 +47,47 @@ use serde::{Deserialize, Serialize}; TypeInfo, MaxEncodedLen, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(transparent))] -pub struct Blake3Hash(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; Blake3Hash::SIZE]); +pub struct Blake3Hash([u8; Blake3Hash::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct Blake3HashBinary([u8; Blake3Hash::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct Blake3HashHex(#[serde(with = "hex")] [u8; Blake3Hash::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for Blake3Hash { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + Blake3HashHex(self.0).serialize(serializer) + } else { + Blake3HashBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Blake3Hash { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + Blake3HashHex::deserialize(deserializer)?.0 + } else { + Blake3HashBinary::deserialize(deserializer)?.0 + })) + } +} impl fmt::Debug for Blake3Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -70,18 +109,6 @@ impl AsMut<[u8]> for Blake3Hash { } } -impl FromHex for Blake3Hash { - type Error = hex::FromHexError; - - fn from_hex>(hex: T) -> Result { - let data = hex::decode(hex)? - .try_into() - .map_err(|_| hex::FromHexError::InvalidStringLength)?; - - Ok(Self(data)) - } -} - impl From<&[u8; Self::SIZE]> for Blake3Hash { #[inline] fn from(value: &[u8; Self::SIZE]) -> Self { diff --git a/crates/subspace-core-primitives/src/lib.rs b/crates/subspace-core-primitives/src/lib.rs index 1d786c34ed7..fa84ccadc6e 100644 --- a/crates/subspace-core-primitives/src/lib.rs +++ b/crates/subspace-core-primitives/src/lib.rs @@ -47,6 +47,8 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; use static_assertions::const_assert; // Refuse to compile on lower than 32-bit platforms @@ -71,8 +73,47 @@ pub const REWARD_SIGNING_CONTEXT: &[u8] = b"subspace_reward"; TypeInfo, MaxEncodedLen, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Randomness(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; Randomness::SIZE]); +pub struct Randomness([u8; Randomness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RandomnessBinary([u8; Randomness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RandomnessHex(#[serde(with = "hex")] [u8; Randomness::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for Randomness { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + RandomnessHex(self.0).serialize(serializer) + } else { + RandomnessBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Randomness { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + RandomnessHex::deserialize(deserializer)?.0 + } else { + RandomnessBinary::deserialize(deserializer)?.0 + })) + } +} impl AsRef<[u8]> for Randomness { #[inline] @@ -131,8 +172,47 @@ pub type BlockWeight = u128; From, Into, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PublicKey(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; PublicKey::SIZE]); +pub struct PublicKey([u8; PublicKey::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PublicKeyBinary([u8; PublicKey::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PublicKeyHex(#[serde(with = "hex")] [u8; PublicKey::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for PublicKey { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + PublicKeyHex(self.0).serialize(serializer) + } else { + PublicKeyBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for PublicKey { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + PublicKeyHex::deserialize(deserializer)?.0 + } else { + PublicKeyBinary::deserialize(deserializer)?.0 + })) + } +} impl fmt::Display for PublicKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/crates/subspace-core-primitives/src/objects.rs b/crates/subspace-core-primitives/src/objects.rs index 54b312f83b1..838b66f1b0b 100644 --- a/crates/subspace-core-primitives/src/objects.rs +++ b/crates/subspace-core-primitives/src/objects.rs @@ -38,7 +38,6 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct BlockObject { /// Object hash - #[cfg_attr(feature = "serde", serde(with = "hex"))] pub hash: Blake3Hash, /// Offset of object in the encoded block. pub offset: u32, diff --git a/crates/subspace-core-primitives/src/pieces.rs b/crates/subspace-core-primitives/src/pieces.rs index 51a0e8377dc..15a15648779 100644 --- a/crates/subspace-core-primitives/src/pieces.rs +++ b/crates/subspace-core-primitives/src/pieces.rs @@ -1,8 +1,5 @@ //! Pieces-related data structures. -#[cfg(feature = "serde")] -mod serde; - #[cfg(not(feature = "std"))] extern crate alloc; @@ -10,6 +7,8 @@ use crate::segments::{ArchivedHistorySegment, RecordedHistorySegment, SegmentInd use crate::ScalarBytes; #[cfg(feature = "serde")] use ::serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use ::serde::{Deserializer, Serializer}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; #[cfg(not(feature = "std"))] @@ -30,6 +29,8 @@ use parity_scale_codec::{Decode, Encode, EncodeLike, Input, MaxEncodedLen, Outpu use rayon::prelude::*; use scale_info::build::Fields; use scale_info::{Path, Type, TypeInfo}; +#[cfg(feature = "serde")] +use serde_big_array::BigArray; /// Piece index in consensus #[derive( @@ -607,11 +608,47 @@ impl Record { TypeInfo, MaxEncodedLen, )] -#[repr(transparent)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RecordCommitment( - #[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; RecordCommitment::SIZE], -); +pub struct RecordCommitment([u8; RecordCommitment::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RecordCommitmentBinary(#[serde(with = "BigArray")] [u8; RecordCommitment::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RecordCommitmentHex(#[serde(with = "hex")] [u8; RecordCommitment::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for RecordCommitment { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + RecordCommitmentHex(self.0).serialize(serializer) + } else { + RecordCommitmentBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RecordCommitment { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + RecordCommitmentHex::deserialize(deserializer)?.0 + } else { + RecordCommitmentBinary::deserialize(deserializer)?.0 + })) + } +} impl Default for RecordCommitment { #[inline] @@ -701,11 +738,47 @@ impl RecordCommitment { TypeInfo, MaxEncodedLen, )] -#[repr(transparent)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RecordWitness( - #[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; RecordWitness::SIZE], -); +pub struct RecordWitness([u8; RecordWitness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RecordWitnessBinary(#[serde(with = "BigArray")] [u8; RecordWitness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RecordWitnessHex(#[serde(with = "hex")] [u8; RecordWitness::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for RecordWitness { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + RecordWitnessHex(self.0).serialize(serializer) + } else { + RecordWitnessBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RecordWitness { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + RecordWitnessHex::deserialize(deserializer)?.0 + } else { + RecordWitnessBinary::deserialize(deserializer)?.0 + })) + } +} impl Default for RecordWitness { #[inline] @@ -901,6 +974,52 @@ impl TypeInfo for Piece { } } +#[cfg(feature = "serde")] +impl Serialize for Piece { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let bytes = match &self.0 { + CowBytes::Shared(bytes) => bytes.as_ref(), + CowBytes::Owned(bytes) => bytes.as_ref(), + }; + + if serializer.is_human_readable() { + hex::serde::serialize(bytes, serializer) + } else { + bytes.serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Piece { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = if deserializer.is_human_readable() { + hex::serde::deserialize::<_, Vec>(deserializer).and_then(|bytes| { + if bytes.len() == Piece::SIZE { + Ok(Bytes::from(bytes)) + } else { + Err(serde::de::Error::invalid_length( + bytes.len(), + &format!("Expected {} bytes", Piece::SIZE).as_str(), + )) + } + })? + } else { + Bytes::deserialize(deserializer)? + }; + + Ok(Piece(CowBytes::Shared(bytes))) + } +} + impl Default for Piece { #[inline] fn default() -> Self { diff --git a/crates/subspace-core-primitives/src/pieces/serde.rs b/crates/subspace-core-primitives/src/pieces/serde.rs deleted file mode 100644 index ed4161ea73a..00000000000 --- a/crates/subspace-core-primitives/src/pieces/serde.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::pieces::Piece; -use hex::{decode_to_slice, FromHex, FromHexError}; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - -impl FromHex for Piece { - type Error = FromHexError; - - fn from_hex>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() % 2 != 0 { - return Err(FromHexError::OddLength); - } - if hex.len() != 2 * Piece::SIZE { - return Err(FromHexError::InvalidStringLength); - } - - let mut out = Self::default(); - - decode_to_slice(hex, out.as_mut_slice())?; - - Ok(out.to_shared()) - } -} - -impl Serialize for Piece { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - Serializer::serialize_newtype_struct(serializer, "Piece", { - struct SerializeWith<'a> { - values: &'a [u8], - } - impl Serialize for SerializeWith<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - hex::serde::serialize(self.values, serializer) - } - } - &SerializeWith { - values: self.as_ref(), - } - }) - } -} - -impl<'de> Deserialize<'de> for Piece { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = Piece; - - fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - formatter.write_str("tuple struct Piece") - } - - #[inline] - fn visit_newtype_struct(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - hex::serde::deserialize(deserializer) - } - - #[inline] - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - struct DeserializeWith { - value: Piece, - } - impl<'de> Deserialize<'de> for DeserializeWith { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(DeserializeWith { - value: hex::serde::deserialize(deserializer)?, - }) - } - } - - de::SeqAccess::next_element::(&mut seq)? - .map(|wrap| wrap.value) - .ok_or(de::Error::invalid_length( - 0usize, - &"tuple struct Piece with 1 element", - )) - } - } - Deserializer::deserialize_newtype_struct(deserializer, "Piece", Visitor) - } -} diff --git a/crates/subspace-core-primitives/src/pos.rs b/crates/subspace-core-primitives/src/pos.rs index d1586ca8b9f..691003ede8f 100644 --- a/crates/subspace-core-primitives/src/pos.rs +++ b/crates/subspace-core-primitives/src/pos.rs @@ -1,12 +1,15 @@ //! Proof of space-related data structures. -#[cfg(feature = "serde")] -mod serde; - use crate::hashes::{blake3_hash, Blake3Hash}; use derive_more::{Deref, DerefMut, From, Into}; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; +#[cfg(feature = "serde")] +use serde_big_array::BigArray; /// Proof of space seed. #[derive(Debug, Copy, Clone, Eq, PartialEq, Deref, From, Into)] @@ -35,6 +38,46 @@ impl PosSeed { )] pub struct PosProof([u8; PosProof::SIZE]); +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PosProofBinary(#[serde(with = "BigArray")] [u8; PosProof::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PosProofHex(#[serde(with = "hex")] [u8; PosProof::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for PosProof { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + PosProofHex(self.0).serialize(serializer) + } else { + PosProofBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for PosProof { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + PosProofHex::deserialize(deserializer)?.0 + } else { + PosProofBinary::deserialize(deserializer)?.0 + })) + } +} + impl Default for PosProof { #[inline] fn default() -> Self { diff --git a/crates/subspace-core-primitives/src/pos/serde.rs b/crates/subspace-core-primitives/src/pos/serde.rs deleted file mode 100644 index f50d627a7fb..00000000000 --- a/crates/subspace-core-primitives/src/pos/serde.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::pos::PosProof; -use hex::{decode_to_slice, FromHex, FromHexError}; -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - -impl FromHex for PosProof { - type Error = FromHexError; - - fn from_hex>(hex: T) -> Result { - let hex = hex.as_ref(); - if hex.len() % 2 != 0 { - return Err(FromHexError::OddLength); - } - if hex.len() != 2 * PosProof::SIZE { - return Err(FromHexError::InvalidStringLength); - } - - let mut out = Self::default(); - - decode_to_slice(hex, out.as_mut_slice())?; - - Ok(out) - } -} - -impl Serialize for PosProof { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - Serializer::serialize_newtype_struct(serializer, "PosProof", { - struct SerializeWith<'a> { - values: &'a [u8], - } - impl Serialize for SerializeWith<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - hex::serde::serialize(self.values, serializer) - } - } - &SerializeWith { - values: self.as_ref(), - } - }) - } -} - -impl<'de> Deserialize<'de> for PosProof { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct Visitor; - - impl<'de> de::Visitor<'de> for Visitor { - type Value = PosProof; - - fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - formatter.write_str("tuple struct PosProof") - } - - #[inline] - fn visit_newtype_struct(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - hex::serde::deserialize(deserializer) - } - - #[inline] - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - struct DeserializeWith { - value: PosProof, - } - impl<'de> Deserialize<'de> for DeserializeWith { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Ok(DeserializeWith { - value: hex::serde::deserialize(deserializer)?, - }) - } - } - - de::SeqAccess::next_element::(&mut seq)? - .map(|wrap| wrap.value) - .ok_or(de::Error::invalid_length( - 0usize, - &"tuple struct PosProof with 1 element", - )) - } - } - Deserializer::deserialize_newtype_struct(deserializer, "PosProof", Visitor) - } -} diff --git a/crates/subspace-core-primitives/src/pot.rs b/crates/subspace-core-primitives/src/pot.rs index b972a1f5c74..cdbe703d4b2 100644 --- a/crates/subspace-core-primitives/src/pot.rs +++ b/crates/subspace-core-primitives/src/pot.rs @@ -10,6 +10,8 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; /// Proof of time key(input to the encryption). #[derive( @@ -29,8 +31,47 @@ use serde::{Deserialize, Serialize}; TypeInfo, MaxEncodedLen, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PotKey(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; Self::SIZE]); +pub struct PotKey([u8; Self::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotKeyBinary([u8; PotKey::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotKeyHex(#[serde(with = "hex")] [u8; PotKey::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for PotKey { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + PotKeyHex(self.0).serialize(serializer) + } else { + PotKeyBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for PotKey { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + PotKeyHex::deserialize(deserializer)?.0 + } else { + PotKeyBinary::deserialize(deserializer)?.0 + })) + } +} impl fmt::Display for PotKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -74,8 +115,47 @@ impl PotKey { TypeInfo, MaxEncodedLen, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PotSeed(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; Self::SIZE]); +pub struct PotSeed([u8; Self::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotSeedBinary([u8; PotSeed::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotSeedHex(#[serde(with = "hex")] [u8; PotSeed::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for PotSeed { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + PotSeedHex(self.0).serialize(serializer) + } else { + PotSeedBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for PotSeed { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + PotSeedHex::deserialize(deserializer)?.0 + } else { + PotSeedBinary::deserialize(deserializer)?.0 + })) + } +} impl fmt::Display for PotSeed { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -124,8 +204,47 @@ impl PotSeed { TypeInfo, MaxEncodedLen, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct PotOutput(#[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; Self::SIZE]); +pub struct PotOutput([u8; Self::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotOutputBinary([u8; PotOutput::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct PotOutputHex(#[serde(with = "hex")] [u8; PotOutput::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for PotOutput { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + PotOutputHex(self.0).serialize(serializer) + } else { + PotOutputBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for PotOutput { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + PotOutputHex::deserialize(deserializer)?.0 + } else { + PotOutputBinary::deserialize(deserializer)?.0 + })) + } +} impl fmt::Display for PotOutput { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/crates/subspace-core-primitives/src/sectors.rs b/crates/subspace-core-primitives/src/sectors.rs index b80f37ed5b7..79dd99b2468 100644 --- a/crates/subspace-core-primitives/src/sectors.rs +++ b/crates/subspace-core-primitives/src/sectors.rs @@ -45,7 +45,7 @@ impl SectorSlotChallenge { /// Data structure representing sector ID in farmer's plot #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Encode, Decode, TypeInfo)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct SectorId(#[cfg_attr(feature = "serde", serde(with = "hex"))] Blake3Hash); +pub struct SectorId(Blake3Hash); impl AsRef<[u8]> for SectorId { #[inline] diff --git a/crates/subspace-core-primitives/src/segments.rs b/crates/subspace-core-primitives/src/segments.rs index 3eb6c1a1a30..c259b59ad93 100644 --- a/crates/subspace-core-primitives/src/segments.rs +++ b/crates/subspace-core-primitives/src/segments.rs @@ -19,6 +19,10 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; +#[cfg(feature = "serde")] +use serde_big_array::BigArray; /// Segment index type. #[derive( @@ -146,10 +150,47 @@ impl SegmentIndex { MaxEncodedLen, )] #[repr(transparent)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct SegmentCommitment( - #[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; SegmentCommitment::SIZE], -); +pub struct SegmentCommitment([u8; SegmentCommitment::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct SegmentCommitmentBinary(#[serde(with = "BigArray")] [u8; SegmentCommitment::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct SegmentCommitmentHex(#[serde(with = "hex")] [u8; SegmentCommitment::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for SegmentCommitment { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + SegmentCommitmentHex(self.0).serialize(serializer) + } else { + SegmentCommitmentBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for SegmentCommitment { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + SegmentCommitmentHex::deserialize(deserializer)?.0 + } else { + SegmentCommitmentBinary::deserialize(deserializer)?.0 + })) + } +} impl Default for SegmentCommitment { #[inline] diff --git a/crates/subspace-core-primitives/src/solutions.rs b/crates/subspace-core-primitives/src/solutions.rs index c3af44e6e21..21d4a8465ed 100644 --- a/crates/subspace-core-primitives/src/solutions.rs +++ b/crates/subspace-core-primitives/src/solutions.rs @@ -12,6 +12,10 @@ use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] +use serde::{Deserializer, Serializer}; +#[cfg(feature = "serde")] +use serde_big_array::BigArray; use static_assertions::const_assert; // TODO: Add related methods to `SolutionRange`. @@ -75,10 +79,47 @@ const_assert!(solution_range_to_pieces(pieces_to_solution_range(5, (1, 6)), (1, From, Into, )] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RewardSignature( - #[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; RewardSignature::SIZE], -); +pub struct RewardSignature([u8; RewardSignature::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RewardSignatureBinary(#[serde(with = "BigArray")] [u8; RewardSignature::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct RewardSignatureHex(#[serde(with = "hex")] [u8; RewardSignature::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for RewardSignature { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + RewardSignatureHex(self.0).serialize(serializer) + } else { + RewardSignatureBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for RewardSignature { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + RewardSignatureHex::deserialize(deserializer)?.0 + } else { + RewardSignatureBinary::deserialize(deserializer)?.0 + })) + } +} impl AsRef<[u8]> for RewardSignature { #[inline] @@ -110,10 +151,47 @@ impl RewardSignature { MaxEncodedLen, )] #[repr(transparent)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct ChunkWitness( - #[cfg_attr(feature = "serde", serde(with = "hex"))] [u8; ChunkWitness::SIZE], -); +pub struct ChunkWitness([u8; ChunkWitness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct ChunkWitnessBinary(#[serde(with = "BigArray")] [u8; ChunkWitness::SIZE]); + +#[cfg(feature = "serde")] +#[derive(Serialize, Deserialize)] +#[serde(transparent)] +struct ChunkWitnessHex(#[serde(with = "hex")] [u8; ChunkWitness::SIZE]); + +#[cfg(feature = "serde")] +impl Serialize for ChunkWitness { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + ChunkWitnessHex(self.0).serialize(serializer) + } else { + ChunkWitnessBinary(self.0).serialize(serializer) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for ChunkWitness { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(Self(if deserializer.is_human_readable() { + ChunkWitnessHex::deserialize(deserializer)?.0 + } else { + ChunkWitnessBinary::deserialize(deserializer)?.0 + })) + } +} impl Default for ChunkWitness { #[inline]