Skip to content

Commit 7c46a3a

Browse files
committed
fix(repo): Serializing block time as integer
1 parent 86f63a8 commit 7c46a3a

File tree

3 files changed

+177
-8
lines changed

3 files changed

+177
-8
lines changed

.github/actions/setup-rust/action.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ runs:
6464
6565
- name: Run sccache-cache only on non-release runs
6666
if: inputs.cache == 'true' || github.event_name != 'release' || github.event_name != 'workflow_dispatch'
67-
uses: mozilla-actions/sccache-action@v0.0.5
67+
uses: mozilla-actions/sccache-action@v0.0.8
6868

6969
- name: Set Rust caching env vars only on non-release runs
7070
shell: sh

crates/types/src/primitives/block_header.rs

Lines changed: 172 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
use std::collections::HashMap;
1+
use std::{collections::HashMap, fmt};
22

33
use apache_avro::{
44
schema::{derive::AvroSchemaComponent, Name},
55
AvroSchema,
66
Schema,
77
};
88
use chrono::{DateTime, TimeZone, Utc};
9-
use serde::{Deserialize, Serialize};
9+
use serde::{
10+
de::{self, Deserializer, Visitor},
11+
ser::Serializer,
12+
Deserialize,
13+
Serialize,
14+
};
1015
use wrapped_int::WrappedU32;
1116

1217
use crate::*;
1318

14-
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19+
#[derive(Debug, Clone, PartialEq)]
1520
pub struct BlockTime(pub FuelCoreTai64);
21+
1622
impl BlockTime {
1723
pub fn into_inner(self) -> FuelCoreTai64 {
1824
self.0
@@ -34,6 +40,87 @@ impl Default for BlockTime {
3440
}
3541
}
3642

43+
impl Serialize for BlockTime {
44+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
45+
where
46+
S: Serializer,
47+
{
48+
let unix_timestamp = self.0.to_unix();
49+
serializer.serialize_i64(unix_timestamp)
50+
}
51+
}
52+
53+
impl<'de> Deserialize<'de> for BlockTime {
54+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55+
where
56+
D: Deserializer<'de>,
57+
{
58+
struct BlockTimeVisitor;
59+
60+
impl<'de> Visitor<'de> for BlockTimeVisitor {
61+
type Value = BlockTime;
62+
63+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
64+
formatter.write_str(
65+
"a string Unix timestamp or an 8-byte array or an integer",
66+
)
67+
}
68+
69+
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
70+
where
71+
E: de::Error,
72+
{
73+
Ok(BlockTime(FuelCoreTai64::from_unix(value)))
74+
}
75+
76+
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
77+
where
78+
E: de::Error,
79+
{
80+
Ok(BlockTime(FuelCoreTai64::from_unix(value as i64)))
81+
}
82+
83+
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
84+
where
85+
E: de::Error,
86+
{
87+
let unix_timestamp =
88+
value.parse::<i64>().map_err(de::Error::custom)?;
89+
Ok(BlockTime(FuelCoreTai64::from_unix(unix_timestamp)))
90+
}
91+
92+
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
93+
where
94+
E: de::Error,
95+
{
96+
let tai64 = FuelCoreTai64::from_slice(value).map_err(|_| {
97+
de::Error::custom("expected an 8-byte array for TAI64")
98+
})?;
99+
Ok(BlockTime(tai64))
100+
}
101+
102+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
103+
where
104+
A: de::SeqAccess<'de>,
105+
{
106+
let mut bytes = [0u8; 8];
107+
for byte in &mut bytes {
108+
*byte = seq.next_element()?.ok_or_else(|| {
109+
de::Error::custom("byte array too short")
110+
})?;
111+
}
112+
if seq.next_element::<u8>()?.is_some() {
113+
return Err(de::Error::custom("byte array too long"));
114+
}
115+
let tai64 = FuelCoreTai64::from(bytes);
116+
Ok(BlockTime(tai64))
117+
}
118+
}
119+
120+
deserializer.deserialize_any(BlockTimeVisitor)
121+
}
122+
}
123+
37124
impl utoipa::ToSchema for BlockTime {
38125
fn name() -> std::borrow::Cow<'static, str> {
39126
std::borrow::Cow::Borrowed("BlockTime")
@@ -63,12 +150,10 @@ impl AvroSchemaComponent for BlockTime {
63150
_ctxt: &mut HashMap<Name, Schema>,
64151
_namespace: &Option<String>,
65152
) -> Schema {
66-
// Use Avro's `long` type (i64) for serialization
67153
Schema::Long
68154
}
69155
}
70156

71-
// Header type
72157
#[derive(
73158
Debug,
74159
Clone,
@@ -135,7 +220,6 @@ impl From<&FuelCoreBlockHeader> for BlockHeader {
135220
}
136221
}
137222

138-
// BlockHeaderVersion enum
139223
#[derive(
140224
Debug,
141225
Clone,
@@ -147,9 +231,90 @@ impl From<&FuelCoreBlockHeader> for BlockHeader {
147231
derive_more::Display,
148232
apache_avro::AvroSchema,
149233
)]
150-
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
234+
#[serde(rename_all = "snake_case")]
151235
pub enum BlockHeaderVersion {
152236
#[default]
153237
#[display("V1")]
154238
V1,
155239
}
240+
241+
#[cfg(test)]
242+
mod tests {
243+
use pretty_assertions::assert_eq;
244+
use serde_json;
245+
246+
use super::*;
247+
248+
#[test]
249+
fn test_serialize_string() {
250+
let block_time = BlockTime::from_unix(1614556800);
251+
let serialized = serde_json::to_string(&block_time).unwrap();
252+
assert_eq!(serialized, "1614556800"); // Expect integer
253+
}
254+
255+
#[test]
256+
fn test_deserialize_string() {
257+
let json = "\"1614556800\"";
258+
let deserialized: BlockTime = serde_json::from_str(json).unwrap();
259+
let expected = BlockTime::from_unix(1614556800);
260+
assert_eq!(deserialized, expected);
261+
assert_eq!(deserialized.0.to_unix(), 1614556800);
262+
}
263+
264+
#[test]
265+
fn test_deserialize_byte_array() {
266+
let block_time = BlockTime::from_unix(1614556800);
267+
let bytes = block_time.0.to_bytes();
268+
let json = serde_json::to_string(&bytes.to_vec()).unwrap();
269+
let deserialized: BlockTime = serde_json::from_str(&json).unwrap();
270+
assert_eq!(deserialized, block_time);
271+
assert_eq!(deserialized.0.to_unix(), 1614556800);
272+
}
273+
274+
#[test]
275+
fn test_round_trip_string() {
276+
let original = BlockTime::from_unix(1614556800);
277+
let serialized = serde_json::to_string(&original).unwrap();
278+
let deserialized: BlockTime =
279+
serde_json::from_str(&serialized).unwrap();
280+
assert_eq!(deserialized, original);
281+
}
282+
283+
#[test]
284+
fn test_deserialize_invalid_string() {
285+
let json = "\"not-a-number\"";
286+
let result = serde_json::from_str::<BlockTime>(json);
287+
assert!(result.is_err());
288+
}
289+
290+
#[test]
291+
fn test_deserialize_invalid_byte_array_length() {
292+
let json = "[1, 2, 3]";
293+
let result = serde_json::from_str::<BlockTime>(json);
294+
assert!(result.is_err());
295+
296+
let json = "[1, 2, 3, 4, 5, 6, 7, 8, 9]";
297+
let result = serde_json::from_str::<BlockTime>(json);
298+
assert!(result.is_err());
299+
}
300+
301+
#[test]
302+
fn test_get_timestamp_utc() {
303+
let block_time = BlockTime::from_unix(1614556800);
304+
let header = BlockHeader {
305+
time: block_time,
306+
..Default::default()
307+
};
308+
let utc_time = header.get_timestamp_utc();
309+
assert_eq!(utc_time.to_rfc3339(), "2021-03-01T00:00:00+00:00");
310+
}
311+
312+
#[test]
313+
fn test_deserialize_integer() {
314+
let json = "1614556800";
315+
let deserialized: BlockTime = serde_json::from_str(json).unwrap();
316+
let expected = BlockTime::from_unix(1614556800);
317+
assert_eq!(deserialized, expected);
318+
assert_eq!(deserialized.0.to_unix(), 1614556800);
319+
}
320+
}

scripts/subjects-schema/src/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use fuel_streams_domains::{
44
blocks::subjects::*,
55
inputs::subjects::*,
66
outputs::subjects::*,
7+
predicates::PredicatesSubject,
78
receipts::subjects::*,
89
transactions::subjects::*,
910
utxos::subjects::*,
@@ -39,6 +40,8 @@ fn main() {
3940
outputs_contract_created_schema,
4041
);
4142

43+
let predicates_schema = PredicatesSubject::new().schema();
44+
4245
let mut receipts_schema = ReceiptsSubject::new().schema();
4346
let receipts_call_schema = ReceiptsCallSubject::new().schema();
4447
let receipts_return_schema = ReceiptsReturnSubject::new().schema();
@@ -82,6 +85,7 @@ fn main() {
8285
("transactions".to_string(), transaction_schema),
8386
("inputs".to_string(), inputs_schema),
8487
("outputs".to_string(), outputs_schema),
88+
("predicates".to_string(), predicates_schema),
8589
("receipts".to_string(), receipts_schema),
8690
("utxos".to_string(), utxos_schema),
8791
]);

0 commit comments

Comments
 (0)