浏览代码

[zk-sdk] Add fixed string tests for proofs (#2814)

* refactor `impl_from_str` and `impl_from_bytes` macros

* impl `Display`, `FromStr`, `From<[u8; _]>` for sigma and range proof pod types

* impl `Display` for Pedersen commitment and grouped ElGamal ciphertext pod types

* impl `Display`, `FromStr`, `From<[u8; _]>` for decrypt handle pod type

* add fixed proof string tests

* cargo clippy

* remove println's

* update `assert!` and `.is_ok()` with `unwrap`'s
samkim-crypto 1 年之前
父节点
当前提交
b5432b001d

+ 2 - 2
zk-sdk/src/encryption/pod/auth_encryption.rs

@@ -3,9 +3,9 @@
 #[cfg(not(target_os = "solana"))]
 use crate::{encryption::auth_encryption::AeCiphertext, errors::AuthenticatedEncryptionError};
 use {
-    crate::encryption::{
+    crate::{
+        encryption::AE_CIPHERTEXT_LEN,
         pod::{impl_from_bytes, impl_from_str},
-        AE_CIPHERTEXT_LEN,
     },
     base64::{prelude::BASE64_STANDARD, Engine},
     bytemuck::{Pod, Zeroable},

+ 26 - 9
zk-sdk/src/encryption/pod/elgamal.rs

@@ -1,14 +1,5 @@
 //! Plain Old Data types for the ElGamal encryption scheme.
 
-use {
-    crate::encryption::{
-        pod::{impl_from_bytes, impl_from_str},
-        DECRYPT_HANDLE_LEN, ELGAMAL_CIPHERTEXT_LEN, ELGAMAL_PUBKEY_LEN,
-    },
-    base64::{prelude::BASE64_STANDARD, Engine},
-    bytemuck::Zeroable,
-    std::fmt,
-};
 #[cfg(not(target_os = "solana"))]
 use {
     crate::{
@@ -17,6 +8,15 @@ use {
     },
     curve25519_dalek::ristretto::CompressedRistretto,
 };
+use {
+    crate::{
+        encryption::{DECRYPT_HANDLE_LEN, ELGAMAL_CIPHERTEXT_LEN, ELGAMAL_PUBKEY_LEN},
+        pod::{impl_from_bytes, impl_from_str},
+    },
+    base64::{prelude::BASE64_STANDARD, Engine},
+    bytemuck::Zeroable,
+    std::fmt,
+};
 
 /// Maximum length of a base64 encoded ElGamal public key
 const ELGAMAL_PUBKEY_MAX_BASE64_LEN: usize = 44;
@@ -24,6 +24,9 @@ const ELGAMAL_PUBKEY_MAX_BASE64_LEN: usize = 44;
 /// Maximum length of a base64 encoded ElGamal ciphertext
 const ELGAMAL_CIPHERTEXT_MAX_BASE64_LEN: usize = 88;
 
+/// Maximum length of a base64 encoded ElGamal decrypt handle
+const DECRYPT_HANDLE_MAX_BASE64_LEN: usize = 44;
+
 /// The `ElGamalCiphertext` type as a `Pod`.
 #[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)]
 #[repr(transparent)]
@@ -150,6 +153,20 @@ impl TryFrom<PodDecryptHandle> for DecryptHandle {
     }
 }
 
+impl fmt::Display for PodDecryptHandle {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodDecryptHandle,
+    BYTES_LEN = DECRYPT_HANDLE_LEN,
+    BASE64_LEN = DECRYPT_HANDLE_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(TYPE = PodDecryptHandle, BYTES_LEN = DECRYPT_HANDLE_LEN);
+
 #[cfg(test)]
 mod tests {
     use {super::*, crate::encryption::elgamal::ElGamalKeypair, std::str::FromStr};

+ 14 - 4
zk-sdk/src/encryption/pod/grouped_elgamal.rs

@@ -5,13 +5,11 @@ use crate::encryption::grouped_elgamal::GroupedElGamalCiphertext;
 use {
     crate::{
         encryption::{
-            pod::{
-                elgamal::PodElGamalCiphertext, impl_from_bytes, impl_from_str,
-                pedersen::PodPedersenCommitment,
-            },
+            pod::{elgamal::PodElGamalCiphertext, pedersen::PodPedersenCommitment},
             DECRYPT_HANDLE_LEN, ELGAMAL_CIPHERTEXT_LEN, PEDERSEN_COMMITMENT_LEN,
         },
         errors::ElGamalError,
+        pod::{impl_from_bytes, impl_from_str},
     },
     base64::{prelude::BASE64_STANDARD, Engine},
     bytemuck::Zeroable,
@@ -89,6 +87,12 @@ impl Default for PodGroupedElGamalCiphertext2Handles {
     }
 }
 
+impl fmt::Display for PodGroupedElGamalCiphertext2Handles {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
 impl_from_str!(
     TYPE = PodGroupedElGamalCiphertext2Handles,
     BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES,
@@ -137,6 +141,12 @@ impl Default for PodGroupedElGamalCiphertext3Handles {
     }
 }
 
+impl fmt::Display for PodGroupedElGamalCiphertext3Handles {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
 impl_from_str!(
     TYPE = PodGroupedElGamalCiphertext3Handles,
     BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES,

+ 0 - 35
zk-sdk/src/encryption/pod/mod.rs

@@ -2,38 +2,3 @@ pub mod auth_encryption;
 pub mod elgamal;
 pub mod grouped_elgamal;
 pub mod pedersen;
-
-macro_rules! impl_from_str {
-    (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr, BASE64_LEN = $base64_len:expr) => {
-        impl std::str::FromStr for $type {
-            type Err = crate::errors::ParseError;
-
-            fn from_str(s: &str) -> Result<Self, Self::Err> {
-                if s.len() > $base64_len {
-                    return Err(Self::Err::WrongSize);
-                }
-                let mut bytes = [0u8; $bytes_len];
-                let decoded_len = BASE64_STANDARD
-                    .decode_slice(s, &mut bytes)
-                    .map_err(|_| Self::Err::Invalid)?;
-                if decoded_len != $bytes_len {
-                    Err(Self::Err::WrongSize)
-                } else {
-                    Ok($type(bytes))
-                }
-            }
-        }
-    };
-}
-pub(crate) use impl_from_str;
-
-macro_rules! impl_from_bytes {
-    (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr) => {
-        impl std::convert::From<[u8; $bytes_len]> for $type {
-            fn from(bytes: [u8; $bytes_len]) -> Self {
-                Self(bytes)
-            }
-        }
-    };
-}
-pub(crate) use impl_from_bytes;

+ 13 - 7
zk-sdk/src/encryption/pod/pedersen.rs

@@ -1,19 +1,19 @@
 //! Plain Old Data type for the Pedersen commitment scheme.
 
+#[cfg(not(target_os = "solana"))]
 use {
-    crate::encryption::{
+    crate::{encryption::pedersen::PedersenCommitment, errors::ElGamalError},
+    curve25519_dalek::ristretto::CompressedRistretto,
+};
+use {
+    crate::{
+        encryption::PEDERSEN_COMMITMENT_LEN,
         pod::{impl_from_bytes, impl_from_str},
-        PEDERSEN_COMMITMENT_LEN,
     },
     base64::{prelude::BASE64_STANDARD, Engine},
     bytemuck_derive::{Pod, Zeroable},
     std::fmt,
 };
-#[cfg(not(target_os = "solana"))]
-use {
-    crate::{encryption::pedersen::PedersenCommitment, errors::ElGamalError},
-    curve25519_dalek::ristretto::CompressedRistretto,
-};
 
 /// Maximum length of a base64 encoded ElGamal public key
 const PEDERSEN_COMMITMENT_MAX_BASE64_LEN: usize = 44;
@@ -36,6 +36,12 @@ impl From<PedersenCommitment> for PodPedersenCommitment {
     }
 }
 
+impl fmt::Display for PodPedersenCommitment {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
 impl_from_str!(
     TYPE = PodPedersenCommitment,
     BYTES_LEN = PEDERSEN_COMMITMENT_LEN,

+ 35 - 0
zk-sdk/src/pod.rs

@@ -27,3 +27,38 @@ impl From<PodU64> for u64 {
         Self::from_le_bytes(pod.0)
     }
 }
+
+macro_rules! impl_from_str {
+    (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr, BASE64_LEN = $base64_len:expr) => {
+        impl std::str::FromStr for $type {
+            type Err = crate::errors::ParseError;
+
+            fn from_str(s: &str) -> Result<Self, Self::Err> {
+                if s.len() > $base64_len {
+                    return Err(Self::Err::WrongSize);
+                }
+                let mut bytes = [0u8; $bytes_len];
+                let decoded_len = BASE64_STANDARD
+                    .decode_slice(s, &mut bytes)
+                    .map_err(|_| Self::Err::Invalid)?;
+                if decoded_len != $bytes_len {
+                    Err(Self::Err::WrongSize)
+                } else {
+                    Ok($type(bytes))
+                }
+            }
+        }
+    };
+}
+pub(crate) use impl_from_str;
+
+macro_rules! impl_from_bytes {
+    (TYPE = $type:ident, BYTES_LEN = $bytes_len:expr) => {
+        impl std::convert::From<[u8; $bytes_len]> for $type {
+            fn from(bytes: [u8; $bytes_len]) -> Self {
+                Self(bytes)
+            }
+        }
+    };
+}
+pub(crate) use impl_from_bytes;

+ 42 - 9
zk-sdk/src/range_proof/mod.rs

@@ -55,21 +55,21 @@ pub const INNER_PRODUCT_PROOF_U64_LEN: usize = 448;
 
 /// Byte length of a range proof for an unsigned 64-bit number
 pub const RANGE_PROOF_U64_LEN: usize =
-    INNER_PRODUCT_PROOF_U64_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
+    INNER_PRODUCT_PROOF_U64_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN; // 672 bytes
 
 /// Byte length of an inner-product proof for a vector of length 128
 pub const INNER_PRODUCT_PROOF_U128_LEN: usize = 512;
 
 /// Byte length of a range proof for an unsigned 128-bit number
 pub const RANGE_PROOF_U128_LEN: usize =
-    INNER_PRODUCT_PROOF_U128_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
+    INNER_PRODUCT_PROOF_U128_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN; // 736 bytes
 
 /// Byte length of an inner-product proof for a vector of length 256
 pub const INNER_PRODUCT_PROOF_U256_LEN: usize = 576;
 
 /// Byte length of a range proof for an unsigned 256-bit number
 pub const RANGE_PROOF_U256_LEN: usize =
-    INNER_PRODUCT_PROOF_U256_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
+    INNER_PRODUCT_PROOF_U256_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN; // 800 bytes
 
 #[allow(non_snake_case)]
 #[cfg(not(target_os = "solana"))]
@@ -466,7 +466,13 @@ fn delta(bit_lengths: &[usize], y: &Scalar, z: &Scalar) -> Scalar {
 
 #[cfg(test)]
 mod tests {
-    use super::*;
+    use {
+        super::*,
+        crate::{
+            encryption::pod::pedersen::PodPedersenCommitment, range_proof::pod::PodRangeProofU128,
+        },
+        std::str::FromStr,
+    };
 
     #[test]
     fn test_single_rangeproof() {
@@ -478,9 +484,9 @@ mod tests {
         let proof =
             RangeProof::new(vec![55], vec![32], vec![&open], &mut transcript_create).unwrap();
 
-        assert!(proof
+        proof
             .verify(vec![&comm], vec![32], &mut transcript_verify)
-            .is_ok());
+            .unwrap();
     }
 
     #[test]
@@ -500,14 +506,41 @@ mod tests {
         )
         .unwrap();
 
-        assert!(proof
+        proof
             .verify(
                 vec![&comm_1, &comm_2, &comm_3],
                 vec![64, 32, 32],
                 &mut transcript_verify,
             )
-            .is_ok());
+            .unwrap();
     }
 
-    // TODO: write test for serialization/deserialization
+    #[test]
+    fn test_range_proof_string() {
+        let commitment_1_str = "dDaa/MTEDlyI0Nxx+iu1tOteZsTWmPXAfn9QI0W9mSc=";
+        let pod_commitment_1 = PodPedersenCommitment::from_str(commitment_1_str).unwrap();
+        let commitment_1: PedersenCommitment = pod_commitment_1.try_into().unwrap();
+
+        let commitment_2_str = "tnRILjKpogi2sXxLgZzMqlqPMLnCJmrSjZ5SPQYhtgg=";
+        let pod_commitment_2 = PodPedersenCommitment::from_str(commitment_2_str).unwrap();
+        let commitment_2: PedersenCommitment = pod_commitment_2.try_into().unwrap();
+
+        let commitment_3_str = "ZAC5ZLXotsMOVExtrr56D/EZNeyo9iWepNbeH22EuRo=";
+        let pod_commitment_3 = PodPedersenCommitment::from_str(commitment_3_str).unwrap();
+        let commitment_3: PedersenCommitment = pod_commitment_3.try_into().unwrap();
+
+        let proof_str = "AvvBQL63pXMXsmuvuNbs/CqXdzeyrMpEIO2O/cI6/SyqU4N+7HUU3LmXai9st+DxqTnuKsm0SgnADfpLpQCEbDDupMb09NY8oHT8Bx8WQhv9eyoBlrPRd7DVhOUsio02gBshe3p2Wj7+yDCpFaZ7/PMypFBX6+E+EqCiPI6yUk4ztslWY0Ksac41eJgcPzXyIx2kvmSTsVBKLb7U01PWBC+AUyUmK3/IdvmJ4DnlS3xFrdg/mxSsYJFd3OZA3cwDb0jePQf/P43/2VVqPRixMVO7+VGoMKPoRTEEVbClsAlW6stGTFPcrimu3c+geASgvwElkIKNGtYcjoj3SS+/VeqIG9Ei1j+TJtPhOE9SG4KNw9xBGwecpliDbQhKjO950EVcnOts+a525/frZV1jHJmOOrZtKRV4pvk37dtQkx4sv+pxRmfVrjwOcKQeg+BzcuF0vaQbqa4SUbzbO9z3RwIMlYIBaz0bqZgJmtPOFuFmNyCJaeB29vlcEAfYbn5gdlgtWP50tKmhoskndulziKZjz4qHSA9rbG2ZtoMHoCsAobHKu2H9OxcaK4Scj1QGwst+zXBEY8uePNbxvU5DMJLVFORtLUXkVdPCmCSsm1Bz4TRbnls8LOVW6wqTgShQMhjNM3RtwdHXENPn5uDnhyvfduAcL+DtI8AIJyRneROefk7i7gjal8dLdMM/QnXT7ctpMQU6uNlpsNzq65xlOQKXO71vQ3c2mE/DmxVJi6BTS5WCzavvhiqdhQyRL61ESCALQpaP0/d0DLwLikVH3ypuDLEnVXe9Pmkxdd0xCzO6QcfyK50CPnV/dVgHeLg8EVag2O83+/7Ys5oLxrDad9TJTDcrT2xsRqECFnSA+z9uZtDPujhQL0ogS5RH4agnQN4mVGTwOLV8OKpn+AvWq6+j1/9EXFkLPBTU5wT0FQuT2VZ8xp5GeqdI13Zey1uPrxc6CZZ407y9OINED4IdBQ==";
+        let pod_proof = PodRangeProofU128::from_str(proof_str).unwrap();
+        let proof: RangeProof = pod_proof.try_into().unwrap();
+
+        let mut transcript_verify = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                vec![&commitment_1, &commitment_2, &commitment_3],
+                vec![64, 32, 32],
+                &mut transcript_verify,
+            )
+            .unwrap()
+    }
 }

+ 54 - 1
zk-sdk/src/range_proof/pod.rs

@@ -6,8 +6,13 @@ use crate::{
     UNIT_LEN,
 };
 use {
-    crate::range_proof::*,
+    crate::{
+        pod::{impl_from_bytes, impl_from_str},
+        range_proof::*,
+    },
+    base64::{prelude::BASE64_STANDARD, Engine},
     bytemuck::{Pod, Zeroable},
+    std::fmt,
 };
 
 /// The `RangeProof` type as a `Pod` restricted to proofs on 64-bit numbers.
@@ -41,6 +46,22 @@ impl TryFrom<PodRangeProofU64> for RangeProof {
     }
 }
 
+const RANGE_PROOF_U64_MAX_BASE64_LEN: usize = 896;
+
+impl fmt::Display for PodRangeProofU64 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodRangeProofU64,
+    BYTES_LEN = RANGE_PROOF_U64_LEN,
+    BASE64_LEN = RANGE_PROOF_U64_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(TYPE = PodRangeProofU64, BYTES_LEN = RANGE_PROOF_U64_LEN);
+
 /// The `RangeProof` type as a `Pod` restricted to proofs on 128-bit numbers.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -72,6 +93,22 @@ impl TryFrom<PodRangeProofU128> for RangeProof {
     }
 }
 
+const RANGE_PROOF_U128_MAX_BASE64_LEN: usize = 984;
+
+impl fmt::Display for PodRangeProofU128 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodRangeProofU128,
+    BYTES_LEN = RANGE_PROOF_U128_LEN,
+    BASE64_LEN = RANGE_PROOF_U128_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(TYPE = PodRangeProofU128, BYTES_LEN = RANGE_PROOF_U128_LEN);
+
 /// The `RangeProof` type as a `Pod` restricted to proofs on 256-bit numbers.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -103,6 +140,22 @@ impl TryFrom<PodRangeProofU256> for RangeProof {
     }
 }
 
+const RANGE_PROOF_U256_MAX_BASE64_LEN: usize = 1068;
+
+impl fmt::Display for PodRangeProofU256 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodRangeProofU256,
+    BYTES_LEN = RANGE_PROOF_U256_LEN,
+    BASE64_LEN = RANGE_PROOF_U256_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(TYPE = PodRangeProofU256, BYTES_LEN = RANGE_PROOF_U256_LEN);
+
 #[cfg(not(target_os = "solana"))]
 fn copy_range_proof_modulo_inner_product_proof(proof: &RangeProof, buf: &mut [u8]) {
     let mut chunks = buf.chunks_mut(UNIT_LEN);

+ 71 - 4
zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_2.rs

@@ -123,11 +123,22 @@ impl BatchedGroupedCiphertext2HandlesValidityProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen},
+        crate::{
+            encryption::{
+                elgamal::ElGamalKeypair,
+                pedersen::Pedersen,
+                pod::{
+                    elgamal::{PodDecryptHandle, PodElGamalPubkey},
+                    pedersen::PodPedersenCommitment,
+                },
+            },
+            sigma_proofs::pod::PodBatchedGroupedCiphertext2HandlesValidityProof,
+        },
+        std::str::FromStr,
     };
 
     #[test]
-    fn test_batched_grouped_ciphertext_validity_proof() {
+    fn test_batched_grouped_ciphertext_2_handles_validity_proof() {
         let first_keypair = ElGamalKeypair::new_rand();
         let first_pubkey = first_keypair.pubkey();
 
@@ -159,7 +170,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 first_pubkey,
                 second_pubkey,
@@ -171,6 +182,62 @@ mod test {
                 &second_handle_hi,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_batched_grouped_ciphertext_2_handles_validity_proof_string() {
+        let first_pubkey_str = "3FQGicS6AgVkRnX5Sau8ybxJDvlehmbdvBUdo+o+oE4=";
+        let pod_first_pubkey = PodElGamalPubkey::from_str(first_pubkey_str).unwrap();
+        let first_pubkey: ElGamalPubkey = pod_first_pubkey.try_into().unwrap();
+
+        let second_pubkey_str = "IieU/fJCRksbDNvIJZvg/N/safpnIWAGT/xpUAG7YUg=";
+        let pod_second_pubkey = PodElGamalPubkey::from_str(second_pubkey_str).unwrap();
+        let second_pubkey: ElGamalPubkey = pod_second_pubkey.try_into().unwrap();
+
+        let commitment_lo_str = "Lq0z7bx3ccyxIB0rRHoWzcba8W1azvAhMfnJogxcz2I=";
+        let pod_commitment_lo = PodPedersenCommitment::from_str(commitment_lo_str).unwrap();
+        let commitment_lo: PedersenCommitment = pod_commitment_lo.try_into().unwrap();
+
+        let commitment_hi_str = "dLPLdQrcl5ZWb0EaJcmebAlJA6RrzKpMSYPDVMJdOm0=";
+        let pod_commitment_hi = PodPedersenCommitment::from_str(commitment_hi_str).unwrap();
+        let commitment_hi: PedersenCommitment = pod_commitment_hi.try_into().unwrap();
+
+        let first_handle_lo_str = "GizvHRUmu6CMjhH7qWg5Rqu43V69Nyjq4QsN/yXBHT8=";
+        let pod_first_handle_lo_str = PodDecryptHandle::from_str(first_handle_lo_str).unwrap();
+        let first_handle_lo: DecryptHandle = pod_first_handle_lo_str.try_into().unwrap();
+
+        let first_handle_hi_str = "qMuR929bbkKiVJfRvYxnb90rbh2btjNDjaXpeLCvQWk=";
+        let pod_first_handle_hi_str = PodDecryptHandle::from_str(first_handle_hi_str).unwrap();
+        let first_handle_hi: DecryptHandle = pod_first_handle_hi_str.try_into().unwrap();
+
+        let second_handle_lo_str = "MmDbMo2l/jAcXUIm09AQZsBXa93lI2BapAiGZ6f9zRs=";
+        let pod_second_handle_lo_str = PodDecryptHandle::from_str(second_handle_lo_str).unwrap();
+        let second_handle_lo: DecryptHandle = pod_second_handle_lo_str.try_into().unwrap();
+
+        let second_handle_hi_str = "gKhb0o3d22XcUcQl5hENF4l1SJwg1vpgiw2RDYqXOxY=";
+        let pod_second_handle_hi_str = PodDecryptHandle::from_str(second_handle_hi_str).unwrap();
+        let second_handle_hi: DecryptHandle = pod_second_handle_hi_str.try_into().unwrap();
+
+        let proof_str = "2n2mADpkNrop+eHJj1sAryXWcTtC/7QKcxMp7FdHeh8wjGKLAa9kC89QLGrphv7pZdb2J25kKXqhWUzRBsJWU0izi5vxau9XX6cyd72F3Q9hMXBfjk3htOHI0VnGAalZ/3dZ6C7erjGQDoeTVGOd1vewQ+NObAbfZwcry3+VhQNpkhL17E1dUgZZ+mb5K0tXAjWCmVh1OfN9h3sGltTUCg==";
+        let pod_proof =
+            PodBatchedGroupedCiphertext2HandlesValidityProof::from_str(proof_str).unwrap();
+        let proof: BatchedGroupedCiphertext2HandlesValidityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                &first_pubkey,
+                &second_pubkey,
+                &commitment_lo,
+                &commitment_hi,
+                &first_handle_lo,
+                &first_handle_hi,
+                &second_handle_lo,
+                &second_handle_hi,
+                &mut verifier_transcript,
+            )
+            .unwrap();
     }
 }

+ 86 - 4
zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_3.rs

@@ -133,11 +133,22 @@ impl BatchedGroupedCiphertext3HandlesValidityProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen},
+        crate::{
+            encryption::{
+                elgamal::ElGamalKeypair,
+                pedersen::Pedersen,
+                pod::{
+                    elgamal::{PodDecryptHandle, PodElGamalPubkey},
+                    pedersen::PodPedersenCommitment,
+                },
+            },
+            sigma_proofs::pod::PodBatchedGroupedCiphertext3HandlesValidityProof,
+        },
+        std::str::FromStr,
     };
 
     #[test]
-    fn test_batched_grouped_ciphertext_validity_proof() {
+    fn test_batched_grouped_ciphertext_3_handles_validity_proof() {
         let first_keypair = ElGamalKeypair::new_rand();
         let first_pubkey = first_keypair.pubkey();
 
@@ -176,7 +187,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 first_pubkey,
                 second_pubkey,
@@ -191,6 +202,77 @@ mod test {
                 &third_handle_hi,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_batched_grouped_ciphertext_3_handles_validity_proof_string() {
+        let first_pubkey_str = "PFQ4AD4W/Y4BEg3nI/qckFLhnjMQ12xPHyaMg9Bkg3w=";
+        let pod_first_pubkey = PodElGamalPubkey::from_str(first_pubkey_str).unwrap();
+        let first_pubkey: ElGamalPubkey = pod_first_pubkey.try_into().unwrap();
+
+        let second_pubkey_str = "2CZ4h5oK7zh4/3P6s/kCQoNlpUPk1IrsrAtTWjCtfFo=";
+        let pod_second_pubkey = PodElGamalPubkey::from_str(second_pubkey_str).unwrap();
+        let second_pubkey: ElGamalPubkey = pod_second_pubkey.try_into().unwrap();
+
+        let third_pubkey_str = "yonKhqkoXNvMbN/tU6fjHFhfZuNPpvMj8L55aP2bBG4=";
+        let pod_third_pubkey = PodElGamalPubkey::from_str(third_pubkey_str).unwrap();
+        let third_pubkey: ElGamalPubkey = pod_third_pubkey.try_into().unwrap();
+
+        let commitment_lo_str = "atIteiveexponnuF2Z1nbovZYYtcGWjglpEA3caMShM=";
+        let pod_commitment_lo = PodPedersenCommitment::from_str(commitment_lo_str).unwrap();
+        let commitment_lo: PedersenCommitment = pod_commitment_lo.try_into().unwrap();
+
+        let commitment_hi_str = "IoZlSj7spae2ogiAUiEuuwAjYA5khgBH8FhaHzkh+lc=";
+        let pod_commitment_hi = PodPedersenCommitment::from_str(commitment_hi_str).unwrap();
+        let commitment_hi: PedersenCommitment = pod_commitment_hi.try_into().unwrap();
+
+        let first_handle_lo_str = "6PlKiitdapVZnh7VccQNbskXop9nmITGppLsV42UMkU=";
+        let pod_first_handle_lo_str = PodDecryptHandle::from_str(first_handle_lo_str).unwrap();
+        let first_handle_lo: DecryptHandle = pod_first_handle_lo_str.try_into().unwrap();
+
+        let first_handle_hi_str = "vF+oZ3WWnrJyJ95Wl8EW+aVJiFmruiuRw6+TT3QVMBI=";
+        let pod_first_handle_hi_str = PodDecryptHandle::from_str(first_handle_hi_str).unwrap();
+        let first_handle_hi: DecryptHandle = pod_first_handle_hi_str.try_into().unwrap();
+
+        let second_handle_lo_str = "rvxzo5ZyrD6YTm7X3GjplgOGJjx6PtoZ+DKbL4LsQWA=";
+        let pod_second_handle_lo_str = PodDecryptHandle::from_str(second_handle_lo_str).unwrap();
+        let second_handle_lo: DecryptHandle = pod_second_handle_lo_str.try_into().unwrap();
+
+        let second_handle_hi_str = "0mdZSGiWQhOjqsExqFMD8hfgUlRRRrF/G3CJ7d0LEEk=";
+        let pod_second_handle_hi_str = PodDecryptHandle::from_str(second_handle_hi_str).unwrap();
+        let second_handle_hi: DecryptHandle = pod_second_handle_hi_str.try_into().unwrap();
+
+        let third_handle_lo_str = "bpT2LuFektFhI/sacjSsqNtCsO8ac5qn0jWeMeQq4WM=";
+        let pod_third_handle_lo_str = PodDecryptHandle::from_str(third_handle_lo_str).unwrap();
+        let third_handle_lo: DecryptHandle = pod_third_handle_lo_str.try_into().unwrap();
+
+        let third_handle_hi_str = "OE8z7Bbv2AHnjxebK6ASJfkJbOlYQdnN6ZPkG2u4SnA=";
+        let pod_third_handle_hi_str = PodDecryptHandle::from_str(third_handle_hi_str).unwrap();
+        let third_handle_hi: DecryptHandle = pod_third_handle_hi_str.try_into().unwrap();
+
+        let proof_str = "GkjZ7QKcJq5X/OU8wb26wZ7p2D9thVK+Cb11CzRjWUoihYvGfuCbVG1vr4qtnfx65SS4jVK1H0q/948A9wy8ZPTrOZJA122G4+cpt5mKnSrKq/vbv4ZRha0oR9RGJFZ2SPT3gx2jysKDKRAQgBLOzSGfQg9Hsbz57i55SQfliUF5mByZKuzGKHSIHi81BDqbrFAj6x5bOeMAaLqsCboCA5XGDUZ2HMPUGuAd9F+OaVH+eJZnuoDjwwcBQ2eANgMB";
+        let pod_proof =
+            PodBatchedGroupedCiphertext3HandlesValidityProof::from_str(proof_str).unwrap();
+        let proof: BatchedGroupedCiphertext3HandlesValidityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                &first_pubkey,
+                &second_pubkey,
+                &third_pubkey,
+                &commitment_lo,
+                &commitment_hi,
+                &first_handle_lo,
+                &first_handle_hi,
+                &second_handle_lo,
+                &second_handle_hi,
+                &third_handle_lo,
+                &third_handle_hi,
+                &mut verifier_transcript,
+            )
+            .unwrap();
     }
 }

+ 46 - 4
zk-sdk/src/sigma_proofs/ciphertext_ciphertext_equality.rs

@@ -267,7 +267,14 @@ impl CiphertextCiphertextEqualityProof {
 
 #[cfg(test)]
 mod test {
-    use super::*;
+    use {
+        super::*,
+        crate::{
+            encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
+            sigma_proofs::pod::PodCiphertextCiphertextEqualityProof,
+        },
+        std::str::FromStr,
+    };
 
     #[test]
     fn test_ciphertext_ciphertext_equality_proof_correctness() {
@@ -295,15 +302,15 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 first_keypair.pubkey(),
                 second_keypair.pubkey(),
                 &first_ciphertext,
                 &second_ciphertext,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // fail case: encrypted and committed messages are different
         let first_message: u64 = 55;
@@ -338,4 +345,39 @@ mod test {
             )
             .is_err());
     }
+
+    #[test]
+    fn test_ciphertext_ciphertext_equality_proof_string() {
+        let first_pubkey_str = "VOPKaqo4nsX4XnbgGjCKHkLkR6JG1jX9D5G/e0EuYmM=";
+        let pod_first_pubkey = PodElGamalPubkey::from_str(first_pubkey_str).unwrap();
+        let first_pubkey: ElGamalPubkey = pod_first_pubkey.try_into().unwrap();
+
+        let second_pubkey_str = "JnVhtKo9B7g9c8Obo/5/EqvA59i3TvtuOcQWf17T7SU=";
+        let pod_second_pubkey = PodElGamalPubkey::from_str(second_pubkey_str).unwrap();
+        let second_pubkey: ElGamalPubkey = pod_second_pubkey.try_into().unwrap();
+
+        let first_ciphertext_str = "oKv6zxN051MXdk2cISD+CUsH2+FINoH1iB4WZyuy6nNkE7Q+eLiY9JB8itJhgKHJEA/1sAzDvpnRlLL06OXvIg==";
+        let pod_first_ciphertext = PodElGamalCiphertext::from_str(first_ciphertext_str).unwrap();
+        let first_ciphertext: ElGamalCiphertext = pod_first_ciphertext.try_into().unwrap();
+
+        let second_ciphertext_str = "ooSA2cQDqutgyCBoMiQktM1Cu4NDNEbphF010gjG4iF0iMK1N+u/Qxqk0wwO/+w+5S6RiicwPs4mEKRJpFiHEw==";
+        let pod_second_ciphertext = PodElGamalCiphertext::from_str(second_ciphertext_str).unwrap();
+        let second_ciphertext: ElGamalCiphertext = pod_second_ciphertext.try_into().unwrap();
+
+        let proof_str = "MlfRDO4sBPbpciEXci3QfVSLVABAJ0s8wMZ/Uz3AyETmGJ1BUE961fHIiNQXPD0j1uu1Josj//E8loPD1w+4E3bfDBJ3Mp2YqeOv41Bdec02YXlAotTGjq/UfncGdUhyampkuXUmSvnmkf5BIp4nr3X18cR9KHTAzBrKv6erjAxIckyRnACaZGEx+ZboEb3FBEXqTklytT1nrebbwkjvDUWbcpZrE+xxBWYek3qeq1x1debzxVhtS2yx44cvR5UIGLzGYa2ec/xh7wvyNEbnX80rZju2dztr4bN5f2vrTgk=";
+        let pod_proof = PodCiphertextCiphertextEqualityProof::from_str(proof_str).unwrap();
+        let proof: CiphertextCiphertextEqualityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                &first_pubkey,
+                &second_pubkey,
+                &first_ciphertext,
+                &second_ciphertext,
+                &mut verifier_transcript,
+            )
+            .unwrap();
+    }
 }

+ 49 - 13
zk-sdk/src/sigma_proofs/ciphertext_commitment_equality.rs

@@ -244,7 +244,18 @@ impl CiphertextCommitmentEqualityProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{elgamal::ElGamalSecretKey, pedersen::Pedersen},
+        crate::{
+            encryption::{
+                elgamal::ElGamalSecretKey,
+                pedersen::Pedersen,
+                pod::{
+                    elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
+                    pedersen::PodPedersenCommitment,
+                },
+            },
+            sigma_proofs::pod::PodCiphertextCommitmentEqualityProof,
+        },
+        std::str::FromStr,
     };
 
     #[test]
@@ -267,14 +278,14 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 keypair.pubkey(),
                 &ciphertext,
                 &commitment,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // fail case: encrypted and committed messages are different
         let keypair = ElGamalKeypair::new_rand();
@@ -357,14 +368,14 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 elgamal_keypair.pubkey(),
                 &ciphertext,
                 &commitment,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // if commitment is all-zero and the ciphertext is a correct encryption of 0, then the
         // proof should still accept
@@ -386,14 +397,14 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 elgamal_keypair.pubkey(),
                 &ciphertext,
                 &commitment,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // if ciphertext is all zero and commitment correctly encodes 0, then the proof should
         // still accept
@@ -414,13 +425,38 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 elgamal_keypair.pubkey(),
                 &ciphertext,
                 &commitment,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_ciphertext_commitment_equality_proof_string() {
+        let pubkey_str = "JNa7rRrDm35laU7f8HPds1PmHoZEPSHFK/M+aTtEhAk=";
+        let pod_pubkey = PodElGamalPubkey::from_str(pubkey_str).unwrap();
+        let pubkey: ElGamalPubkey = pod_pubkey.try_into().unwrap();
+
+        let ciphertext_str = "RAXnbQ/DPRlYAWmD+iHRNqMDv7oQcPgQ7OejRzj4bxVy2qOJNziqqDOC7VP3iTW1+z/jckW4smA3EUF7i/r8Rw==";
+        let pod_ciphertext = PodElGamalCiphertext::from_str(ciphertext_str).unwrap();
+        let ciphertext: ElGamalCiphertext = pod_ciphertext.try_into().unwrap();
+
+        let commitment_str = "ngPTYvbY9P5l6aOfr7bLQiI+0HZsw8GBgiumdW3tNzw=";
+        let pod_commitment = PodPedersenCommitment::from_str(commitment_str).unwrap();
+        let commitment: PedersenCommitment = pod_commitment.try_into().unwrap();
+
+        let proof_str = "cCZySLxB2XJdGyDvckVBm2OWiXqf7Jf54IFoDuLJ4G+ySj+lh5DbaDMHDhuozQC9tDWtk2mFITuaXOc5Zw3nZ2oEvVYpqv5hN+k5dx9k8/nZKabUCkZwx310z7x4fE4Np5SY9PYia1hkrq9AWq0b3v97XvW1+XCSSxuflvBk5wsdaQQ+ZgcmPnKWKjHfRwmU2k5iVgYzs2VmvZa5E3OWBoM/M2yFNvukY+FCC2YMnspO0c4lNBr/vDFQuHdW0OgJ";
+        let pod_proof = PodCiphertextCommitmentEqualityProof::from_str(proof_str).unwrap();
+        let proof: CiphertextCommitmentEqualityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(&pubkey, &ciphertext, &commitment, &mut verifier_transcript)
+            .unwrap();
     }
 }

+ 58 - 7
zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs

@@ -238,7 +238,18 @@ impl GroupedCiphertext2HandlesValidityProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen},
+        crate::{
+            encryption::{
+                elgamal::ElGamalKeypair,
+                pedersen::Pedersen,
+                pod::{
+                    elgamal::{PodDecryptHandle, PodElGamalPubkey},
+                    pedersen::PodPedersenCommitment,
+                },
+            },
+            sigma_proofs::pod::PodGroupedCiphertext2HandlesValidityProof,
+        },
+        std::str::FromStr,
     };
 
     #[test]
@@ -266,7 +277,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -275,7 +286,7 @@ mod test {
                 &second_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
     }
 
     #[test]
@@ -339,7 +350,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -348,7 +359,7 @@ mod test {
                 &second_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // decryption handles can be zero as long as the Pedersen commitment is valid
         let first_keypair = ElGamalKeypair::new_rand();
@@ -374,7 +385,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -383,6 +394,46 @@ mod test {
                 &second_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_grouped_ciphertext_validity_proof_string() {
+        let commitment_str = "VjdpJcofkU/Lhd6RRvwsCoqaZ8XSbhiizI7jsxZNKSU=";
+        let pod_commitment = PodPedersenCommitment::from_str(commitment_str).unwrap();
+        let commitment: PedersenCommitment = pod_commitment.try_into().unwrap();
+
+        let first_pubkey_str = "YllcTvlVBp9nv+bi8d0Z9UOujPfMsgH3ZcCqQSwXfic=";
+        let pod_first_pubkey = PodElGamalPubkey::from_str(first_pubkey_str).unwrap();
+        let first_pubkey: ElGamalPubkey = pod_first_pubkey.try_into().unwrap();
+
+        let second_pubkey_str = "CCq+4oKGWlh3pkSbZpEsj6vfimhC/c3TxTVAghXq5Xo=";
+        let pod_second_pubkey = PodElGamalPubkey::from_str(second_pubkey_str).unwrap();
+        let second_pubkey: ElGamalPubkey = pod_second_pubkey.try_into().unwrap();
+
+        let first_handle_str = "EE1qdL/QLMGXvsWIjw2c07Vg/DgUsaexxQECKtjEwWE=";
+        let pod_first_handle_str = PodDecryptHandle::from_str(first_handle_str).unwrap();
+        let first_handle: DecryptHandle = pod_first_handle_str.try_into().unwrap();
+
+        let second_handle_str = "2Jn0+IVwpI5O/5pBU/nizS759k6dNn6UyUzxc1bt3RM=";
+        let pod_second_handle_str = PodDecryptHandle::from_str(second_handle_str).unwrap();
+        let second_handle: DecryptHandle = pod_second_handle_str.try_into().unwrap();
+
+        let proof_str = "/GITIw3LjQSphEG1GWYpKGjKUrYnC1n4yGFDvBwcE2V6XdSM8FKgc3AjQYJWGVkUMsciv/vMRv3lyDuW4VJJclQk9STY7Pd2F4r6Lz1P3fBmODbDp++k3Ni759FrV141Oy4puCzHV8+LHg6ePh3WlZ8yL+Ri6VDTyLc+3pblSQ0VIno0QoxyavznU6faQhuCXuy3bD+E87ZlRNtk9jPKDg==";
+        let pod_proof = PodGroupedCiphertext2HandlesValidityProof::from_str(proof_str).unwrap();
+        let proof: GroupedCiphertext2HandlesValidityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                &commitment,
+                &first_pubkey,
+                &second_pubkey,
+                &first_handle,
+                &second_handle,
+                &mut verifier_transcript,
+            )
+            .unwrap();
     }
 }

+ 68 - 7
zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_3.rs

@@ -268,7 +268,18 @@ impl GroupedCiphertext3HandlesValidityProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen},
+        crate::{
+            encryption::{
+                elgamal::ElGamalKeypair,
+                pedersen::Pedersen,
+                pod::{
+                    elgamal::{PodDecryptHandle, PodElGamalCiphertext, PodElGamalPubkey},
+                    pedersen::PodPedersenCommitment,
+                },
+            },
+            sigma_proofs::pod::PodGroupedCiphertext3HandlesValidityProof,
+        },
+        std::str::FromStr,
     };
 
     #[test]
@@ -301,7 +312,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -312,7 +323,7 @@ mod test {
                 &third_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
     }
 
     #[test]
@@ -386,7 +397,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -397,7 +408,7 @@ mod test {
                 &third_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // decryption handles can be zero as long as the Pedersen commitment is valid
         let first_keypair = ElGamalKeypair::new_rand();
@@ -430,7 +441,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &commitment,
                 first_pubkey,
@@ -441,6 +452,56 @@ mod test {
                 &third_handle,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_grouped_ciphertext_3_handles_validity_proof_string() {
+        let commitment_str = "DDSCVZLH+eqC9gX+ZeP3HQQxigojAOgda3YwVChR5W4=";
+        let pod_commitment = PodPedersenCommitment::from_str(commitment_str).unwrap();
+        let commitment: PedersenCommitment = pod_commitment.try_into().unwrap();
+
+        let first_pubkey_str = "yGGJnLUs8B744So/Ua3n2wNm+8u9ey/6KrDdHx4ySwk=";
+        let pod_first_pubkey = PodElGamalPubkey::from_str(first_pubkey_str).unwrap();
+        let first_pubkey: ElGamalPubkey = pod_first_pubkey.try_into().unwrap();
+
+        let second_pubkey_str = "ZFETe85sZdWpxLAo177kwiOxZCpsXGeyZEnzern7tAk=";
+        let pod_second_pubkey = PodElGamalPubkey::from_str(second_pubkey_str).unwrap();
+        let second_pubkey: ElGamalPubkey = pod_second_pubkey.try_into().unwrap();
+
+        let third_pubkey_str = "duUYiBx0l0jRRPsTLCoCD8PIKFczPdrxl+2f4eCflhQ=";
+        let pod_third_pubkey = PodElGamalPubkey::from_str(third_pubkey_str).unwrap();
+        let third_pubkey: ElGamalPubkey = pod_third_pubkey.try_into().unwrap();
+
+        let first_handle_str = "Asor2klomf847EmJZmXn3qoi0SGE3cBXCkKttbJa+lE=";
+        let pod_first_handle_str = PodDecryptHandle::from_str(first_handle_str).unwrap();
+        let first_handle: DecryptHandle = pod_first_handle_str.try_into().unwrap();
+
+        let second_handle_str = "kJ0GYHDVeB1Kgvqp+MY/my3BYZvqsC5Mv0gQLJHnNBQ=";
+        let pod_second_handle_str = PodDecryptHandle::from_str(second_handle_str).unwrap();
+        let second_handle: DecryptHandle = pod_second_handle_str.try_into().unwrap();
+
+        let third_handle_str = "Jnd5jZLNDOMMt+kbgQWCQqTytbwHx3Bz5vwtfDLhRn0=";
+        let pod_third_handle_str = PodDecryptHandle::from_str(third_handle_str).unwrap();
+        let third_handle: DecryptHandle = pod_third_handle_str.try_into().unwrap();
+
+        let proof_str = "8NoqOM40+fvPY2aHzO0SdWZM6lvSoaqI7KpaFuE4wQUaqewILtQV8IMHeHmpevxt/GTErJsdcV8kY3HDZ1GHbMoDujYpstUhyubX1voJh/DstYAL1SQqlRpNLG+kWEUZYvCudTur7i5R+zqZQY3sRMEAxW458V+1GmyCWbWP3FZEz5gX/Pa28/ZNLBvmSPpJBZapXRI5Ra0dKPskFmQ0CH0gBWo6pxj/PH9sgNEkLrbVZB7jpVtdmNzivwgFeb4M";
+        let pod_proof = PodGroupedCiphertext3HandlesValidityProof::from_str(proof_str).unwrap();
+        let proof: GroupedCiphertext3HandlesValidityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"Test");
+
+        proof
+            .verify(
+                &commitment,
+                &first_pubkey,
+                &second_pubkey,
+                &third_pubkey,
+                &first_handle,
+                &second_handle,
+                &third_handle,
+                &mut verifier_transcript,
+            )
+            .unwrap();
     }
 }

+ 50 - 7
zk-sdk/src/sigma_proofs/percentage_with_cap.rs

@@ -556,7 +556,14 @@ fn conditional_select_ristretto(
 
 #[cfg(test)]
 mod test {
-    use {super::*, crate::encryption::pedersen::Pedersen};
+    use {
+        super::*,
+        crate::{
+            encryption::{pedersen::Pedersen, pod::pedersen::PodPedersenCommitment},
+            sigma_proofs::pod::PodPercentageWithCapProof,
+        },
+        std::str::FromStr,
+    };
 
     #[test]
     fn test_proof_above_max_proof() {
@@ -594,7 +601,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &percentage_commitment,
                 &delta_commitment,
@@ -602,7 +609,7 @@ mod test {
                 max_value,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
     }
 
     #[test]
@@ -646,7 +653,7 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
             .verify(
                 &percentage_commitment,
                 &delta_commitment,
@@ -654,7 +661,7 @@ mod test {
                 max_value,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
     }
 
     #[test]
@@ -693,7 +700,43 @@ mod test {
             &mut prover_transcript,
         );
 
-        assert!(proof
+        proof
+            .verify(
+                &percentage_commitment,
+                &delta_commitment,
+                &claimed_commitment,
+                max_value,
+                &mut verifier_transcript,
+            )
+            .unwrap();
+    }
+
+    #[test]
+    fn test_percentage_with_cap_proof_string() {
+        let max_value: u64 = 3;
+
+        let percentage_commitment_str = "JGuzRjhmp3d8PWshbrN3Q7kg027OdPn7IU26ISTiz3c=";
+        let pod_percentage_commitment =
+            PodPedersenCommitment::from_str(percentage_commitment_str).unwrap();
+        let percentage_commitment: PedersenCommitment =
+            pod_percentage_commitment.try_into().unwrap();
+
+        let delta_commitment_str = "3mwfK4u0J0UqCVznbxyCjlGEgMrI+XHdW7g00YVjSVA=";
+        let pod_delta_commitment = PodPedersenCommitment::from_str(delta_commitment_str).unwrap();
+        let delta_commitment: PedersenCommitment = pod_delta_commitment.try_into().unwrap();
+
+        let claimed_commitment_str = "/t9n3yJa7p9wJV5P2cclnUiirKU5oNUv/gQMe27WMT4=";
+        let pod_claimed_commitment =
+            PodPedersenCommitment::from_str(claimed_commitment_str).unwrap();
+        let claimed_commitment: PedersenCommitment = pod_claimed_commitment.try_into().unwrap();
+
+        let proof_str = "SpmzL7hrLLp7P/Cz+2kBh22QKq3mWb0v28Er6lO9aRfBer77VY03i9VSEd4uHYMXdaf/MBPUsDVjUxNjoauwBmw6OrAcq6tq9o1Z+NS8lkukVh6sqSrSh9dy9ipq6JcIePAVmGwDNk07ACgPE/ynrenwSPJ7ZHDGZszGkw95h25gTKPyoaMbvZoXGLtkuHmvXJ7KBBJmK2eTzELb6UF2HOUg9cGFgomL8Xa3l14LBDMwLAokJK4n2d6eTkk1O0ECddmTDwoG6lmt0fHXYm37Z+k4yrQkhUgKwph2nLWG3Q7zvRM2qVFxFUGfLWJq5Sm7l7segOm+hQpRaH+q7OHNBg==";
+        let pod_proof = PodPercentageWithCapProof::from_str(proof_str).unwrap();
+        let proof: PercentageWithCapProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"test");
+
+        proof
             .verify(
                 &percentage_commitment,
                 &delta_commitment,
@@ -701,6 +744,6 @@ mod test {
                 max_value,
                 &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
     }
 }

+ 177 - 1
zk-sdk/src/sigma_proofs/pod.rs

@@ -16,8 +16,13 @@ use crate::sigma_proofs::{
     zero_ciphertext::ZeroCiphertextProof,
 };
 use {
-    crate::sigma_proofs::{errors::*, *},
+    crate::{
+        pod::{impl_from_bytes, impl_from_str},
+        sigma_proofs::{errors::*, *},
+    },
+    base64::{prelude::BASE64_STANDARD, Engine},
     bytemuck::{Pod, Zeroable},
+    std::fmt,
 };
 
 /// The `CiphertextCommitmentEqualityProof` type as a `Pod`.
@@ -43,6 +48,25 @@ impl TryFrom<PodCiphertextCommitmentEqualityProof> for CiphertextCommitmentEqual
     }
 }
 
+const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_MAX_BASE64_LEN: usize = 256;
+
+impl fmt::Display for PodCiphertextCommitmentEqualityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodCiphertextCommitmentEqualityProof,
+    BYTES_LEN = CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN,
+    BASE64_LEN = CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodCiphertextCommitmentEqualityProof,
+    BYTES_LEN = CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN
+);
+
 /// The `CiphertextCiphertextEqualityProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -66,6 +90,25 @@ impl TryFrom<PodCiphertextCiphertextEqualityProof> for CiphertextCiphertextEqual
     }
 }
 
+const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_MAX_BASE64_LEN: usize = 300;
+
+impl fmt::Display for PodCiphertextCiphertextEqualityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodCiphertextCiphertextEqualityProof,
+    BYTES_LEN = CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN,
+    BASE64_LEN = CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodCiphertextCiphertextEqualityProof,
+    BYTES_LEN = CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN
+);
+
 /// The `GroupedCiphertext2HandlesValidityProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -89,6 +132,25 @@ impl TryFrom<PodGroupedCiphertext2HandlesValidityProof> for GroupedCiphertext2Ha
     }
 }
 
+const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 216;
+
+impl fmt::Display for PodGroupedCiphertext2HandlesValidityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodGroupedCiphertext2HandlesValidityProof,
+    BYTES_LEN = GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN,
+    BASE64_LEN = GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodGroupedCiphertext2HandlesValidityProof,
+    BYTES_LEN = GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN
+);
+
 /// The `GroupedCiphertext3HandlesValidityProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -112,6 +174,25 @@ impl TryFrom<PodGroupedCiphertext3HandlesValidityProof> for GroupedCiphertext3Ha
     }
 }
 
+const GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 256;
+
+impl fmt::Display for PodGroupedCiphertext3HandlesValidityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodGroupedCiphertext3HandlesValidityProof,
+    BYTES_LEN = GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN,
+    BASE64_LEN = GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodGroupedCiphertext3HandlesValidityProof,
+    BYTES_LEN = GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN
+);
+
 /// The `BatchedGroupedCiphertext2HandlesValidityProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -141,6 +222,25 @@ impl TryFrom<PodBatchedGroupedCiphertext2HandlesValidityProof>
     }
 }
 
+const BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 216;
+
+impl fmt::Display for PodBatchedGroupedCiphertext2HandlesValidityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodBatchedGroupedCiphertext2HandlesValidityProof,
+    BYTES_LEN = BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN,
+    BASE64_LEN = BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodBatchedGroupedCiphertext2HandlesValidityProof,
+    BYTES_LEN = BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN
+);
+
 /// The `BatchedGroupedCiphertext3HandlesValidityProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -170,6 +270,25 @@ impl TryFrom<PodBatchedGroupedCiphertext3HandlesValidityProof>
     }
 }
 
+const BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 256;
+
+impl fmt::Display for PodBatchedGroupedCiphertext3HandlesValidityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodBatchedGroupedCiphertext3HandlesValidityProof,
+    BYTES_LEN = BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN,
+    BASE64_LEN = BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodBatchedGroupedCiphertext3HandlesValidityProof,
+    BYTES_LEN = BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN
+);
+
 /// The `ZeroCiphertextProof` type as a `Pod`.
 #[derive(Clone, Copy)]
 #[repr(transparent)]
@@ -191,6 +310,25 @@ impl TryFrom<PodZeroCiphertextProof> for ZeroCiphertextProof {
     }
 }
 
+const ZERO_CIPHERTEXT_PROOF_MAX_BASE64_LEN: usize = 128;
+
+impl fmt::Display for PodZeroCiphertextProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodZeroCiphertextProof,
+    BYTES_LEN = ZERO_CIPHERTEXT_PROOF_LEN,
+    BASE64_LEN = ZERO_CIPHERTEXT_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodZeroCiphertextProof,
+    BYTES_LEN = ZERO_CIPHERTEXT_PROOF_LEN
+);
+
 /// The `PercentageWithCapProof` type as a `Pod`.
 #[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable)]
 #[repr(transparent)]
@@ -212,6 +350,25 @@ impl TryFrom<PodPercentageWithCapProof> for PercentageWithCapProof {
     }
 }
 
+const PERCENTAGE_WITH_CAP_PROOF_MAX_BASE64_LEN: usize = 344;
+
+impl fmt::Display for PodPercentageWithCapProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodPercentageWithCapProof,
+    BYTES_LEN = PERCENTAGE_WITH_CAP_PROOF_LEN,
+    BASE64_LEN = PERCENTAGE_WITH_CAP_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodPercentageWithCapProof,
+    BYTES_LEN = PERCENTAGE_WITH_CAP_PROOF_LEN
+);
+
 /// The `PubkeyValidityProof` type as a `Pod`.
 #[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable)]
 #[repr(transparent)]
@@ -233,6 +390,25 @@ impl TryFrom<PodPubkeyValidityProof> for PubkeyValidityProof {
     }
 }
 
+const PUBKEY_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 88;
+
+impl fmt::Display for PodPubkeyValidityProof {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", BASE64_STANDARD.encode(self.0))
+    }
+}
+
+impl_from_str!(
+    TYPE = PodPubkeyValidityProof,
+    BYTES_LEN = PUBKEY_VALIDITY_PROOF_LEN,
+    BASE64_LEN = PUBKEY_VALIDITY_PROOF_MAX_BASE64_LEN
+);
+
+impl_from_bytes!(
+    TYPE = PodPubkeyValidityProof,
+    BYTES_LEN = PUBKEY_VALIDITY_PROOF_LEN
+);
+
 // The sigma proof pod types are wrappers for byte arrays, which are both `Pod` and `Zeroable`. However,
 // the marker traits `bytemuck::Pod` and `bytemuck::Zeroable` can only be derived for power-of-two
 // length byte arrays. Directly implement these traits for the sigma proof pod types.

+ 23 - 4
zk-sdk/src/sigma_proofs/pubkey_validity.rs

@@ -140,7 +140,11 @@ impl PubkeyValidityProof {
 mod test {
     use {
         super::*,
+        crate::{
+            encryption::pod::elgamal::PodElGamalPubkey, sigma_proofs::pod::PodPubkeyValidityProof,
+        },
         solana_sdk::{pubkey::Pubkey, signature::Keypair},
+        std::str::FromStr,
     };
 
     #[test]
@@ -152,9 +156,9 @@ mod test {
         let mut verifier_transcript = Transcript::new(b"test");
 
         let proof = PubkeyValidityProof::new(&keypair, &mut prover_transcript);
-        assert!(proof
+        proof
             .verify(keypair.pubkey(), &mut verifier_transcript)
-            .is_ok());
+            .unwrap();
 
         // derived ElGamal keypair
         let keypair =
@@ -164,8 +168,23 @@ mod test {
         let mut verifier_transcript = Transcript::new(b"test");
 
         let proof = PubkeyValidityProof::new(&keypair, &mut prover_transcript);
-        assert!(proof
+        proof
             .verify(keypair.pubkey(), &mut verifier_transcript)
-            .is_ok());
+            .unwrap();
+    }
+
+    #[test]
+    fn test_pubkey_proof_str() {
+        let pubkey_str = "XKF3GnFDX4HBoBEj04yDTr6Lqx+0qp9pQyPzFjyVmXY=";
+        let pod_pubkey = PodElGamalPubkey::from_str(pubkey_str).unwrap();
+        let pubkey: ElGamalPubkey = pod_pubkey.try_into().unwrap();
+
+        let proof_str = "5hmM4uVtfJ2JfCcjWpo2dEbg22n4CdzHYQF4oBgWSGeYAh5d91z4emkjeXq9ihtmqAR+7BYCv44TqQWoMQrECA==";
+        let pod_proof = PodPubkeyValidityProof::from_str(proof_str).unwrap();
+        let proof: PubkeyValidityProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"test");
+
+        proof.verify(&pubkey, &mut verifier_transcript).unwrap();
     }
 }

+ 35 - 9
zk-sdk/src/sigma_proofs/zero_ciphertext.rs

@@ -180,14 +180,19 @@ impl ZeroCiphertextProof {
 mod test {
     use {
         super::*,
-        crate::encryption::{
-            elgamal::{DecryptHandle, ElGamalKeypair},
-            pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
+        crate::{
+            encryption::{
+                elgamal::{DecryptHandle, ElGamalKeypair},
+                pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
+                pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey},
+            },
+            sigma_proofs::pod::PodZeroCiphertextProof,
         },
+        std::str::FromStr,
     };
 
     #[test]
-    fn test_zero_cipehrtext_proof_correctness() {
+    fn test_zero_ciphertext_proof_correctness() {
         let keypair = ElGamalKeypair::new_rand();
 
         let mut prover_transcript = Transcript::new(b"test");
@@ -196,13 +201,13 @@ mod test {
         // general case: encryption of 0
         let elgamal_ciphertext = keypair.pubkey().encrypt(0_u64);
         let proof = ZeroCiphertextProof::new(&keypair, &elgamal_ciphertext, &mut prover_transcript);
-        assert!(proof
+        proof
             .verify(
                 keypair.pubkey(),
                 &elgamal_ciphertext,
-                &mut verifier_transcript
+                &mut verifier_transcript,
             )
-            .is_ok());
+            .unwrap();
 
         // general case: encryption of > 0
         let elgamal_ciphertext = keypair.pubkey().encrypt(1_u64);
@@ -228,9 +233,9 @@ mod test {
 
         let proof = ZeroCiphertextProof::new(&keypair, &ciphertext, &mut prover_transcript);
 
-        assert!(proof
+        proof
             .verify(keypair.pubkey(), &ciphertext, &mut verifier_transcript)
-            .is_ok());
+            .unwrap();
 
         // if only either commitment or handle is zero, the ciphertext is always invalid and proof
         // verification should always reject
@@ -281,4 +286,25 @@ mod test {
             .verify(keypair.pubkey(), &ciphertext, &mut verifier_transcript)
             .is_err());
     }
+
+    #[test]
+    fn test_zero_ciphertext_proof_string() {
+        let pubkey_str = "Vlx+Fr61KnreO27JDg5MsBN8NgbICGa3fIech8oZ4hQ=";
+        let pod_pubkey = PodElGamalPubkey::from_str(pubkey_str).unwrap();
+        let pubkey: ElGamalPubkey = pod_pubkey.try_into().unwrap();
+
+        let ciphertext_str = "wps5X1mou5PUdPD+llxiJ+aoX4YWrR/S6/U2MUC2LjLS7wDu6S9nOG92VMnlngQaP4irBY0OqlsGdXS4j8DROg==";
+        let pod_ciphertext = PodElGamalCiphertext::from_str(ciphertext_str).unwrap();
+        let ciphertext: ElGamalCiphertext = pod_ciphertext.try_into().unwrap();
+
+        let proof_str = "qMDiQ5zPcTYFhchYBZzRS81UGIt2QRNce2/ULEqDBXBQEnGRI0u0G1HzRJfpIbOWCHBwMaNgsT1jTZwTOTWyMBE/2UjHI4x9IFpAM6ccGuexo/HjSECPDgL+85zrfA8L";
+        let pod_proof = PodZeroCiphertextProof::from_str(proof_str).unwrap();
+        let proof: ZeroCiphertextProof = pod_proof.try_into().unwrap();
+
+        let mut verifier_transcript = Transcript::new(b"test");
+
+        proof
+            .verify(&pubkey, &ciphertext, &mut verifier_transcript)
+            .unwrap();
+    }
 }