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

fix(proto)!: wrong json serialization of consensus params #51

Merged
merged 3 commits into from
Mar 4, 2024
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
32 changes: 27 additions & 5 deletions proto-compiler/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const DERIVE_FROM_STR: &str = r#"#[derive(derive_more::FromStr)]"#;
/// here: <https://docs.rs/prost-build/0.6.1/prost_build/struct.Config.html#method.btree_map>
pub static CUSTOM_TYPE_ATTRIBUTES: &[(&str, &str)] = &[
(".tendermint.libs.bits.BitArray", SERIALIZED),
(".tendermint.types.EvidenceParams", SERIALIZED),
(".tendermint.types.BlockIDFlag", PRIMITIVE_ENUM),
(".tendermint.types.Block", SERIALIZED),
(".tendermint.types.Data", SERIALIZED),
Expand Down Expand Up @@ -91,6 +90,15 @@ pub static CUSTOM_TYPE_ATTRIBUTES: &[(&str, &str)] = &[
(".tendermint.crypto.Proof", SERIALIZED),
(".tendermint.abci.Response.value", DERIVE_FROM),
(".tendermint.abci.Request.value", DERIVE_FROM),
// Consensus params
(".tendermint.types.ConsensusParams", SERIALIZED),
(".tendermint.types.ABCIParams", SERIALIZED),
(".tendermint.types.BlockParams", SERIALIZED),
(".tendermint.types.EvidenceParams", SERIALIZED),
(".tendermint.types.ValidatorParams", SERIALIZED),
(".tendermint.types.VersionParams", SERIALIZED),
(".tendermint.types.SynchronyParams", SERIALIZED),
(".tendermint.types.TimeoutParams", SERIALIZED),
];

/// Custom field attributes applied on top of protobuf fields in (a) struct(s)
Expand All @@ -99,10 +107,6 @@ pub static CUSTOM_TYPE_ATTRIBUTES: &[(&str, &str)] = &[
/// The first item is a path as defined in the prost_build::Config::btree_map
/// here: <https://docs.rs/prost-build/0.6.1/prost_build/struct.Config.html#method.btree_map>
pub static CUSTOM_FIELD_ATTRIBUTES: &[(&str, &str)] = &[
(
".tendermint.types.EvidenceParams.max_bytes",
QUOTED_WITH_DEFAULT,
),
(".tendermint.version.Consensus.block", QUOTED),
(".tendermint.version.Consensus.app", QUOTED_WITH_DEFAULT),
(".tendermint.abci.ResponseInfo.data", DEFAULT),
Expand Down Expand Up @@ -200,4 +204,22 @@ pub static CUSTOM_FIELD_ATTRIBUTES: &[(&str, &str)] = &[
(".tendermint.crypto.Proof.total", QUOTED),
(".tendermint.crypto.Proof.aunts", VEC_BASE64STRING),
(".tendermint.crypto.Proof.leaf_hash", BASE64STRING),
// Consensus params
(
".tendermint.types.BlockParams.max_bytes",
QUOTED_WITH_DEFAULT,
),
(".tendermint.types.BlockParams.max_gas", QUOTED_WITH_DEFAULT),
(
".tendermint.types.EvidenceParams.max_age_num_blocks",
QUOTED_WITH_DEFAULT,
),
(
".tendermint.types.EvidenceParams.max_bytes",
QUOTED_WITH_DEFAULT,
),
(
".tendermint.types.VersionParams.app_version",
QUOTED_WITH_DEFAULT,
),
];
66 changes: 60 additions & 6 deletions proto/src/protobuf.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Google protobuf Timestamp and Duration types reimplemented because their comments are turned
// into invalid documentation texts and doctest chokes on them. See https://github.com/danburkert/prost/issues/374
// Prost does not seem to have a way yet to remove documentations defined in protobuf files.
// These structs are defined in gogoproto v1.3.1 at https://github.com/gogo/protobuf/tree/v1.3.1/protobuf/google/protobuf
// Google protobuf Timestamp and Duration types reimplemented because their
// comments are turned into invalid documentation texts and doctest chokes on them. See https://github.com/danburkert/prost/issues/374
// Prost does not seem to have a way yet to remove documentations defined in
// protobuf files. These structs are defined in gogoproto v1.3.1 at https://github.com/gogo/protobuf/tree/v1.3.1/protobuf/google/protobuf

#[cfg(not(feature = "std"))]
use core::fmt;
#[cfg(feature = "std")]
use std::fmt;

/// A Timestamp represents a point in time independent of any time zone or local
/// calendar, encoded as a count of seconds and fractions of seconds at
Expand All @@ -17,7 +22,10 @@
/// restricting to that range, we ensure that we can convert to and from [RFC
/// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
#[derive(Clone, PartialEq, ::prost::Message, ::serde::Deserialize, ::serde::Serialize)]
#[serde(from = "crate::serializers::timestamp::Rfc3339", into = "crate::serializers::timestamp::Rfc3339")]
#[serde(
from = "crate::serializers::timestamp::Rfc3339",
into = "crate::serializers::timestamp::Rfc3339"
)]
pub struct Timestamp {
/// Represents seconds of UTC time since Unix epoch
/// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
Expand All @@ -38,7 +46,7 @@ pub struct Timestamp {
/// or "month". It is related to Timestamp in that the difference between
/// two Timestamp values is a Duration and it can be added or subtracted
/// from a Timestamp. Range is approximately +-10,000 years.
#[derive(Clone, PartialEq, ::prost::Message, ::serde::Deserialize, ::serde::Serialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Duration {
/// Signed seconds of the span of time. Must be from -315,576,000,000
/// to +315,576,000,000 inclusive. Note: these bounds are computed from:
Expand All @@ -54,3 +62,49 @@ pub struct Duration {
#[prost(int32, tag = "2")]
pub nanos: i32,
}

impl serde::Serialize for Duration {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let total_nanos = self.seconds * 1_000_000_000 + self.nanos as i64;
serializer.serialize_i64(total_nanos)
}
}

struct DurationVisitor;

impl<'de> serde::de::Visitor<'de> for DurationVisitor {
type Value = Duration;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a nanosecond representation of a duration")
}

fn visit_i128<E>(self, value: i128) -> Result<Duration, E>
where
E: serde::de::Error,
{
let seconds = (value / 1_000_000_000) as i64;
let nanos = (value % 1_000_000_000) as i32;
Ok(Duration { seconds, nanos })
}

fn visit_str<E>(self, value: &str) -> Result<Duration, E>
where
E: serde::de::Error,
{
let value = value.parse::<i128>().map_err(E::custom)?;
self.visit_i128(value)
}
}

impl<'de> serde::Deserialize<'de> for Duration {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
deserializer.deserialize_str(DurationVisitor)
}
}
12 changes: 8 additions & 4 deletions proto/src/serializers/from_str.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
//! Serialize and deserialize any `T` that implements [[core::str::FromStr]]
//! and [[core::fmt::Display]] from or into string. Note this can be used for
//! all primitive data types.
#[cfg(not(feature = "std"))]
use core::{fmt::Display, str::FromStr};
#[cfg(feature = "std")]
use std::{fmt::Display, str::FromStr};

use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};

use crate::prelude::*;

/// Deserialize string into T
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: core::str::FromStr,
<T as core::str::FromStr>::Err: core::fmt::Display,
T: FromStr,
<T as FromStr>::Err: Display,
{
String::deserialize(deserializer)?
.parse::<T>()
Expand All @@ -21,7 +25,7 @@ where
pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: core::fmt::Display,
T: Display,
{
format!("{value}").serialize(serializer)
}
3 changes: 3 additions & 0 deletions proto/src/serializers/time_duration.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Serialize/deserialize core::time::Duration type from and into string:
#[cfg(not(feature = "std"))]
use core::time::Duration;
#[cfg(feature = "std")]
use std::time::Duration;

use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};

Expand Down
8 changes: 5 additions & 3 deletions proto/src/serializers/timestamp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Serialize/deserialize Timestamp type from and into string:

#[cfg(not(feature = "std"))]
use core::fmt::{self, Debug};
#[cfg(feature = "std")]
use std::fmt::{self, Debug};

use serde::{de::Error as _, ser::Error, Deserialize, Deserializer, Serialize, Serializer};
use time::{
Expand Down Expand Up @@ -147,8 +149,8 @@ pub fn to_rfc3339_nanos(t: OffsetDateTime) -> String {
/// ie. a RFC3339 date-time with left-padded subsecond digits without
/// trailing zeros and no trailing dot.
///
/// [`Display`]: core::fmt::Display
/// [`Debug`]: core::fmt::Debug
/// [`Display`]: fmt::Display
/// [`Debug`]: fmt::Debug
pub fn fmt_as_rfc3339_nanos(t: OffsetDateTime, f: &mut impl fmt::Write) -> fmt::Result {
let t = t.to_offset(offset!(UTC));
let nanos = t.nanosecond();
Expand Down
43 changes: 42 additions & 1 deletion proto/tests/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::convert::TryFrom;

use tenderdash_proto::{
abci::ResponseException,
types::{BlockId as RawBlockId, PartSetHeader as RawPartSetHeader},
types::{BlockId as RawBlockId, ConsensusParams, PartSetHeader as RawPartSetHeader},
Protobuf,
};

Expand Down Expand Up @@ -133,3 +133,44 @@ pub fn test_response_exception_from() {
"string"
);
}

#[test]
pub fn test_consensus_params_serde() {
let json = r#"
{
"block": {
"max_bytes": "2097152",
"max_gas": "500000000"
},
"evidence": {
"max_age_num_blocks": "10000",
"max_age_duration": "172800000000000",
"max_bytes": "0"
},
"validator": {
"pub_key_types": [
"bls12381"
]
},
"version": {
"app_version": "1"
},
"synchrony": {
"precision": "500000000",
"message_delay": "60000000000"
},
"timeout": {
"propose": "40000000000",
"propose_delta": "5000000000",
"vote": "40000000000",
"vote_delta": "5000000000",
"bypass_commit_timeout": true
},
"abci": {
"recheck_tx": true
}
}
"#;

let _new_params: ConsensusParams = serde_json::from_str(json).unwrap();
}
Loading