Skip to content

Commit 2484812

Browse files
authored
Merge pull request #6140 from hstove/feat/serde-serializers
feat: move serde serializers to stacks_common
2 parents 0daf342 + e51bd5b commit 2484812

File tree

9 files changed

+173
-175
lines changed

9 files changed

+173
-175
lines changed

libsigner/src/events.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ use blockstack_lib::chainstate::stacks::StacksTransaction;
2929
use blockstack_lib::net::api::postblock_proposal::{
3030
BlockValidateReject, BlockValidateResponse, ValidateRejectCode,
3131
};
32-
use blockstack_lib::net::api::{prefix_hex, prefix_opt_hex};
3332
use blockstack_lib::net::stackerdb::MINER_SLOT_COUNT;
3433
use blockstack_lib::util_lib::boot::boot_code_id;
3534
use blockstack_lib::version_string;
@@ -47,6 +46,7 @@ use stacks_common::types::chainstate::{
4746
BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, SortitionId, StacksPublicKey,
4847
};
4948
use stacks_common::util::hash::{Hash160, Sha512Trunc256Sum};
49+
use stacks_common::util::serde_serializers::{prefix_hex, prefix_opt_hex};
5050
use stacks_common::util::HexError;
5151
use stacks_common::versions::STACKS_NODE_VERSION;
5252
use tiny_http::{

stacks-common/src/util/macros.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2-
// Copyright (C) 2020 Stacks Open Internet Foundation
2+
// Copyright (C) 2020-2025 Stacks Open Internet Foundation
33
//
44
// This program is free software: you can redistribute it and/or modify
55
// it under the terms of the GNU General Public License as published by
@@ -613,6 +613,12 @@ macro_rules! impl_byte_array_newtype {
613613
Self(o)
614614
}
615615
}
616+
617+
impl $crate::util::HexDeser for $thing {
618+
fn try_from_hex(hex_str: &str) -> Result<Self, $crate::util::HexError> {
619+
$thing::from_hex(hex_str)
620+
}
621+
}
616622
};
617623
}
618624

stacks-common/src/util/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub mod pair;
2727
pub mod pipe;
2828
pub mod retry;
2929
pub mod secp256k1;
30+
pub mod serde_serializers;
3031
pub mod uint;
3132
pub mod vrf;
3233

@@ -111,6 +112,10 @@ impl error::Error for HexError {
111112
}
112113
}
113114

115+
pub trait HexDeser: Sized {
116+
fn try_from_hex(hex: &str) -> Result<Self, HexError>;
117+
}
118+
114119
/// Write any `serde_json` object directly to a file
115120
pub fn serialize_json_to_file<J, P>(json: &J, path: P) -> Result<(), std::io::Error>
116121
where
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/// This module serde encodes and decodes optional byte fields in RPC
2+
/// responses as Some(String) where the String is a `0x` prefixed
3+
/// hex string.
4+
pub mod prefix_opt_hex {
5+
pub fn serialize<S: serde::Serializer, T: std::fmt::LowerHex>(
6+
val: &Option<T>,
7+
s: S,
8+
) -> Result<S::Ok, S::Error> {
9+
match val {
10+
Some(ref some_val) => {
11+
let val_str = format!("0x{some_val:x}");
12+
s.serialize_some(&val_str)
13+
}
14+
None => s.serialize_none(),
15+
}
16+
}
17+
18+
pub fn deserialize<'de, D: serde::Deserializer<'de>, T: crate::util::HexDeser>(
19+
d: D,
20+
) -> Result<Option<T>, D::Error> {
21+
let opt_inst_str: Option<String> = serde::Deserialize::deserialize(d)?;
22+
let Some(inst_str) = opt_inst_str else {
23+
return Ok(None);
24+
};
25+
let Some(hex_str) = inst_str.get(2..) else {
26+
return Err(serde::de::Error::invalid_length(
27+
inst_str.len(),
28+
&"at least length 2 string",
29+
));
30+
};
31+
let val = T::try_from_hex(hex_str).map_err(serde::de::Error::custom)?;
32+
Ok(Some(val))
33+
}
34+
}
35+
36+
/// This module serde encodes and decodes byte fields in RPC
37+
/// responses as a String where the String is a `0x` prefixed
38+
/// hex string.
39+
pub mod prefix_hex {
40+
pub fn serialize<S: serde::Serializer, T: std::fmt::LowerHex>(
41+
val: &T,
42+
s: S,
43+
) -> Result<S::Ok, S::Error> {
44+
s.serialize_str(&format!("0x{val:x}"))
45+
}
46+
47+
pub fn deserialize<'de, D: serde::Deserializer<'de>, T: crate::util::HexDeser>(
48+
d: D,
49+
) -> Result<T, D::Error> {
50+
let inst_str: String = serde::Deserialize::deserialize(d)?;
51+
let Some(hex_str) = inst_str.get(2..) else {
52+
return Err(serde::de::Error::invalid_length(
53+
inst_str.len(),
54+
&"at least length 2 string",
55+
));
56+
};
57+
T::try_from_hex(hex_str).map_err(serde::de::Error::custom)
58+
}
59+
}
60+
61+
/// This module serde encode and decodes structs that
62+
/// implement StacksMessageCodec as a 0x-prefixed hex string.
63+
pub mod prefix_hex_codec {
64+
use crate::codec::StacksMessageCodec;
65+
use crate::util::hash::{hex_bytes, to_hex};
66+
67+
pub fn serialize<S: serde::Serializer, T: StacksMessageCodec>(
68+
val: &T,
69+
s: S,
70+
) -> Result<S::Ok, S::Error> {
71+
let mut bytes = vec![];
72+
val.consensus_serialize(&mut bytes)
73+
.map_err(serde::ser::Error::custom)?;
74+
s.serialize_str(&format!("0x{}", to_hex(&bytes)))
75+
}
76+
77+
pub fn deserialize<'de, D: serde::Deserializer<'de>, T: StacksMessageCodec>(
78+
d: D,
79+
) -> Result<T, D::Error> {
80+
let inst_str: String = serde::Deserialize::deserialize(d)?;
81+
let Some(hex_str) = inst_str.get(2..) else {
82+
return Err(serde::de::Error::invalid_length(
83+
inst_str.len(),
84+
&"at least length 2 string",
85+
));
86+
};
87+
let bytes = hex_bytes(hex_str).map_err(serde::de::Error::custom)?;
88+
T::consensus_deserialize(&mut &bytes[..]).map_err(serde::de::Error::custom)
89+
}
90+
}
91+
92+
/// This module serde encode and decodes structs that
93+
/// implement StacksMessageCodec as a 0x-prefixed hex string.
94+
/// This is the same as prefix_hex_codec, but for Option<T>.
95+
pub mod prefix_opt_hex_codec {
96+
use crate::codec::StacksMessageCodec;
97+
use crate::util::hash::{hex_bytes, to_hex};
98+
99+
pub fn serialize<S: serde::Serializer, T: StacksMessageCodec>(
100+
val: &Option<T>,
101+
s: S,
102+
) -> Result<S::Ok, S::Error> {
103+
match val {
104+
Some(ref some_val) => {
105+
let mut bytes = vec![];
106+
some_val
107+
.consensus_serialize(&mut bytes)
108+
.map_err(serde::ser::Error::custom)?;
109+
let hex_string = format!("0x{}", to_hex(&bytes));
110+
s.serialize_some(&hex_string)
111+
}
112+
None => s.serialize_none(),
113+
}
114+
}
115+
116+
pub fn deserialize<'de, D: serde::Deserializer<'de>, T: StacksMessageCodec>(
117+
d: D,
118+
) -> Result<Option<T>, D::Error> {
119+
let opt_inst_str: Option<String> = serde::Deserialize::deserialize(d)?;
120+
let Some(inst_string) = opt_inst_str else {
121+
return Ok(None);
122+
};
123+
let Some(hex_str) = inst_string.get(2..) else {
124+
return Err(serde::de::Error::invalid_length(
125+
inst_string.len(),
126+
&"at least length 2 string",
127+
));
128+
};
129+
let bytes = hex_bytes(hex_str).map_err(serde::de::Error::custom)?;
130+
let val = T::consensus_deserialize(&mut &bytes[..]).map_err(serde::de::Error::custom)?;
131+
Ok(Some(val))
132+
}
133+
}
134+
135+
/// Serialize strings as 0x-prefixed strings.
136+
pub mod prefix_string_0x {
137+
use serde::{Deserialize, Deserializer, Serializer};
138+
139+
pub fn serialize<S: Serializer>(val: &str, s: S) -> Result<S::Ok, S::Error> {
140+
s.serialize_str(&format!("0x{}", val))
141+
}
142+
143+
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<String, D::Error> {
144+
let s: String = Deserialize::deserialize(d)?;
145+
let Some(hex_str) = s.get(2..) else {
146+
return Err(serde::de::Error::invalid_length(
147+
s.len(),
148+
&"at least length 2 string",
149+
));
150+
};
151+
Ok(hex_str.to_string())
152+
}
153+
}

stackslib/src/net/api/get_tenures_fork_info.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use stacks_common::types::chainstate::{
2525
use stacks_common::types::net::PeerHost;
2626
use stacks_common::types::StacksEpochId;
2727
use stacks_common::util::hash::{to_hex, Hash160};
28+
use stacks_common::util::serde_serializers::{prefix_hex, prefix_opt_hex, prefix_opt_hex_codec};
2829
use stacks_common::util::HexError;
2930
use {serde, serde_json};
3031

@@ -34,7 +35,6 @@ use crate::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState, NakamotoSta
3435
use crate::chainstate::stacks::db::StacksChainState;
3536
use crate::chainstate::stacks::Error as ChainError;
3637
use crate::net::api::getblock_v3::NakamotoBlockStream;
37-
use crate::net::api::{prefix_hex, prefix_opt_hex, prefix_opt_hex_codec};
3838
use crate::net::http::{
3939
parse_bytes, parse_json, Error, HttpBadRequest, HttpChunkGenerator, HttpContentType,
4040
HttpNotFound, HttpRequest, HttpRequestContents, HttpRequestPreamble, HttpResponse,

stackslib/src/net/api/getsortition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use stacks_common::types::chainstate::{
2626
};
2727
use stacks_common::types::net::PeerHost;
2828
use stacks_common::util::hash::{to_hex, Hash160};
29+
use stacks_common::util::serde_serializers::{prefix_hex, prefix_opt_hex};
2930
use stacks_common::util::HexError;
3031
use {serde, serde_json};
3132

@@ -37,7 +38,6 @@ use crate::chainstate::nakamoto::{
3738
use crate::chainstate::stacks::db::StacksChainState;
3839
use crate::chainstate::stacks::Error as ChainError;
3940
use crate::net::api::getblock_v3::NakamotoBlockStream;
40-
use crate::net::api::{prefix_hex, prefix_opt_hex};
4141
use crate::net::http::{
4242
parse_bytes, parse_json, Error, HttpBadRequest, HttpChunkGenerator, HttpContentType,
4343
HttpNotFound, HttpRequest, HttpRequestContents, HttpRequestPreamble, HttpResponse,

0 commit comments

Comments
 (0)