feat: add serde impls to Blob and Bytes48 (#342)
* feat: add serde impls to Blob and Bytes48 * remove duplicate serde file * cargo fmt * serialize and deserialize with prefix * cargo fmt * use different trusted setup based on minimal spec * add Bytes32 serde impls
This commit is contained in:
parent
4c0d477c0f
commit
666a9de002
|
@ -93,6 +93,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -690,9 +691,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.104"
|
version = "1.0.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
|
|
@ -31,6 +31,7 @@ criterion = "0.5.1"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
serde_yaml = "0.9.17"
|
serde_yaml = "0.9.17"
|
||||||
|
serde_json = "1.0.105"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = { git = "https://github.com/rust-lang/rust-bindgen" , rev = "0de11f0a521611ac8738b7b01d19dddaf3899e66" }
|
bindgen = { git = "https://github.com/rust-lang/rust-bindgen" , rev = "0de11f0a521611ac8738b7b01d19dddaf3899e66" }
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
mod serde_helpers;
|
||||||
mod test_formats;
|
mod test_formats;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||||
|
@ -492,6 +493,12 @@ impl Deref for Bytes48 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Bytes48 {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for Blob {
|
impl Deref for Blob {
|
||||||
type Target = [u8; BYTES_PER_BLOB];
|
type Target = [u8; BYTES_PER_BLOB];
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
|
@ -693,10 +700,8 @@ mod tests {
|
||||||
for test_file in test_files {
|
for test_file in test_files {
|
||||||
let yaml_data = fs::read_to_string(test_file).unwrap();
|
let yaml_data = fs::read_to_string(test_file).unwrap();
|
||||||
let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
|
let test: compute_blob_kzg_proof::Test = serde_yaml::from_str(&yaml_data).unwrap();
|
||||||
let (Ok(blob), Ok(commitment)) = (
|
let (Ok(blob), Ok(commitment)) = (test.input.get_blob(), test.input.get_commitment())
|
||||||
test.input.get_blob(),
|
else {
|
||||||
test.input.get_commitment()
|
|
||||||
) else {
|
|
||||||
assert!(test.get_output().is_none());
|
assert!(test.get_output().is_none());
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -727,7 +732,7 @@ mod tests {
|
||||||
test.input.get_commitment(),
|
test.input.get_commitment(),
|
||||||
test.input.get_z(),
|
test.input.get_z(),
|
||||||
test.input.get_y(),
|
test.input.get_y(),
|
||||||
test.input.get_proof()
|
test.input.get_proof(),
|
||||||
) else {
|
) else {
|
||||||
assert!(test.get_output().is_none());
|
assert!(test.get_output().is_none());
|
||||||
continue;
|
continue;
|
||||||
|
@ -758,7 +763,7 @@ mod tests {
|
||||||
let (Ok(blob), Ok(commitment), Ok(proof)) = (
|
let (Ok(blob), Ok(commitment), Ok(proof)) = (
|
||||||
test.input.get_blob(),
|
test.input.get_blob(),
|
||||||
test.input.get_commitment(),
|
test.input.get_commitment(),
|
||||||
test.input.get_proof()
|
test.input.get_proof(),
|
||||||
) else {
|
) else {
|
||||||
assert!(test.get_output().is_none());
|
assert!(test.get_output().is_none());
|
||||||
continue;
|
continue;
|
||||||
|
@ -789,7 +794,7 @@ mod tests {
|
||||||
let (Ok(blobs), Ok(commitments), Ok(proofs)) = (
|
let (Ok(blobs), Ok(commitments), Ok(proofs)) = (
|
||||||
test.input.get_blobs(),
|
test.input.get_blobs(),
|
||||||
test.input.get_commitments(),
|
test.input.get_commitments(),
|
||||||
test.input.get_proofs()
|
test.input.get_proofs(),
|
||||||
) else {
|
) else {
|
||||||
assert!(test.get_output().is_none());
|
assert!(test.get_output().is_none());
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
//! Serde serialization and deserialization for the basic types in this crate.
|
||||||
|
use crate::{Blob, Bytes32, Bytes48};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
|
/// Serialize a byte vec as a hex string with 0x prefix
|
||||||
|
pub fn serialize_bytes<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
T: AsRef<[u8]>,
|
||||||
|
{
|
||||||
|
s.serialize_str(&format!("0x{}", hex::encode(x.as_ref())))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Blob {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serialize_bytes(self.bytes, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Blob {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
let bytes_res = match value.strip_prefix("0x") {
|
||||||
|
Some(value) => hex::decode(value),
|
||||||
|
None => hex::decode(&value),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = bytes_res.map_err(|e| serde::de::Error::custom(e.to_string()))?;
|
||||||
|
Blob::from_bytes(bytes.as_slice()).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Bytes48 {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serialize_bytes(self.bytes, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Bytes48 {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
let bytes_res = match value.strip_prefix("0x") {
|
||||||
|
Some(value) => hex::decode(value),
|
||||||
|
None => hex::decode(&value),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = bytes_res.map_err(|e| serde::de::Error::custom(e.to_string()))?;
|
||||||
|
Bytes48::from_bytes(bytes.as_slice())
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Bytes32 {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serialize_bytes(self.bytes, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Bytes32 {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?;
|
||||||
|
let bytes_res = match value.strip_prefix("0x") {
|
||||||
|
Some(value) => hex::decode(value),
|
||||||
|
None => hex::decode(&value),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = bytes_res.map_err(|e| serde::de::Error::custom(e.to_string()))?;
|
||||||
|
Bytes32::from_bytes(bytes.as_slice())
|
||||||
|
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::super::*;
|
||||||
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn generate_random_blob(rng: &mut ThreadRng) -> Blob {
|
||||||
|
let mut arr = [0u8; BYTES_PER_BLOB];
|
||||||
|
rng.fill(&mut arr[..]);
|
||||||
|
// Ensure that the blob is canonical by ensuring that
|
||||||
|
// each field element contained in the blob is < BLS_MODULUS
|
||||||
|
for i in 0..FIELD_ELEMENTS_PER_BLOB {
|
||||||
|
arr[i * BYTES_PER_FIELD_ELEMENT] = 0;
|
||||||
|
}
|
||||||
|
arr.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trusted_setup_file() -> PathBuf {
|
||||||
|
if cfg!(feature = "minimal-spec") {
|
||||||
|
PathBuf::from("../../src/trusted_setup_4.txt")
|
||||||
|
} else {
|
||||||
|
PathBuf::from("../../src/trusted_setup.txt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_roundtrip() {
|
||||||
|
// load setup so we can create commitments and blobs
|
||||||
|
let trusted_setup_file = trusted_setup_file();
|
||||||
|
assert!(trusted_setup_file.exists());
|
||||||
|
let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
|
||||||
|
|
||||||
|
// generate blob, commitment, proof
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let blob = generate_random_blob(&mut rng);
|
||||||
|
let commitment =
|
||||||
|
KZGCommitment::blob_to_kzg_commitment(blob.clone(), &kzg_settings).unwrap();
|
||||||
|
let proof =
|
||||||
|
KZGProof::compute_blob_kzg_proof(blob.clone(), commitment.to_bytes(), &kzg_settings)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// check blob serialization
|
||||||
|
let blob_serialized = serde_json::to_string(&blob).unwrap();
|
||||||
|
let blob_deserialized: Blob = serde_json::from_str(&blob_serialized).unwrap();
|
||||||
|
assert_eq!(blob, blob_deserialized);
|
||||||
|
|
||||||
|
// check commitment serialization
|
||||||
|
let commitment_serialized = serde_json::to_string(&commitment.to_bytes()).unwrap();
|
||||||
|
let commitment_deserialized: Bytes48 =
|
||||||
|
serde_json::from_str(&commitment_serialized).unwrap();
|
||||||
|
assert_eq!(commitment.to_bytes(), commitment_deserialized);
|
||||||
|
|
||||||
|
// check proof serialization
|
||||||
|
let proof_serialized = serde_json::to_string(&proof.to_bytes()).unwrap();
|
||||||
|
let proof_deserialized: Bytes48 = serde_json::from_str(&proof_serialized).unwrap();
|
||||||
|
assert_eq!(proof.to_bytes(), proof_deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_blob_with_prefix() {
|
||||||
|
// generate blob
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let blob = generate_random_blob(&mut rng);
|
||||||
|
|
||||||
|
// check blob serialization
|
||||||
|
let blob_serialized = serde_json::to_string(&blob).unwrap();
|
||||||
|
|
||||||
|
// check that this begins with a quote and 0x
|
||||||
|
let mut chars = blob_serialized.chars();
|
||||||
|
assert_eq!(chars.next().unwrap(), '"');
|
||||||
|
assert_eq!(chars.next().unwrap(), '0');
|
||||||
|
assert_eq!(chars.next().unwrap(), 'x');
|
||||||
|
|
||||||
|
// check that it ends with a quote (sanity check)
|
||||||
|
assert_eq!(chars.last().unwrap(), '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_bytes_48_with_prefix() {
|
||||||
|
// load setup so we can create a commitments
|
||||||
|
let trusted_setup_file = trusted_setup_file();
|
||||||
|
assert!(trusted_setup_file.exists());
|
||||||
|
let kzg_settings = KZGSettings::load_trusted_setup_file(trusted_setup_file).unwrap();
|
||||||
|
|
||||||
|
// generate blob just to calculate a commitment
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let blob = generate_random_blob(&mut rng);
|
||||||
|
let commitment =
|
||||||
|
KZGCommitment::blob_to_kzg_commitment(blob.clone(), &kzg_settings).unwrap();
|
||||||
|
|
||||||
|
// check blob serialization
|
||||||
|
let blob_serialized = serde_json::to_string(&commitment.to_bytes()).unwrap();
|
||||||
|
|
||||||
|
// check that this begins with a quote and 0x
|
||||||
|
let mut chars = blob_serialized.chars();
|
||||||
|
assert_eq!(chars.next().unwrap(), '"');
|
||||||
|
assert_eq!(chars.next().unwrap(), '0');
|
||||||
|
assert_eq!(chars.next().unwrap(), 'x');
|
||||||
|
|
||||||
|
// check that it ends with a quote (sanity check)
|
||||||
|
assert_eq!(chars.last().unwrap(), '"');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue