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

Serialization refactor step 2 #269

Merged
merged 6 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from 5 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
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## Pending

Testing:
- Updated abci_info test to 0.17.0 ([#249](https://github.com/informalsystems/tendermint-rs/issues/249))

Serialization:
- Refactor serializers library to use modules and give a nicer annotation to structs. ([#247](https://github.com/informalsystems/tendermint-rs/issues/247))
- Added nullable Vec<u8> serialization ([#247](https://github.com/informalsystems/tendermint-rs/issues/247))

RPC:
- Tendermint-Go v0.33 compatibility ([#184](https://github.com/informalsystems/tendermint-rs/issues/184))
- `abci_info`, `abci_query`, `block_results`, `genesis` structs
Expand Down
10 changes: 3 additions & 7 deletions tendermint/src/rpc/endpoint/abci_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,9 @@ pub struct AbciQuery {
#[serde(default, with = "serializers::bytes::base64string")]
pub key: Vec<u8>,

/// Value (might be explicit null)
#[serde(
default,
serialize_with = "serializers::serialize_option_base64",
deserialize_with = "serializers::parse_option_base64"
)]
pub value: Option<Vec<u8>>,
/// Value
#[serde(default, with = "serializers::bytes::base64string")]
pub value: Vec<u8>,

/// Proof (might be explicit null)
pub proof: Option<Proof>,
Expand Down
141 changes: 84 additions & 57 deletions tendermint/src/serializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! Serializers and deserializers for a transparent developer experience.
//!
//! All serializers are presented in a serializers::<Rust_type_name>::<JSON_representation_name> format.
//! All serializers are presented in a serializers::<Rust_nickname>::<JSON_representation_name> format.
//!
//! This example shows how to serialize Vec<u8> into different types of strings:
//! ```
Expand All @@ -27,17 +27,18 @@
//! Available serializers:
//! i64 <-> string: #[serde(with="serializers::from_str")]
//! u64 <-> string: #[serde(with="serializers::from_str")]
//! std::time::Dureation <-> nanoseconds as string #[serde(with="serializers::time_duration")]
//! std::time::Duration <-> nanoseconds as string #[serde(with="serializers::time_duration")]
//! Vec<u8> <-> HexString: #[serde(with="serializers::bytes::hexstring")]
//! Vec<u8> <-> Base64String: #[serde(with="serializers::bytes::base64string")]
//! Vec<u8> <-> String: #[serde(with="serializers::bytes::string")]
//!
//! Any type that has the "FromStr" trait can be serialized into a string with serializers::primitives::string.
//!
//! Notes:
//! * Any type that has the "FromStr" trait can be serialized into a string with serializers::primitives::string.
//! * serializers::bytes::* deserializes a null value into an empty vec![].

use crate::account::{Id, LENGTH};
use crate::{block, Hash, Signature};
use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
use serde::{de::Error as _, Deserialize, Deserializer};
use std::str::FromStr;

/// Serialize and deserialize any `T` that implements [[std::str::FromStr]]
Expand Down Expand Up @@ -96,24 +97,83 @@ pub mod time_duration {

/// Serialize/deserialize bytes (Vec<u8>) type
pub mod bytes {
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer,
};
use std::fmt;
use subtle_encoding::{base64, hex};

/// ByteStringType defines the options what an incoming string can represent.
enum ByteStringType {
Hex,
Base64,
Regular,
}

/// The Visitor struct to decode the incoming string.
struct BytesVisitor {
string_type: ByteStringType,
}

/// The Visitor implementation
impl<'de> Visitor<'de> for BytesVisitor {
type Value = Vec<u8>;

/// Description of expected input
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self.string_type {
ByteStringType::Hex => {
formatter.write_str("Hex-encoded byte-array in a String or null")
}
ByteStringType::Base64 => {
formatter.write_str("Base64-encoded byte-array in a String or null")
}
ByteStringType::Regular => formatter.write_str("Byte-array in a String or null"),
}
}

/// If incoming is 'null', return an empty array.
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: Error,
{
Ok(vec![])
}

/// Decode the incoming string based on what string type it is.
fn visit_some<D>(
self,
deserializer: D,
) -> Result<Self::Value, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;

match self.string_type {
ByteStringType::Hex => hex::decode_upper(&string)
.or_else(|_| hex::decode(&string))
.map_err(Error::custom),
ByteStringType::Base64 => base64::decode(&string).map_err(Error::custom),
ByteStringType::Regular => Ok(string.as_bytes().to_vec()),
}
}
}

/// Serialize into hexstring, deserialize from hexstring
pub mod hexstring {
use serde::{
de::Error as deError, ser::Error as serError, Deserialize, Deserializer, Serializer,
};
use serde::{ser::Error, Deserializer, Serializer};
use subtle_encoding::hex;

/// Deserialize hexstring into Vec<u8>
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;

hex::decode_upper(&string)
.or_else(|_| hex::decode(&string))
.map_err(deError::custom)
deserializer.deserialize_option(super::BytesVisitor {
string_type: super::ByteStringType::Hex,
})
}

/// Serialize from T into hexstring
Expand All @@ -123,26 +183,24 @@ pub mod bytes {
T: AsRef<[u8]>,
{
let hex_bytes = hex::encode(value.as_ref());
let hex_string = String::from_utf8(hex_bytes).map_err(serError::custom)?;
let hex_string = String::from_utf8(hex_bytes).map_err(Error::custom)?;
serializer.serialize_str(&hex_string)
}
}

/// Serialize into base64string, deserialize from base64string
pub mod base64string {
use serde::{
de::Error as deError, ser::Error as serError, Deserialize, Deserializer, Serializer,
};
use serde::{ser::Error, Deserializer, Serializer};
use subtle_encoding::base64;

/// Deserialize base64string into Vec<u8>
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
let string = String::deserialize(deserializer)?;

base64::decode(&string).map_err(deError::custom)
deserializer.deserialize_option(super::BytesVisitor {
string_type: super::ByteStringType::Base64,
})
}

/// Serialize from T into base64string
Expand All @@ -152,25 +210,23 @@ pub mod bytes {
T: AsRef<[u8]>,
{
let base64_bytes = base64::encode(value.as_ref());
let base64_string = String::from_utf8(base64_bytes).map_err(serError::custom)?;
let base64_string = String::from_utf8(base64_bytes).map_err(Error::custom)?;
serializer.serialize_str(&base64_string)
}
}

/// Serialize into string, deserialize from string
pub mod string {
use serde::{
de::Error as _, ser::Error as serError, Deserialize, Deserializer, Serializer,
};
use serde::{ser::Error, Deserializer, Serializer};

/// Deserialize string into Vec<u8>
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.map(|m| m.as_bytes().to_vec())
.map_err(|e| D::Error::custom(format!("{}", e)))
deserializer.deserialize_option(super::BytesVisitor {
string_type: super::ByteStringType::Regular,
})
}

/// Serialize from T into string
Expand All @@ -179,7 +235,7 @@ pub mod bytes {
S: Serializer,
T: AsRef<[u8]>,
{
let string = String::from_utf8(value.as_ref().to_vec()).map_err(serError::custom)?;
let string = String::from_utf8(value.as_ref().to_vec()).map_err(Error::custom)?;
serializer.serialize_str(&string)
}
}
Expand All @@ -201,35 +257,6 @@ where
}
}

#[allow(dead_code)]
pub(crate) fn serialize_option_base64<S>(
maybe_bytes: &Option<Vec<u8>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct Wrapper<'a>(#[serde(with = "bytes::base64string")] &'a Vec<u8>);

match maybe_bytes {
Some(bytes) => Wrapper(bytes).serialize(serializer),
None => maybe_bytes.serialize(serializer),
}
}

#[allow(dead_code)]
pub(crate) fn parse_option_base64<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(#[serde(with = "bytes::base64string")] Vec<u8>);

let v = Option::deserialize(deserializer)?;
Ok(v.map(|Wrapper(a)| a))
}

/// Parse empty block id as None.
pub(crate) fn parse_non_empty_block_id<'de, D>(
deserializer: D,
Expand Down
2 changes: 1 addition & 1 deletion tendermint/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ mod rpc {
assert_eq!(abci_query.index, 0);
assert_eq!(&abci_query.key, &Vec::<u8>::new());
assert!(&abci_query.key.is_empty());
assert!(abci_query.value.is_none());
assert_eq!(abci_query.value, Vec::<u8>::new());
assert!(abci_query.proof.is_none());
assert!(abci_query.height.value() > 0);
assert_eq!(abci_query.codespace, String::new());
Expand Down
33 changes: 33 additions & 0 deletions tendermint/tests/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,36 @@ fn deserialize_vec_from_string() {
assert_eq!(result.mybase64bytes, b"MyString decoded.");
assert_eq!(result.stringifiedbytes, b"hello");
}

#[test]
fn serialize_emptyvec_into_emptystring() {
let outgoing = BytesTests {
myhexbytes: vec![],
mybase64bytes: vec![],
stringifiedbytes: vec![],
};

let result: String = serde_json::to_string(&outgoing).unwrap();

assert_eq!(
result,
r#"{"myhexbytes":"","mybase64bytes":"","stringifiedbytes":""}"#
);
}

#[test]
fn deserialize_emptyvec_from_null() {
let incoming = r#"
{
"myhexbytes": null,
"mybase64bytes": null,
"stringifiedbytes": null
}
"#;

let result: BytesTests = serde_json::from_str(&incoming).unwrap();

assert_eq!(result.myhexbytes, Vec::<u8>::new());
assert_eq!(result.mybase64bytes, Vec::<u8>::new());
assert_eq!(result.stringifiedbytes, Vec::<u8>::new());
}