Skip to main content

dpp/
metadata.rs

1use bincode::{DecodeUntrusted, Encode};
2use platform_serialization::de::Decode;
3use serde::{Deserialize, Serialize};
4
5#[cfg(feature = "json-conversion")]
6use crate::serialization::json_safe_fields;
7use crate::{errors::ProtocolError, prelude::TimestampMillis, util::deserializer::ProtocolVersion};
8
9#[cfg_attr(feature = "json-conversion", json_safe_fields)]
10#[derive(
11    Serialize,
12    Deserialize,
13    Encode,
14    Decode,
15    Debug,
16    Default,
17    Clone,
18    Copy,
19    PartialEq,
20    PartialOrd,
21    Eq,
22    DecodeUntrusted,
23)]
24#[serde(rename_all = "camelCase")]
25pub struct Metadata {
26    #[serde(default)]
27    pub block_height: u64,
28    #[serde(default)]
29    pub core_chain_locked_height: u64,
30    #[serde(default)]
31    pub time_ms: TimestampMillis,
32    #[serde(default)]
33    pub protocol_version: ProtocolVersion,
34}
35
36impl std::convert::TryFrom<&str> for Metadata {
37    type Error = ProtocolError;
38
39    fn try_from(d: &str) -> Result<Metadata, Self::Error> {
40        serde_json::from_str(d).map_err(|e| ProtocolError::EncodingError(e.to_string()))
41    }
42}
43
44#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
45impl crate::serialization::JsonConvertible for Metadata {}
46
47#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
48impl crate::serialization::ValueConvertible for Metadata {}
49
50#[cfg(all(
51    test,
52    feature = "json-conversion",
53    feature = "value-conversion",
54    feature = "serde-conversion"
55))]
56mod json_convertible_tests {
57    use super::*;
58    use crate::serialization::{JsonConvertible, ValueConvertible};
59    use platform_value::platform_value;
60    use serde_json::json;
61
62    /// Non-default values per field. `Metadata` crosses to JS as the
63    /// `$metadata` field of `ExtendedDocument`, so its u64 fields go through
64    /// `json_safe_fields` (numbers below 2^53, strings above).
65    fn fixture() -> Metadata {
66        Metadata {
67            block_height: 1_234_567,
68            core_chain_locked_height: 2_222_333,
69            time_ms: 1_700_000_000_000,
70            protocol_version: 9,
71        }
72    }
73
74    #[test]
75    fn json_round_trip_with_full_wire_shape() {
76        let original = fixture();
77        let json = original.to_json().expect("to_json");
78        // Sized-int info erased in JSON; the value-path assertion locks the
79        // typed variants (u64 heights/time, u32 protocolVersion).
80        assert_eq!(
81            json,
82            json!({
83                "blockHeight": 1_234_567,
84                "coreChainLockedHeight": 2_222_333,
85                "timeMs": 1_700_000_000_000u64,
86                "protocolVersion": 9,
87            })
88        );
89        let recovered = Metadata::from_json(json).expect("from_json");
90        assert_eq!(original, recovered);
91    }
92
93    #[test]
94    fn value_round_trip_with_full_wire_shape() {
95        let original = fixture();
96        let value = original.to_object().expect("to_object");
97        assert_eq!(
98            value,
99            platform_value!({
100                "blockHeight": 1_234_567u64,
101                "coreChainLockedHeight": 2_222_333u64,
102                "timeMs": 1_700_000_000_000u64,
103                "protocolVersion": 9u32,
104            })
105        );
106        let recovered = Metadata::from_object(value).expect("from_object");
107        assert_eq!(original, recovered);
108    }
109
110    /// Values above JS `Number.MAX_SAFE_INTEGER` must stringify in JSON
111    /// (json_safe_fields contract) and still round-trip.
112    #[test]
113    fn json_stringifies_unsafe_u64() {
114        let original = Metadata {
115            block_height: u64::MAX,
116            ..fixture()
117        };
118        let json = original.to_json().expect("to_json");
119        assert_eq!(json["blockHeight"], json!(u64::MAX.to_string()));
120        let recovered = Metadata::from_json(json).expect("from_json");
121        assert_eq!(original, recovered);
122    }
123}