diff --git a/frozen-abi/src/abi_example.rs b/frozen-abi/src/abi_example.rs index 980bb1d01..8c232acaf 100644 --- a/frozen-abi/src/abi_example.rs +++ b/frozen-abi/src/abi_example.rs @@ -613,6 +613,14 @@ impl AbiEnumVisitor for Result { } } +impl AbiEnumVisitor for str { + fn visit_for_abi(&self, digester: &mut AbiDigester) -> DigestResult { + info!("AbiEnumVisitor for str"); + "".serialize(digester.create_new()) + .map_err(DigestError::wrap_by_type::) + } +} + #[cfg(not(target_os = "solana"))] impl AbiExample for std::sync::OnceLock { fn example() -> Self { diff --git a/message/src/legacy.rs b/message/src/legacy.rs index f45cb2ee4..7e0af37f8 100644 --- a/message/src/legacy.rs +++ b/message/src/legacy.rs @@ -66,7 +66,7 @@ fn compile_instructions(ixs: &[Instruction], keys: &[Address]) -> Vec { - let mut seq = serializer.serialize_tuple(1)?; - seq.serialize_element(message)?; - seq.end() + if serializer.is_human_readable() { + #[derive(Serialize)] + #[serde(tag = "version")] + enum HR<'a> { + #[serde(rename = "legacy")] + Legacy { message: &'a LegacyMessage }, + #[serde(rename = "0")] + V0 { message: &'a v0::Message }, } - Self::V0(message) => { - let mut seq = serializer.serialize_tuple(2)?; - seq.serialize_element(&MESSAGE_VERSION_PREFIX)?; - seq.serialize_element(message)?; - seq.end() + + let hr = match self { + Self::Legacy(message) => HR::Legacy { message }, + Self::V0(message) => HR::V0 { message }, + }; + + HR::serialize(&hr, serializer) + } else { + match self { + Self::Legacy(message) => { + let mut seq = serializer.serialize_tuple(1)?; + seq.serialize_element(message)?; + seq.end() + } + Self::V0(message) => { + let mut seq = serializer.serialize_tuple(2)?; + seq.serialize_element(&MESSAGE_VERSION_PREFIX)?; + seq.serialize_element(message)?; + seq.end() + } } } } @@ -247,7 +265,6 @@ impl<'de> serde::Deserialize<'de> for VersionedMessage { D: Deserializer<'de>, { struct MessageVisitor; - impl<'de> Visitor<'de> for MessageVisitor { type Value = VersionedMessage; @@ -262,7 +279,6 @@ impl<'de> serde::Deserialize<'de> for VersionedMessage { let prefix: MessagePrefix = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; - match prefix { MessagePrefix::Legacy(num_required_signatures) => { // The remaining fields of the legacy Message struct after the first byte. @@ -322,7 +338,23 @@ impl<'de> serde::Deserialize<'de> for VersionedMessage { } } - deserializer.deserialize_tuple(2, MessageVisitor) + if deserializer.is_human_readable() { + #[derive(Deserialize)] + #[serde(tag = "version")] + enum HR { + #[serde(rename = "legacy")] + Legacy { message: LegacyMessage }, + #[serde(rename = "0")] + V0 { message: v0::Message }, + } + + match HR::deserialize(deserializer)? { + HR::Legacy { message } => Ok(VersionedMessage::Legacy(message)), + HR::V0 { message } => Ok(VersionedMessage::V0(message)), + } + } else { + deserializer.deserialize_tuple(2, MessageVisitor) + } } } @@ -334,6 +366,21 @@ mod tests { solana_instruction::{AccountMeta, Instruction}, }; + #[test] + fn test_versioned_message_human_readable_serialization() { + let message = LegacyMessage::default(); + let message_string = serde_json::to_string(&message).unwrap(); + let string = serde_json::to_string(&VersionedMessage::Legacy(message)).unwrap(); + let expected_string = format!(r#"{{"version":"legacy","message":{message_string}}}"#); + assert_eq!(string, expected_string); + + let message = v0::Message::default(); + let message_string = serde_json::to_string(&message).unwrap(); + let string = serde_json::to_string(&VersionedMessage::V0(message)).unwrap(); + let expected_string = format!(r#"{{"version":"0","message":{message_string}}}"#); + assert_eq!(string, expected_string); + } + #[test] fn test_legacy_message_serialization() { let program_id0 = Address::new_unique(); @@ -367,10 +414,10 @@ mod tests { assert_eq!(bytes, bincode::serialize(&wrapped_message).unwrap()); let message_from_bytes: LegacyMessage = bincode::deserialize(&bytes).unwrap(); + assert_eq!(message, message_from_bytes); + let wrapped_message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap(); - - assert_eq!(message, message_from_bytes); assert_eq!(wrapped_message, wrapped_message_from_bytes); } @@ -379,12 +426,17 @@ mod tests { let string = serde_json::to_string(&message).unwrap(); let message_from_string: LegacyMessage = serde_json::from_str(&string).unwrap(); assert_eq!(message, message_from_string); + + let wrapped_string = serde_json::to_string(&wrapped_message).unwrap(); + let wrapped_message_from_string: VersionedMessage = + serde_json::from_str(&wrapped_string).unwrap(); + assert_eq!(wrapped_message, wrapped_message_from_string); } } #[test] fn test_versioned_message_serialization() { - let message = VersionedMessage::V0(v0::Message { + let message = v0::Message { header: MessageHeader { num_required_signatures: 1, num_readonly_signed_accounts: 0, @@ -409,14 +461,31 @@ mod tests { accounts: vec![0, 2, 3, 4], data: vec![], }], - }); + }; + let wrapped_message = VersionedMessage::V0(message.clone()); - let bytes = bincode::serialize(&message).unwrap(); - let message_from_bytes: VersionedMessage = bincode::deserialize(&bytes).unwrap(); - assert_eq!(message, message_from_bytes); + // bincode + { + let bytes = bincode::serialize(&message).unwrap(); + let message_from_bytes: v0::Message = bincode::deserialize(&bytes).unwrap(); + assert_eq!(message, message_from_bytes); - let string = serde_json::to_string(&message).unwrap(); - let message_from_string: VersionedMessage = serde_json::from_str(&string).unwrap(); - assert_eq!(message, message_from_string); + let wrapped_bytes = bincode::serialize(&wrapped_message).unwrap(); + let wrapped_message_from_bytes: VersionedMessage = + bincode::deserialize(&wrapped_bytes).unwrap(); + assert_eq!(wrapped_message, wrapped_message_from_bytes); + } + + // serde_json + { + let string = serde_json::to_string(&message).unwrap(); + let message_from_string: v0::Message = serde_json::from_str(&string).unwrap(); + assert_eq!(message, message_from_string); + + let wrapped_string = serde_json::to_string(&wrapped_message).unwrap(); + let wrapped_message_from_string: VersionedMessage = + serde_json::from_str(&wrapped_string).unwrap(); + assert_eq!(wrapped_message, wrapped_message_from_string); + } } } diff --git a/message/src/versions/v0/mod.rs b/message/src/versions/v0/mod.rs index 960831541..feb49f886 100644 --- a/message/src/versions/v0/mod.rs +++ b/message/src/versions/v0/mod.rs @@ -13,7 +13,7 @@ pub use loaded::*; #[cfg(feature = "serde")] use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "frozen-abi")] -use solana_frozen_abi_macro::AbiExample; +use solana_frozen_abi_macro::{frozen_abi, AbiExample}; use { crate::{ compiled_instruction::CompiledInstruction, @@ -57,7 +57,11 @@ pub struct MessageAddressTableLookup { /// /// See the crate documentation for further description. /// -#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] +#[cfg_attr( + feature = "frozen-abi", + frozen_abi(digest = "4Mr6qK8Srdgr7PyeZjXy99ekj5pwHQSbNCVD3TmzcxTM"), + derive(AbiExample) +)] #[cfg_attr( feature = "serde", derive(Deserialize, Serialize), diff --git a/short-vec/src/lib.rs b/short-vec/src/lib.rs index b902d585c..5396f73e8 100644 --- a/short-vec/src/lib.rs +++ b/short-vec/src/lib.rs @@ -26,6 +26,10 @@ impl Serialize for ShortU16 { where S: Serializer, { + if serializer.is_human_readable() { + return serializer.serialize_u16(self.0); + } + // Pass a non-zero value to serialize_tuple() so that serde_json will // generate an open bracket. let mut seq = serializer.serialize_tuple(1)?; @@ -155,7 +159,11 @@ impl<'de> Deserialize<'de> for ShortU16 { where D: Deserializer<'de>, { - deserializer.deserialize_tuple(3, ShortU16Visitor) + if deserializer.is_human_readable() { + Ok(ShortU16(u16::deserialize(deserializer)?)) + } else { + deserializer.deserialize_tuple(3, ShortU16Visitor) + } } } @@ -168,6 +176,15 @@ pub fn serialize( elements: &[T], serializer: S, ) -> Result { + if serializer.is_human_readable() { + use serde_core::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(elements.len()))?; + for element in elements { + seq.serialize_element(element)?; + } + return seq.end(); + } + // Pass a non-zero value to serialize_tuple() so that serde_json will // generate an open bracket. let mut seq = serializer.serialize_tuple(1)?; @@ -229,6 +246,9 @@ where D: Deserializer<'de>, T: Deserialize<'de>, { + if deserializer.is_human_readable() { + return as Deserialize<'de>>::deserialize(deserializer); + } let visitor = ShortVecVisitor { _t: PhantomData }; deserializer.deserialize_tuple(usize::MAX, visitor) } @@ -274,8 +294,43 @@ mod tests { super::*, assert_matches::assert_matches, bincode::{deserialize, serialize}, + std::u16, }; + #[test] + fn test_short_u16_human_readable_serialization() { + let value = ShortU16(0u16); + let string = serde_json::to_string(&value).unwrap(); + let expected_string = String::from("0"); + assert_eq!(string, expected_string); + let deser_value: ShortU16 = serde_json::from_str(&string).unwrap(); + assert_eq!(value.0, deser_value.0); + + let value = ShortU16(u16::MAX); + let string = serde_json::to_string(&value).unwrap(); + let expected_string = String::from("65535"); + assert_eq!(string, expected_string); + let deser_value: ShortU16 = serde_json::from_str(&string).unwrap(); + assert_eq!(value.0, deser_value.0); + } + + #[test] + fn test_short_vec_human_readable_serialization() { + let value = ShortVec::<()>(vec![]); + let string = serde_json::to_string(&value).unwrap(); + let expected_string = String::from("[]"); + assert_eq!(string, expected_string); + let deser_value: ShortVec<()> = serde_json::from_str(&string).unwrap(); + assert_eq!(value.0, deser_value.0); + + let value = ShortVec(vec![1, 2, 3]); + let string = serde_json::to_string(&value).unwrap(); + let expected_string = String::from("[1,2,3]"); + assert_eq!(string, expected_string); + let deser_value: ShortVec = serde_json::from_str(&string).unwrap(); + assert_eq!(value.0, deser_value.0); + } + /// Return the serialized length. fn encode_len(len: u16) -> Vec { bincode::serialize(&ShortU16(len)).unwrap() @@ -372,13 +427,6 @@ mod tests { assert_matches!(serialize(&vec), Err(_)); } - #[test] - fn test_short_vec_json() { - let vec = ShortVec(vec![0, 1, 2]); - let s = serde_json::to_string(&vec).unwrap(); - assert_eq!(s, "[[3],0,1,2]"); - } - #[test] fn test_short_vec_aliased_length() { let bytes = [ diff --git a/transaction/src/lib.rs b/transaction/src/lib.rs index 317c0ffb7..9aeafee7d 100644 --- a/transaction/src/lib.rs +++ b/transaction/src/lib.rs @@ -177,7 +177,7 @@ const NONCED_TX_MARKER_IX_INDEX: u8 = 0; #[cfg_attr( feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample), - solana_frozen_abi_macro::frozen_abi(digest = "BLig4G2ysd7dcensK9bhKtnKvCQc1n65XdanyzsdWGXN") + solana_frozen_abi_macro::frozen_abi(digest = "5V7i4Tngkou3ieDNdtPpwYaFZpe2S2vZL71wrdJq5zTQ") )] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[derive(Debug, PartialEq, Default, Eq, Clone)]