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",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
]
|
||||
|
||||
|
@ -690,9 +691,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.104"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
|
|
@ -31,6 +31,7 @@ criterion = "0.5.1"
|
|||
glob = "0.3.1"
|
||||
rand = "0.8.5"
|
||||
serde_yaml = "0.9.17"
|
||||
serde_json = "1.0.105"
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = { git = "https://github.com/rust-lang/rust-bindgen" , rev = "0de11f0a521611ac8738b7b01d19dddaf3899e66" }
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
mod serde_helpers;
|
||||
mod test_formats;
|
||||
|
||||
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 {
|
||||
type Target = [u8; BYTES_PER_BLOB];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
@ -693,10 +700,8 @@ mod tests {
|
|||
for test_file in test_files {
|
||||
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 (Ok(blob), Ok(commitment)) = (
|
||||
test.input.get_blob(),
|
||||
test.input.get_commitment()
|
||||
) else {
|
||||
let (Ok(blob), Ok(commitment)) = (test.input.get_blob(), test.input.get_commitment())
|
||||
else {
|
||||
assert!(test.get_output().is_none());
|
||||
continue;
|
||||
};
|
||||
|
@ -727,7 +732,7 @@ mod tests {
|
|||
test.input.get_commitment(),
|
||||
test.input.get_z(),
|
||||
test.input.get_y(),
|
||||
test.input.get_proof()
|
||||
test.input.get_proof(),
|
||||
) else {
|
||||
assert!(test.get_output().is_none());
|
||||
continue;
|
||||
|
@ -758,7 +763,7 @@ mod tests {
|
|||
let (Ok(blob), Ok(commitment), Ok(proof)) = (
|
||||
test.input.get_blob(),
|
||||
test.input.get_commitment(),
|
||||
test.input.get_proof()
|
||||
test.input.get_proof(),
|
||||
) else {
|
||||
assert!(test.get_output().is_none());
|
||||
continue;
|
||||
|
@ -789,7 +794,7 @@ mod tests {
|
|||
let (Ok(blobs), Ok(commitments), Ok(proofs)) = (
|
||||
test.input.get_blobs(),
|
||||
test.input.get_commitments(),
|
||||
test.input.get_proofs()
|
||||
test.input.get_proofs(),
|
||||
) else {
|
||||
assert!(test.get_output().is_none());
|
||||
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