Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: explicit deny_unknown_fields for protobuf serde deserialization #200

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions node/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions node/libs/protobuf/examples/conformance_test/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@ mod proto;
/// Decodes a generated proto message from json for arbitrary ReflectMessage.
fn decode_json_proto<T: ReflectMessage + Default>(json: &str) -> anyhow::Result<T> {
let mut deserializer = serde_json::Deserializer::from_str(json);
let proto: T = zksync_protobuf::serde::deserialize_proto(&mut deserializer)?;
let proto: T = zksync_protobuf::serde::Deserialize {
deny_unknown_fields: true,
}
.proto(&mut deserializer)?;
deserializer.end()?;
Ok(proto)
}

/// Encodes a generated proto message to json for arbitrary ReflectMessage.
fn encode_json_proto<T: ReflectMessage>(proto: &T) -> String {
let mut serializer = serde_json::Serializer::pretty(vec![]);
zksync_protobuf::serde::serialize_proto(proto, &mut serializer).unwrap();
zksync_protobuf::serde::Serialize
.proto(proto, &mut serializer)
.unwrap();
String::from_utf8(serializer.into_inner()).unwrap()
}

Expand Down
163 changes: 117 additions & 46 deletions node/libs/protobuf/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,135 @@
//! therefore it is suitable for version control.
//! WARNING: Currently this serde implementation uses reflection,
//! so it is not very efficient.
use crate::ProtoFmt;
use crate::{ProtoFmt, ProtoRepr};
use prost::Message as _;
use prost_reflect::ReflectMessage;

/// ProtoFmt wrapper which implements serde Serialize/Deserialize.
#[derive(Debug, Clone)]
pub struct Serde<T>(pub T);
/// Serialization options.
pub struct Serialize;

impl<T: ProtoFmt> serde::Serialize for Serde<T> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
serialize(&self.0, s)
impl Serialize {
/// Serializes ReflectMessage.
pub fn proto<T: ReflectMessage, S: serde::Serializer>(
&self,
x: &T,
s: S,
) -> Result<S::Ok, S::Error> {
let opts = prost_reflect::SerializeOptions::new();
x.transcode_to_dynamic().serialize_with_options(s, &opts)
}
}

impl<'de, T: ProtoFmt> serde::Deserialize<'de> for Serde<T> {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Ok(Self(deserialize(d)?))
/// Serializes ProtoFmt.
pub fn proto_fmt<T: ProtoFmt, S: serde::Serializer>(
&self,
x: &T,
s: S,
) -> Result<S::Ok, S::Error> {
self.proto(&x.build(), s)
}
}

/// Implementation of serde::Serialize for arbitrary ReflectMessage.
pub fn serialize_proto<T: ReflectMessage, S: serde::Serializer>(
x: &T,
s: S,
) -> Result<S::Ok, S::Error> {
let opts = prost_reflect::SerializeOptions::new();
x.transcode_to_dynamic().serialize_with_options(s, &opts)
}
/// Serializes ProtoFmt to json.
pub fn proto_fmt_to_json<T: ProtoFmt>(&self, x: &T) -> String {
let mut s = serde_json::Serializer::pretty(vec![]);
self.proto_fmt(x, &mut s).unwrap();
String::from_utf8(s.into_inner()).unwrap()
}

/// Implementation of serde::Serialize for arbitrary ProtoFmt.
pub fn serialize<T: ProtoFmt, S: serde::Serializer>(x: &T, s: S) -> Result<S::Ok, S::Error> {
serialize_proto(&x.build(), s)
}
/// Serializes ProtoFmt to yaml
pub fn proto_fmt_to_yaml<T: ProtoFmt>(&self, x: &T) -> String {
let mut s = serde_yaml::Serializer::new(vec![]);
self.proto_fmt(x, &mut s).unwrap();
String::from_utf8(s.into_inner().unwrap()).unwrap()
}

/// Implementation of serde::Deserialize for arbitrary ReflectMessage denying unknown fields
pub fn deserialize_proto<'de, T: ReflectMessage + Default, D: serde::Deserializer<'de>>(
d: D,
) -> Result<T, D::Error> {
deserialize_proto_with_options(d, true)
/// Serializes ProtoRepr.
pub fn proto_repr<T: ProtoRepr, S: serde::Serializer>(
&self,
x: &T::Type,
s: S,
) -> Result<S::Ok, S::Error> {
self.proto(&T::build(x), s)
}

/// Serializes ProtoRepr to json.
pub fn proto_repr_to_json<T: ProtoRepr>(&self, x: &T::Type) -> String {
let mut s = serde_json::Serializer::pretty(vec![]);
self.proto_repr::<T, _>(x, &mut s).unwrap();
String::from_utf8(s.into_inner()).unwrap()
}

/// Serializes ProtoRepr to yaml
pub fn proto_repr_to_yaml<T: ProtoRepr>(&self, x: &T::Type) -> String {
let mut s = serde_yaml::Serializer::new(vec![]);
self.proto_repr::<T, _>(x, &mut s).unwrap();
String::from_utf8(s.into_inner().unwrap()).unwrap()
}
}

/// Implementation of serde::Deserialize for arbitrary ReflectMessage with deny_unknown_fields option
pub fn deserialize_proto_with_options<
'de,
T: ReflectMessage + Default,
D: serde::Deserializer<'de>,
>(
d: D,
deny_unknown_fields: bool,
) -> Result<T, D::Error> {
let mut p = T::default();
let options = prost_reflect::DeserializeOptions::new().deny_unknown_fields(deny_unknown_fields);
let msg = prost_reflect::DynamicMessage::deserialize_with_options(p.descriptor(), d, &options)?;
p.merge(msg.encode_to_vec().as_slice()).unwrap();
Ok(p)
/// Deserialization options.
#[derive(Default)]
pub struct Deserialize {
/// true => returns an error when an unknown field is found.
/// false => silently ignores unknown fields.
pub deny_unknown_fields: bool,
}

/// Implementation of serde::Deserialize for arbitrary ProtoFmt.
pub fn deserialize<'de, T: ProtoFmt, D: serde::Deserializer<'de>>(d: D) -> Result<T, D::Error> {
T::read(&deserialize_proto(d)?).map_err(serde::de::Error::custom)
impl Deserialize {
/// Implementation of serde::Deserialize for arbitrary ReflectMessage with deny_unknown_fields option
pub fn proto<'de, T: ReflectMessage + Default, D: serde::Deserializer<'de>>(
&self,
d: D,
) -> Result<T, D::Error> {
let mut p = T::default();
let options =
prost_reflect::DeserializeOptions::new().deny_unknown_fields(self.deny_unknown_fields);
let msg =
prost_reflect::DynamicMessage::deserialize_with_options(p.descriptor(), d, &options)?;
p.merge(msg.encode_to_vec().as_slice()).unwrap();
Ok(p)
}

/// Implementation of serde::Deserialize for arbitrary ProtoFmt.
pub fn proto_fmt<'de, T: ProtoFmt, D: serde::Deserializer<'de>>(
&self,
d: D,
) -> Result<T, D::Error> {
T::read(&self.proto(d)?).map_err(serde::de::Error::custom)
}

/// Deserializes ProtoFmt from json.
pub fn proto_fmt_from_json<T: ProtoFmt>(&self, json: &str) -> anyhow::Result<T> {
let mut d = serde_json::Deserializer::from_str(json);
let p = self.proto_fmt(&mut d)?;
d.end()?;
Ok(p)
}

/// Deserializes ProtoFmt from yaml.
pub fn proto_fmt_from_yaml<T: ProtoFmt>(&self, yaml: &str) -> anyhow::Result<T> {
Ok(self.proto_fmt(serde_yaml::Deserializer::from_str(yaml))?)
}

/// Implementation of serde::Deserialize for arbitrary ProtoFmt.
pub fn proto_repr<'de, T: ProtoRepr, D: serde::Deserializer<'de>>(
&self,
d: D,
) -> Result<T::Type, D::Error> {
self.proto::<T, D>(d)?
.read()
.map_err(serde::de::Error::custom)
}

/// Deserializes ProtoRepr from json.
pub fn proto_repr_from_json<T: ProtoRepr>(&self, json: &str) -> anyhow::Result<T::Type> {
let mut d = serde_json::Deserializer::from_str(json);
let p = self.proto_repr::<T, _>(&mut d)?;
d.end()?;
Ok(p)
}

/// Deserializes ProtoRepr from yaml.
pub fn proto_repr_from_yaml<T: ProtoRepr>(&self, yaml: &str) -> anyhow::Result<T::Type> {
Ok(self.proto_repr::<T, _>(serde_yaml::Deserializer::from_str(yaml))?)
}
}
24 changes: 19 additions & 5 deletions node/libs/protobuf/src/testonly/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! Testonly utilities.
use super::{canonical, canonical_raw, decode, encode, read_fields, ProtoFmt, ProtoRepr, Wire};
use super::{
canonical, canonical_raw, decode, encode, read_fields,
serde::{Deserialize, Serialize},
ProtoFmt, ProtoRepr, Wire,
};
use prost::Message as _;
use prost_reflect::ReflectMessage;
use rand::{
Expand Down Expand Up @@ -114,24 +118,34 @@ fn decode_proto<X: ProtoConv>(bytes: &[u8]) -> anyhow::Result<X::Type> {

fn encode_json<X: ProtoConv>(msg: &X::Type) -> String {
let mut s = serde_json::Serializer::pretty(vec![]);
crate::serde::serialize_proto(&X::build(msg), &mut s).unwrap();
Serialize.proto(&X::build(msg), &mut s).unwrap();
String::from_utf8(s.into_inner()).unwrap()
}

fn decode_json<X: ProtoConv>(json: &str) -> anyhow::Result<X::Type> {
let mut d = serde_json::Deserializer::from_str(json);
X::read(&crate::serde::deserialize_proto(&mut d)?)
X::read(
&Deserialize {
deny_unknown_fields: true,
}
.proto(&mut d)?,
)
}

fn encode_yaml<X: ProtoConv>(msg: &X::Type) -> String {
let mut s = serde_yaml::Serializer::new(vec![]);
crate::serde::serialize_proto(&X::build(msg), &mut s).unwrap();
Serialize.proto(&X::build(msg), &mut s).unwrap();
String::from_utf8(s.into_inner().unwrap()).unwrap()
}

fn decode_yaml<X: ProtoConv>(yaml: &str) -> anyhow::Result<X::Type> {
let d = serde_yaml::Deserializer::from_str(yaml);
X::read(&crate::serde::deserialize_proto(d)?)
X::read(
&Deserialize {
deny_unknown_fields: true,
}
.proto(d)?,
)
}

/// Wrapper for `ProtoRepr`, implementing ProtoConv;
Expand Down
4 changes: 2 additions & 2 deletions node/tools/src/bin/localnet_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{
};
use zksync_consensus_roles::{node, validator};
use zksync_consensus_tools::config;
use zksync_protobuf::serde::Serde;
use zksync_protobuf::serde::Serialize;

/// Command line arguments.
#[derive(Debug, Parser)]
Expand Down Expand Up @@ -111,7 +111,7 @@ fn main() -> anyhow::Result<()> {
.context("fs::set_permissions()")?;

let config_path = root.join("config.json");
fs::write(&config_path, config::encode_json(&Serde(cfg))).context("fs::write()")?;
fs::write(&config_path, Serialize.proto_fmt_to_json(&cfg)).context("fs::write()")?;
fs::set_permissions(&config_path, Permissions::from_mode(0o600))
.context("fs::set_permissions()")?;
}
Expand Down
Loading
Loading