Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some types and enum variants were not properly json-serializing #4980

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions resources/test/sse_data_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2449,14 +2449,7 @@
],
"properties": {
"Purse": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"maxItems": 32,
"minItems": 32
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -3245,14 +3238,7 @@
],
"properties": {
"DelegatedPurse": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"maxItems": 32,
"minItems": 32
"type": "string"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -3937,12 +3923,7 @@
],
"properties": {
"RawBytes": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
"type": "string"
}
},
"additionalProperties": false
Expand Down
21 changes: 16 additions & 5 deletions types/src/stored_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub enum StoredValue {
EntryPoint(EntryPointValue),
/// Raw bytes. Similar to a [`crate::StoredValue::CLValue`] but does not incur overhead of a
/// [`crate::CLValue`] and [`crate::CLType`].
RawBytes(Vec<u8>),
RawBytes(#[cfg_attr(feature = "json-schema", schemars(with = "String"))] Vec<u8>),
}

impl StoredValue {
Expand Down Expand Up @@ -915,7 +915,7 @@ mod serde_helpers {
Reservation(&'a PrepaidKind),
EntryPoint(&'a EntryPointValue),
/// Raw bytes.
RawBytes(&'a Vec<u8>),
RawBytes(Bytes),
}

#[derive(Deserialize)]
Expand All @@ -940,7 +940,7 @@ mod serde_helpers {
NamedKey(NamedKeyValue),
EntryPoint(EntryPointValue),
/// Raw bytes.
RawBytes(Vec<u8>),
RawBytes(Bytes),
}

impl<'a> From<&'a StoredValue> for HumanReadableSerHelper<'a> {
Expand Down Expand Up @@ -976,7 +976,9 @@ mod serde_helpers {
StoredValue::NamedKey(payload) => HumanReadableSerHelper::NamedKey(payload),
StoredValue::Prepaid(payload) => HumanReadableSerHelper::Reservation(payload),
StoredValue::EntryPoint(payload) => HumanReadableSerHelper::EntryPoint(payload),
StoredValue::RawBytes(bytes) => HumanReadableSerHelper::RawBytes(bytes),
StoredValue::RawBytes(bytes) => {
HumanReadableSerHelper::RawBytes(bytes.as_slice().into())
}
}
}
}
Expand Down Expand Up @@ -1015,7 +1017,7 @@ mod serde_helpers {
}
HumanReadableDeserHelper::NamedKey(payload) => StoredValue::NamedKey(payload),
HumanReadableDeserHelper::EntryPoint(payload) => StoredValue::EntryPoint(payload),
HumanReadableDeserHelper::RawBytes(bytes) => StoredValue::RawBytes(bytes),
HumanReadableDeserHelper::RawBytes(bytes) => StoredValue::RawBytes(bytes.into()),
}
}
}
Expand Down Expand Up @@ -1113,6 +1115,15 @@ mod tests {
.contains("duplicate contract version: ContractVersionKey(1, 1)"));
}

#[test]
fn json_serialization_of_raw_bytes() {
let stored_value = StoredValue::RawBytes(vec![1, 2, 3, 4]);
assert_eq!(
serde_json::to_string(&stored_value).unwrap(),
r#"{"RawBytes":"01020304"}"#
);
}

proptest! {

#[test]
Expand Down
173 changes: 167 additions & 6 deletions types/src/system/auction/delegator_kind.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{
bytesrepr,
bytesrepr::{FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
CLType, CLTyped, PublicKey, URef, URefAddr,
checksummed_hex, CLType, CLTyped, PublicKey, URef, URefAddr,
};
use alloc::vec::Vec;
use alloc::{string::String, vec::Vec};
use core::{
fmt,
fmt::{Display, Formatter},
Expand All @@ -17,7 +17,8 @@ use rand::{
};
#[cfg(feature = "json-schema")]
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
use serde_helpers::{HumanReadableDelegatorKind, NonHumanReadableDelegatorKind};

/// DelegatorKindTag variants.
#[repr(u8)]
Expand All @@ -30,15 +31,15 @@ pub enum DelegatorKindTag {
}

/// Auction bid variants.
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, PartialOrd, Ord)]
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[cfg_attr(feature = "datasize", derive(DataSize))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
/// Kinds of delegation bids.
pub enum DelegatorKind {
/// Delegation from public key.
PublicKey(PublicKey),
/// Delegation from purse.
Purse(URefAddr),
Purse(#[cfg_attr(feature = "json-schema", schemars(with = "String"))] URefAddr),
}

impl DelegatorKind {
Expand Down Expand Up @@ -155,9 +156,169 @@ impl Distribution<DelegatorKind> for Standard {
}
}

impl Serialize for DelegatorKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
HumanReadableDelegatorKind::from(self).serialize(serializer)
} else {
NonHumanReadableDelegatorKind::from(self).serialize(serializer)
}
}
}

#[derive(Debug)]
enum DelegatorKindError {
DeserializationError(String),
}

impl Display for DelegatorKindError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
DelegatorKindError::DeserializationError(error) => {
write!(f, "Error when deserializing DelegatorKind: {}", error)
}
}
}
}

impl TryFrom<HumanReadableDelegatorKind> for DelegatorKind {
type Error = DelegatorKindError;

fn try_from(value: HumanReadableDelegatorKind) -> Result<Self, Self::Error> {
match value {
HumanReadableDelegatorKind::PublicKey(public_key) => {
Ok(DelegatorKind::PublicKey(public_key))
}
HumanReadableDelegatorKind::Purse(encoded) => {
let decoded = checksummed_hex::decode(encoded).map_err(|e| {
DelegatorKindError::DeserializationError(format!(
"Failed to decode encoded URefAddr: {}",
e
))
})?;
let uref_addr = URefAddr::try_from(decoded.as_ref()).map_err(|e| {
DelegatorKindError::DeserializationError(format!(
"Failed to build uref address: {}",
e
))
})?;
Ok(DelegatorKind::Purse(uref_addr))
}
}
}
}

impl From<NonHumanReadableDelegatorKind> for DelegatorKind {
fn from(value: NonHumanReadableDelegatorKind) -> Self {
match value {
NonHumanReadableDelegatorKind::PublicKey(public_key) => {
DelegatorKind::PublicKey(public_key)
}
NonHumanReadableDelegatorKind::Purse(addr) => DelegatorKind::Purse(addr),
}
}
}

impl<'de> Deserialize<'de> for DelegatorKind {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
if deserializer.is_human_readable() {
let human_readable = HumanReadableDelegatorKind::deserialize(deserializer)?;
DelegatorKind::try_from(human_readable)
.map_err(|error| SerdeError::custom(format!("{:?}", error)))
} else {
let non_human_readable = NonHumanReadableDelegatorKind::deserialize(deserializer)?;
Ok(DelegatorKind::from(non_human_readable))
}
}
}

mod serde_helpers {
use super::DelegatorKind;
use crate::{PublicKey, URefAddr};
use alloc::string::String;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub(super) enum HumanReadableDelegatorKind {
PublicKey(PublicKey),
Purse(String),
}

#[derive(Serialize, Deserialize)]
pub(super) enum NonHumanReadableDelegatorKind {
PublicKey(PublicKey),
Purse(URefAddr),
}

impl From<&DelegatorKind> for HumanReadableDelegatorKind {
fn from(delegator_kind: &DelegatorKind) -> Self {
match delegator_kind {
DelegatorKind::PublicKey(public_key) => {
HumanReadableDelegatorKind::PublicKey(public_key.clone())
}
DelegatorKind::Purse(uref_addr) => {
HumanReadableDelegatorKind::Purse(base16::encode_lower(uref_addr))
}
}
}
}

impl From<&DelegatorKind> for NonHumanReadableDelegatorKind {
fn from(delegator_kind: &DelegatorKind) -> Self {
match delegator_kind {
DelegatorKind::PublicKey(public_key) => {
NonHumanReadableDelegatorKind::PublicKey(public_key.clone())
}
DelegatorKind::Purse(uref_addr) => NonHumanReadableDelegatorKind::Purse(*uref_addr),
}
}
}
}

#[cfg(test)]
mod tests {
use crate::{bytesrepr, system::auction::delegator_kind::DelegatorKind, PublicKey, SecretKey};
use rand::Rng;

use crate::{
bytesrepr, system::auction::delegator_kind::DelegatorKind, testing::TestRng, PublicKey,
SecretKey,
};

#[test]
fn purse_serialized_as_string() {
let delegator_kind_payload = DelegatorKind::Purse([1; 32]);
let serialized = serde_json::to_string(&delegator_kind_payload).unwrap();
assert_eq!(
serialized,
"{\"Purse\":\"0101010101010101010101010101010101010101010101010101010101010101\"}"
);
}

#[test]
fn given_broken_address_purse_deserialziation_fails() {
let failing =
"{\"Purse\":\"Z101010101010101010101010101010101010101010101010101010101010101\"}";
let ret = serde_json::from_str::<DelegatorKind>(failing);
assert!(ret.is_err());
let failing = "{\"Purse\":\"01010101010101010101010101010101010101010101010101010101\"}";
let ret = serde_json::from_str::<DelegatorKind>(failing);
assert!(ret.is_err());
}

#[test]
fn json_roundtrip() {
let rng = &mut TestRng::new();

let delegator_kind_payload = DelegatorKind::PublicKey(PublicKey::random(rng));
let json_string = serde_json::to_string_pretty(&delegator_kind_payload).unwrap();
let decoded: DelegatorKind = serde_json::from_str(&json_string).unwrap();
assert_eq!(decoded, delegator_kind_payload);

let delegator_kind_payload = DelegatorKind::Purse(rng.gen());
let json_string = serde_json::to_string_pretty(&delegator_kind_payload).unwrap();
let decoded: DelegatorKind = serde_json::from_str(&json_string).unwrap();
assert_eq!(decoded, delegator_kind_payload);
}

#[test]
fn serialization_roundtrip() {
Expand Down
Loading
Loading