1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Serde serialization and deserialization for the basic types in this crate.

use crate::{Blob, Bytes32, Bytes48};
use alloc::string::String;
use alloc::vec::Vec;
use serde::{de::Error, 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())))
}

fn deserialize_hex<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
    let s = String::deserialize(deserializer)?;
    let hex_bytes = s.strip_prefix("0x").unwrap_or(&s);
    hex::decode(hex_bytes).map_err(Error::custom)
}

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<'de>>(deserializer: D) -> Result<Self, D::Error> {
        Blob::from_bytes(&deserialize_hex(deserializer)?).map_err(Error::custom)
    }
}

impl Serialize for Bytes48 {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serialize_bytes(self.bytes, serializer)
    }
}

impl<'de> Deserialize<'de> for Bytes48 {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        Bytes48::from_bytes(&deserialize_hex(deserializer)?).map_err(Error::custom)
    }
}

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>,
    {
        Bytes32::from_bytes(&deserialize_hex(deserializer)?).map_err(Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use super::super::*;
    use rand::{rngs::ThreadRng, Rng};

    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() -> &'static Path {
        Path::new("../../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, &kzg_settings).unwrap();
        let proof =
            KZGProof::compute_blob_kzg_proof(&blob, &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, &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(), '"');
    }
}