Optimize serialization/deserialization for [u8; 32]
(#306)
* optimize simulation app serialization
This commit is contained in:
parent
61b587a4ce
commit
87f49c0fe9
@ -921,6 +921,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&info).unwrap();
|
let serialized = serde_json::to_string(&info).unwrap();
|
||||||
|
eprintln!("{serialized}");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serialized,
|
serialized,
|
||||||
r#"{"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"current_view":1,"highest_voted_view":-1,"local_high_qc":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"safe_blocks":[[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],{"view":0,"parent_qc":{"Standard":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"leader_proof":{"LeaderId":{"leader_id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}}]],"last_view_timeout_qc":null,"committed_blocks":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#
|
r#"{"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"current_view":1,"highest_voted_view":-1,"local_high_qc":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"safe_blocks":[[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],{"view":0,"parent_qc":{"Standard":{"view":0,"id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"leader_proof":{"LeaderId":{"leader_id":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}}}]],"last_view_timeout_qc":null,"committed_blocks":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]}"#
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
mod event_builder;
|
mod event_builder;
|
||||||
mod message_cache;
|
mod message_cache;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
|
mod serde_util;
|
||||||
mod tally;
|
mod tally;
|
||||||
mod timeout;
|
mod timeout;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ use std::{collections::HashMap, time::Duration};
|
|||||||
// crates
|
// crates
|
||||||
use bls_signatures::PrivateKey;
|
use bls_signatures::PrivateKey;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
// internal
|
// internal
|
||||||
use self::messages::CarnotMessage;
|
use self::messages::CarnotMessage;
|
||||||
use super::{Node, NodeId};
|
use super::{Node, NodeId};
|
||||||
@ -85,7 +86,6 @@ impl serde::Serialize for CarnotState {
|
|||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
if let Some(rs) = RECORD_SETTINGS.get() {
|
if let Some(rs) = RECORD_SETTINGS.get() {
|
||||||
let keys = rs
|
let keys = rs
|
||||||
.iter()
|
.iter()
|
||||||
@ -98,53 +98,54 @@ impl serde::Serialize for CarnotState {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut ser = serializer.serialize_struct("CarnotState", keys.len())?;
|
let mut state = serde_util::CarnotState::default();
|
||||||
for k in keys {
|
for k in keys {
|
||||||
match k.trim() {
|
match k.trim() {
|
||||||
NODE_ID => ser.serialize_field(NODE_ID, &self.node_id)?,
|
NODE_ID => {
|
||||||
CURRENT_VIEW => ser.serialize_field(CURRENT_VIEW, &self.current_view)?,
|
state.node_id = Some(self.node_id.into());
|
||||||
HIGHEST_VOTED_VIEW => {
|
}
|
||||||
ser.serialize_field(HIGHEST_VOTED_VIEW, &self.highest_voted_view)?
|
CURRENT_VIEW => {
|
||||||
|
state.current_view = Some(self.current_view);
|
||||||
|
}
|
||||||
|
HIGHEST_VOTED_VIEW => {
|
||||||
|
state.highest_voted_view = Some(self.highest_voted_view);
|
||||||
|
}
|
||||||
|
LOCAL_HIGH_QC => {
|
||||||
|
state.local_high_qc = Some((&self.local_high_qc).into());
|
||||||
}
|
}
|
||||||
LOCAL_HIGH_QC => ser.serialize_field(LOCAL_HIGH_QC, &self.local_high_qc)?,
|
|
||||||
SAFE_BLOCKS => {
|
SAFE_BLOCKS => {
|
||||||
#[derive(Serialize)]
|
state.safe_blocks = Some((&self.safe_blocks).into());
|
||||||
#[serde(transparent)]
|
|
||||||
struct SafeBlockHelper<'a> {
|
|
||||||
#[serde(serialize_with = "serialize_blocks")]
|
|
||||||
safe_blocks: &'a HashMap<BlockId, Block>,
|
|
||||||
}
|
|
||||||
ser.serialize_field(
|
|
||||||
SAFE_BLOCKS,
|
|
||||||
&SafeBlockHelper {
|
|
||||||
safe_blocks: &self.safe_blocks,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
LAST_VIEW_TIMEOUT_QC => {
|
LAST_VIEW_TIMEOUT_QC => {
|
||||||
ser.serialize_field(LAST_VIEW_TIMEOUT_QC, &self.last_view_timeout_qc)?
|
state.last_view_timeout_qc =
|
||||||
|
Some(self.last_view_timeout_qc.as_ref().map(From::from));
|
||||||
}
|
}
|
||||||
LATEST_COMMITTED_BLOCK => {
|
LATEST_COMMITTED_BLOCK => {
|
||||||
ser.serialize_field(LATEST_COMMITTED_BLOCK, &self.latest_committed_block)?
|
state.latest_committed_block = Some((&self.latest_committed_block).into());
|
||||||
}
|
}
|
||||||
LATEST_COMMITTED_VIEW => {
|
LATEST_COMMITTED_VIEW => {
|
||||||
ser.serialize_field(LATEST_COMMITTED_VIEW, &self.latest_committed_view)?
|
state.latest_committed_view = Some(self.latest_committed_view);
|
||||||
|
}
|
||||||
|
ROOT_COMMITTEE => {
|
||||||
|
state.root_committee = Some((&self.root_committee).into());
|
||||||
}
|
}
|
||||||
ROOT_COMMITTEE => ser.serialize_field(ROOT_COMMITTEE, &self.root_committee)?,
|
|
||||||
PARENT_COMMITTEE => {
|
PARENT_COMMITTEE => {
|
||||||
ser.serialize_field(PARENT_COMMITTEE, &self.parent_committee)?
|
state.parent_committee =
|
||||||
|
Some(self.parent_committee.as_ref().map(From::from));
|
||||||
}
|
}
|
||||||
CHILD_COMMITTEES => {
|
CHILD_COMMITTEES => {
|
||||||
ser.serialize_field(CHILD_COMMITTEES, &self.child_committees)?
|
state.child_committees = Some(self.child_committees.as_slice().into());
|
||||||
}
|
}
|
||||||
COMMITTED_BLOCKS => {
|
COMMITTED_BLOCKS => {
|
||||||
ser.serialize_field(COMMITTED_BLOCKS, &self.committed_blocks)?
|
state.committed_blocks = Some(self.committed_blocks.as_slice().into());
|
||||||
|
}
|
||||||
|
STEP_DURATION => {
|
||||||
|
state.step_duration = Some(self.step_duration);
|
||||||
}
|
}
|
||||||
STEP_DURATION => ser.serialize_field(STEP_DURATION, &self.step_duration)?,
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ser.end()
|
state.serialize(serializer)
|
||||||
} else {
|
} else {
|
||||||
serializer.serialize_none()
|
serializer.serialize_none()
|
||||||
}
|
}
|
||||||
|
377
simulations/src/node/carnot/serde_util.rs
Normal file
377
simulations/src/node/carnot/serde_util.rs
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
|
use serde::{
|
||||||
|
ser::{SerializeSeq, Serializer},
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
serde_block::BlockHelper,
|
||||||
|
serde_id::{BlockIdHelper, NodeIdHelper},
|
||||||
|
standard_qc::StandardQcHelper,
|
||||||
|
timeout_qc::TimeoutQcHelper,
|
||||||
|
};
|
||||||
|
use consensus_engine::{AggregateQc, Block, BlockId, Committee, Qc, StandardQc, TimeoutQc, View};
|
||||||
|
|
||||||
|
#[serde_with::skip_serializing_none]
|
||||||
|
#[serde_with::serde_as]
|
||||||
|
#[derive(Serialize, Default)]
|
||||||
|
pub(crate) struct CarnotState<'a> {
|
||||||
|
pub(crate) node_id: Option<NodeIdHelper>,
|
||||||
|
pub(crate) current_view: Option<View>,
|
||||||
|
pub(crate) highest_voted_view: Option<View>,
|
||||||
|
pub(crate) local_high_qc: Option<StandardQcHelper>,
|
||||||
|
pub(crate) safe_blocks: Option<SafeBlocksHelper<'a>>,
|
||||||
|
pub(crate) last_view_timeout_qc: Option<Option<TimeoutQcHelper<'a>>>,
|
||||||
|
pub(crate) latest_committed_block: Option<BlockHelper>,
|
||||||
|
pub(crate) latest_committed_view: Option<View>,
|
||||||
|
pub(crate) root_committee: Option<CommitteeHelper<'a>>,
|
||||||
|
pub(crate) parent_committee: Option<Option<CommitteeHelper<'a>>>,
|
||||||
|
pub(crate) child_committees: Option<CommitteesHelper<'a>>,
|
||||||
|
pub(crate) committed_blocks: Option<CommittedBlockHelper<'a>>,
|
||||||
|
#[serde_as(as = "Option<serde_with::DurationMilliSeconds>")]
|
||||||
|
pub(crate) step_duration: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a super::CarnotState> for CarnotState<'a> {
|
||||||
|
fn from(value: &'a super::CarnotState) -> Self {
|
||||||
|
Self {
|
||||||
|
node_id: Some(value.node_id.into()),
|
||||||
|
current_view: Some(value.current_view),
|
||||||
|
highest_voted_view: Some(value.highest_voted_view),
|
||||||
|
local_high_qc: Some(StandardQcHelper::from(&value.local_high_qc)),
|
||||||
|
safe_blocks: Some(SafeBlocksHelper::from(&value.safe_blocks)),
|
||||||
|
last_view_timeout_qc: Some(value.last_view_timeout_qc.as_ref().map(From::from)),
|
||||||
|
latest_committed_block: Some(BlockHelper::from(&value.latest_committed_block)),
|
||||||
|
latest_committed_view: Some(value.latest_committed_view),
|
||||||
|
root_committee: Some(CommitteeHelper::from(&value.root_committee)),
|
||||||
|
parent_committee: Some(value.parent_committee.as_ref().map(From::from)),
|
||||||
|
child_committees: Some(CommitteesHelper::from(value.child_committees.as_slice())),
|
||||||
|
committed_blocks: Some(CommittedBlockHelper::from(
|
||||||
|
value.committed_blocks.as_slice(),
|
||||||
|
)),
|
||||||
|
step_duration: Some(value.step_duration),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SafeBlocksHelper<'a>(&'a HashMap<BlockId, Block>);
|
||||||
|
|
||||||
|
impl<'a> From<&'a HashMap<BlockId, Block>> for SafeBlocksHelper<'a> {
|
||||||
|
fn from(val: &'a HashMap<BlockId, Block>) -> Self {
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for SafeBlocksHelper<'a> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let iter = self.0.values();
|
||||||
|
let mut s = serializer.serialize_seq(Some(iter.size_hint().0))?;
|
||||||
|
for b in iter {
|
||||||
|
s.serialize_element(&BlockHelper::from(b))?;
|
||||||
|
}
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CommitteeHelper<'a>(&'a Committee);
|
||||||
|
|
||||||
|
impl<'a> From<&'a Committee> for CommitteeHelper<'a> {
|
||||||
|
fn from(val: &'a Committee) -> Self {
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for CommitteeHelper<'a> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let iter = self.0.iter();
|
||||||
|
let mut s = serializer.serialize_seq(Some(iter.size_hint().0))?;
|
||||||
|
for id in iter {
|
||||||
|
s.serialize_element(&NodeIdHelper::from(*id))?;
|
||||||
|
}
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CommitteesHelper<'a>(&'a [Committee]);
|
||||||
|
|
||||||
|
impl<'a> From<&'a [Committee]> for CommitteesHelper<'a> {
|
||||||
|
fn from(val: &'a [Committee]) -> Self {
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for CommitteesHelper<'a> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut s = serializer.serialize_seq(Some(self.0.len()))?;
|
||||||
|
for c in self.0 {
|
||||||
|
s.serialize_element(&CommitteeHelper::from(c))?;
|
||||||
|
}
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct CommittedBlockHelper<'a>(&'a [BlockId]);
|
||||||
|
|
||||||
|
impl<'a> From<&'a [BlockId]> for CommittedBlockHelper<'a> {
|
||||||
|
fn from(val: &'a [BlockId]) -> Self {
|
||||||
|
Self(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serialize for CommittedBlockHelper<'a> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut s = serializer.serialize_seq(Some(self.0.len()))?;
|
||||||
|
for c in self.0 {
|
||||||
|
s.serialize_element(&BlockIdHelper::from(*c))?;
|
||||||
|
}
|
||||||
|
s.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod standard_qc {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct StandardQcHelper {
|
||||||
|
view: View,
|
||||||
|
id: serde_id::BlockIdHelper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&StandardQc> for StandardQcHelper {
|
||||||
|
fn from(val: &StandardQc) -> Self {
|
||||||
|
Self {
|
||||||
|
view: val.view,
|
||||||
|
id: val.id.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: Serializer>(t: &StandardQc, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
StandardQcHelper::from(t).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod aggregate_qc {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct AggregateQcHelper<'a> {
|
||||||
|
#[serde(serialize_with = "standard_qc::serialize")]
|
||||||
|
high_qc: &'a StandardQc,
|
||||||
|
view: View,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a AggregateQc> for AggregateQcHelper<'a> {
|
||||||
|
fn from(t: &'a AggregateQc) -> Self {
|
||||||
|
Self {
|
||||||
|
high_qc: &t.high_qc,
|
||||||
|
view: t.view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: serde::Serializer>(
|
||||||
|
t: &AggregateQc,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
AggregateQcHelper::from(t).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod qc {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub(crate) enum QcHelper<'a> {
|
||||||
|
Standard(#[serde(serialize_with = "standard_qc::serialize")] &'a StandardQc),
|
||||||
|
Aggregate(aggregate_qc::AggregateQcHelper<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: serde::Serializer>(t: &Qc, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
let qc = match t {
|
||||||
|
Qc::Standard(s) => QcHelper::Standard(s),
|
||||||
|
Qc::Aggregated(a) => QcHelper::Aggregate(aggregate_qc::AggregateQcHelper::from(a)),
|
||||||
|
};
|
||||||
|
qc.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod timeout_qc {
|
||||||
|
use super::*;
|
||||||
|
use consensus_engine::NodeId;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct TimeoutQcHelper<'a> {
|
||||||
|
view: View,
|
||||||
|
#[serde(serialize_with = "standard_qc::serialize")]
|
||||||
|
high_qc: &'a StandardQc,
|
||||||
|
#[serde(serialize_with = "serde_id::serialize_node_id")]
|
||||||
|
sender: NodeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a TimeoutQc> for TimeoutQcHelper<'a> {
|
||||||
|
fn from(value: &'a TimeoutQc) -> Self {
|
||||||
|
Self {
|
||||||
|
view: value.view(),
|
||||||
|
high_qc: value.high_qc(),
|
||||||
|
sender: value.sender(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: serde::Serializer>(
|
||||||
|
t: &TimeoutQc,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
TimeoutQcHelper::from(t).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod serde_block {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub(crate) struct BlockHelper {
|
||||||
|
view: View,
|
||||||
|
id: BlockIdHelper,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Block> for BlockHelper {
|
||||||
|
fn from(val: &Block) -> Self {
|
||||||
|
Self {
|
||||||
|
view: val.view,
|
||||||
|
id: val.id.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: serde::Serializer>(t: &Block, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
BlockHelper::from(t).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod serde_id {
|
||||||
|
use consensus_engine::{BlockId, NodeId};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub(crate) struct BlockIdHelper(#[serde(with = "serde_array32")] [u8; 32]);
|
||||||
|
|
||||||
|
impl From<BlockId> for BlockIdHelper {
|
||||||
|
fn from(val: BlockId) -> Self {
|
||||||
|
Self(val.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub(crate) struct NodeIdHelper(#[serde(with = "serde_array32")] [u8; 32]);
|
||||||
|
|
||||||
|
impl From<NodeId> for NodeIdHelper {
|
||||||
|
fn from(val: NodeId) -> Self {
|
||||||
|
Self(val.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_node_id<S: serde::Serializer>(
|
||||||
|
t: &NodeId,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
NodeIdHelper::from(*t).serialize(serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod serde_array32 {
|
||||||
|
use super::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
const MAX_SERIALIZATION_LENGTH: usize = 32 * 2 + 2;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static STRING_BUFFER: RefCell<String> = RefCell::new(String::with_capacity(MAX_SERIALIZATION_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S: serde::Serializer>(
|
||||||
|
t: &[u8; 32],
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
if serializer.is_human_readable() {
|
||||||
|
STRING_BUFFER.with(|s| {
|
||||||
|
let mut s = s.borrow_mut();
|
||||||
|
s.clear();
|
||||||
|
s.push_str("0x");
|
||||||
|
for v in t {
|
||||||
|
std::fmt::write(&mut *s, format_args!("{:02x}", v)).unwrap();
|
||||||
|
}
|
||||||
|
s.serialize(serializer)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
t.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
if deserializer.is_human_readable() {
|
||||||
|
<&str>::deserialize(deserializer).and_then(|s| {
|
||||||
|
super::parse_hex_from_str::<32>(s)
|
||||||
|
.map_err(<D::Error as serde::de::Error>::custom)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let x = <&[u8]>::deserialize(deserializer)?;
|
||||||
|
<[u8; 32]>::try_from(x).map_err(<D::Error as serde::de::Error>::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
enum DecodeError {
|
||||||
|
#[error("expected str of length {expected} but got length {actual}")]
|
||||||
|
UnexpectedSize { expected: usize, actual: usize },
|
||||||
|
#[error("invalid character pair '{0}{1}'")]
|
||||||
|
InvalidCharacter(char, char),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_hex_from_str<const N: usize>(mut s: &str) -> Result<[u8; N], DecodeError> {
|
||||||
|
// check if we start with 0x or not
|
||||||
|
let prefix_len = if s.starts_with("0x") || s.starts_with("0X") {
|
||||||
|
s = &s[2..];
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if s.len() != N * 2 {
|
||||||
|
return Err(DecodeError::UnexpectedSize {
|
||||||
|
expected: N * 2 + prefix_len,
|
||||||
|
actual: s.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = [0; N];
|
||||||
|
|
||||||
|
for (chars, byte) in s.as_bytes().chunks_exact(2).zip(output.iter_mut()) {
|
||||||
|
let (l, r) = (chars[0] as char, chars[1] as char);
|
||||||
|
match (l.to_digit(16), r.to_digit(16)) {
|
||||||
|
(Some(l), Some(r)) => *byte = (l as u8) << 4 | r as u8,
|
||||||
|
(_, _) => return Err(DecodeError::InvalidCharacter(l, r)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
}
|
@ -39,5 +39,5 @@ path = "src/tests/unhappy.rs"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
metrics = ["nomos-node/metrics"]
|
metrics = ["nomos-node/metrics"]
|
||||||
waku = ["nomos-network/waku", "nomos-mempool/waku", "waku-bindings"]
|
waku = ["nomos-network/waku", "nomos-mempool/waku", "nomos-node/waku", "waku-bindings"]
|
||||||
libp2p = ["nomos-network/libp2p", "nomos-mempool/libp2p", "nomos-libp2p"]
|
libp2p = ["nomos-network/libp2p", "nomos-mempool/libp2p", "nomos-libp2p", "nomos-node/libp2p"]
|
Loading…
x
Reference in New Issue
Block a user