Skip to content

Commit

Permalink
fix: wrong json serialization of ConsensusParams
Browse files Browse the repository at this point in the history
  • Loading branch information
lklimek committed Feb 27, 2024
1 parent 8b5afe6 commit 2b9dcec
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 18 deletions.
31 changes: 27 additions & 4 deletions proto-compiler/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,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 +108,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 +205,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();
}

0 comments on commit 2b9dcec

Please sign in to comment.