semaphore_rs_proof/
packing.rs

1use std::{
2    fmt::Display,
3    str::{from_utf8, FromStr},
4};
5
6use alloy_core::sol_types::{
7    sol_data::{FixedArray, Uint},
8    SolType, SolValue,
9};
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
12use crate::Proof;
13use semaphore_rs_utils::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes};
14
15/// A packed proof is a representation of the ZKP in a single attribute (as
16/// opposed to array of arrays) which is easier to transport
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct PackedProof(pub [u8; 256]);
19
20impl From<Proof> for PackedProof {
21    fn from(proof: Proof) -> Self {
22        let flat_proof = [
23            proof.0 .0,
24            proof.0 .1,
25            proof.1 .0[0],
26            proof.1 .0[1],
27            proof.1 .1[0],
28            proof.1 .1[1],
29            proof.2 .0,
30            proof.2 .1,
31        ];
32
33        let bytes = flat_proof.abi_encode();
34        let mut encoded = [0u8; 256];
35        encoded.copy_from_slice(&bytes[..256]);
36        Self(encoded)
37    }
38}
39
40impl From<PackedProof> for Proof {
41    fn from(proof: PackedProof) -> Self {
42        let decoded = FixedArray::<Uint<256>, 8>::abi_decode(&proof.0).unwrap();
43
44        let a = (decoded[0], decoded[1]);
45        let b = ([decoded[2], decoded[3]], [decoded[4], decoded[5]]);
46        let c = (decoded[6], decoded[7]);
47
48        Self(a, b, c)
49    }
50}
51
52impl Display for PackedProof {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        let hex = bytes_to_hex::<256, 514>(&self.0);
55        write!(
56            f,
57            "{}",
58            from_utf8(&hex).expect("failed to convert to string")
59        )
60    }
61}
62
63impl FromStr for PackedProof {
64    type Err = hex::FromHexError;
65
66    fn from_str(s: &str) -> Result<Self, Self::Err> {
67        bytes_from_hex::<256>(s).map(Self)
68    }
69}
70
71impl Serialize for PackedProof {
72    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
73        serialize_bytes::<256, 514, S>(serializer, &self.0)
74    }
75}
76
77impl<'de> Deserialize<'de> for PackedProof {
78    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
79        let bytes = deserialize_bytes::<256, _>(deserializer)?;
80        Ok(Self(bytes))
81    }
82}
83
84#[cfg(test)]
85pub mod test {
86    use super::*;
87    use ruint::aliases::U256;
88
89    #[test]
90    fn test_serializing_proof_into_packed_proof() {
91        let proof = Proof(
92            (U256::from(1), U256::from(2)),
93            (
94                [U256::from(3), U256::from(4)],
95                [U256::from(5), U256::from(6)],
96            ),
97            (U256::from(7), U256::from(8)),
98        );
99
100        let packed_proof = PackedProof::from(proof);
101
102        assert_eq!(packed_proof.to_string(), "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008");
103
104        let proof2 = Proof::from(packed_proof);
105
106        assert_eq!(proof, proof2);
107    }
108
109    #[test]
110    fn test_parse_from_string() {
111        let packed_proof_str =  "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
112
113        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
114
115        let expected_proof = Proof(
116            (U256::from(1), U256::from(2)),
117            (
118                [U256::from(3), U256::from(4)],
119                [U256::from(5), U256::from(6)],
120            ),
121            (U256::from(7), U256::from(8)),
122        );
123
124        let proof: Proof = packed_proof.into();
125
126        assert_eq!(proof, expected_proof);
127    }
128
129    #[test]
130    fn test_parse_from_string_without_prefix() {
131        // note the lack of 0x prefix
132        let packed_proof_str =  "00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
133
134        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
135
136        let expected_proof = Proof(
137            (U256::from(5), U256::from(6)),
138            (
139                [U256::from(3), U256::from(4)],
140                [U256::from(5), U256::from(6)],
141            ),
142            (U256::from(7), U256::from(8)),
143        );
144
145        let proof: Proof = packed_proof.into();
146
147        assert_eq!(proof, expected_proof);
148    }
149
150    #[test]
151    fn test_serialize_proof_to_json() {
152        let packed_proof_str =  "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
153
154        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
155        let proof: Proof = packed_proof.into();
156
157        let serialized = serde_json::to_value(proof).unwrap();
158
159        assert_eq!(
160            serialized,
161            serde_json::json!([
162                ["0x1", "0x2"],
163                [["0x3", "0x4"], ["0x5", "0x6"]],
164                ["0x7", "0x8"]
165            ])
166        );
167    }
168
169    #[test]
170    fn test_serialize_proof_to_json_real_numbers() {
171        let packed_proof_str =  "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";
172
173        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
174        let proof: Proof = packed_proof.into();
175
176        let serialized = serde_json::to_value(proof).unwrap();
177
178        assert_eq!(
179            serialized,
180            serde_json::json!([
181                [
182                    "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d",
183                    "0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a"
184                ],
185                [
186                    [
187                        "0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6",
188                        "0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f"
189                    ],
190                    [
191                        "0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5",
192                        "0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd"
193                    ]
194                ],
195                [
196                    "0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487",
197                    "0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520"
198                ]
199            ])
200        );
201    }
202
203    #[test]
204    fn test_deserialize_proof_from_json() {
205        let proof_str = "[
206            [
207                \"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d\",
208                \"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a\"
209            ],
210            [
211                [
212                    \"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6\",
213                    \"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f\"
214                ],
215                [
216                    \"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5\",
217                    \"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd\"
218                ]
219            ],
220            [
221                \"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487\",
222                \"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\"
223            ]
224        ]";
225
226        let proof = serde_json::from_str::<Proof>(proof_str).unwrap();
227
228        let packed_proof = PackedProof::from(proof);
229
230        let expected_proof =  "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";
231
232        assert_eq!(packed_proof.to_string(), expected_proof);
233    }
234
235    #[test]
236    fn test_invalid_parsing() {
237        // note this is only 7 numbers
238        let packed_proof_str =  "0x0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007";
239        PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
240
241        // not a valid number
242        let packed_proof_str =  "0000000000000000p000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
243        PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
244
245        // completely invalid
246        let packed_proof_str = "0x0";
247        PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
248    }
249}