diff --git a/Cargo.lock b/Cargo.lock index 2ab4bee..e77d91a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -951,12 +951,23 @@ dependencies = [ "solana-seed-phrase", "solana-signature", "solana-signer", + "solana-zk-sdk-pod", "subtle", "thiserror 2.0.17", "tiny-bip39", "zeroize", ] +[[package]] +name = "solana-zk-sdk-pod" +version = "0.1.0" +dependencies = [ + "base64", + "bytemuck", + "bytemuck_derive", + "thiserror 2.0.17", +] + [[package]] name = "solana-zk-sdk-wasm-js" version = "0.1.0" @@ -965,6 +976,7 @@ dependencies = [ "getrandom", "js-sys", "solana-zk-sdk", + "solana-zk-sdk-pod", "wasm-bindgen", "wasm-bindgen-test", ] diff --git a/Cargo.toml b/Cargo.toml index a353033..8cc9ded 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["zk-sdk", "zk-sdk-wasm-js"] +members = ["zk-sdk", "zk-sdk-pod", "zk-sdk-wasm-js"] [workspace.package] authors = ["Anza Maintainers "] @@ -53,6 +53,7 @@ solana-seed-phrase = "3.0.0" solana-signature = { version = "3.0.0", default-features = false } solana-signer = "3.0.0" solana-zk-sdk = { path = "zk-sdk", version = "4.0.0" } +solana-zk-sdk-pod = { path = "zk-sdk-pod", version = "0.1.0" } solana-zk-sdk-wasm-js = { path = "zk-sdk-wasm-js", version = "0.1.0" } subtle = "2.6.1" thiserror = "2.0.17" diff --git a/zk-sdk-pod/Cargo.toml b/zk-sdk-pod/Cargo.toml new file mode 100644 index 0000000..3dc8398 --- /dev/null +++ b/zk-sdk-pod/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "solana-zk-sdk-pod" +description = "Solana ZK SDK Plain Old Data (Pod)" +documentation = "https://docs.rs/solana-zk-sdk-pod" +version = "0.1.0" +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +base64 = { workspace = true } +bytemuck = { workspace = true } +bytemuck_derive = { workspace = true } +thiserror = { workspace = true } + +[lib] +crate-type = ["rlib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[lints] +workspace = true diff --git a/zk-sdk/src/encryption/pod/auth_encryption.rs b/zk-sdk-pod/src/encryption/auth_encryption.rs similarity index 52% rename from zk-sdk/src/encryption/pod/auth_encryption.rs rename to zk-sdk-pod/src/encryption/auth_encryption.rs index 8496e77..a359930 100644 --- a/zk-sdk/src/encryption/pod/auth_encryption.rs +++ b/zk-sdk-pod/src/encryption/auth_encryption.rs @@ -1,11 +1,9 @@ //! Plain Old Data types for the AES128-GCM-SIV authenticated encryption scheme. -#[cfg(not(target_os = "solana"))] -use crate::{encryption::auth_encryption::AeCiphertext, errors::AuthenticatedEncryptionError}; use { crate::{ encryption::AE_CIPHERTEXT_LEN, - pod::{impl_from_bytes, impl_from_str}, + macros::{impl_from_bytes, impl_from_str}, }, base64::{prelude::BASE64_STANDARD, Engine}, bytemuck::{Pod, Zeroable}, @@ -18,7 +16,7 @@ const AE_CIPHERTEXT_MAX_BASE64_LEN: usize = 48; /// The `AeCiphertext` type as a `Pod`. #[derive(Clone, Copy, PartialEq, Eq)] #[repr(transparent)] -pub struct PodAeCiphertext(pub(crate) [u8; AE_CIPHERTEXT_LEN]); +pub struct PodAeCiphertext(pub [u8; AE_CIPHERTEXT_LEN]); // `PodAeCiphertext` is a wrapper type for a byte array, which is both `Pod` and `Zeroable`. However, // the marker traits `bytemuck::Pod` and `bytemuck::Zeroable` can only be derived for power-of-two @@ -51,35 +49,3 @@ impl Default for PodAeCiphertext { Self::zeroed() } } - -#[cfg(not(target_os = "solana"))] -impl From for PodAeCiphertext { - fn from(decoded_ciphertext: AeCiphertext) -> Self { - Self(decoded_ciphertext.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for AeCiphertext { - type Error = AuthenticatedEncryptionError; - - fn try_from(pod_ciphertext: PodAeCiphertext) -> Result { - Self::from_bytes(&pod_ciphertext.0).ok_or(AuthenticatedEncryptionError::Deserialization) - } -} - -#[cfg(test)] -mod tests { - use {super::*, crate::encryption::auth_encryption::AeKey, std::str::FromStr}; - - #[test] - fn ae_ciphertext_fromstr() { - let ae_key = AeKey::new_rand(); - let expected_ae_ciphertext: PodAeCiphertext = ae_key.encrypt(0_u64).into(); - - let ae_ciphertext_base64_str = format!("{}", expected_ae_ciphertext); - let computed_ae_ciphertext = PodAeCiphertext::from_str(&ae_ciphertext_base64_str).unwrap(); - - assert_eq!(expected_ae_ciphertext, computed_ae_ciphertext); - } -} diff --git a/zk-sdk-pod/src/encryption/elgamal.rs b/zk-sdk-pod/src/encryption/elgamal.rs new file mode 100644 index 0000000..3559c43 --- /dev/null +++ b/zk-sdk-pod/src/encryption/elgamal.rs @@ -0,0 +1,104 @@ +//! Plain Old Data types for the ElGamal encryption scheme. + +use { + crate::{ + encryption::{DECRYPT_HANDLE_LEN, ELGAMAL_CIPHERTEXT_LEN, ELGAMAL_PUBKEY_LEN}, + macros::{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; + +/// 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)] +pub struct PodElGamalCiphertext(pub [u8; ELGAMAL_CIPHERTEXT_LEN]); + +impl fmt::Debug for PodElGamalCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl fmt::Display for PodElGamalCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", BASE64_STANDARD.encode(self.0)) + } +} + +impl Default for PodElGamalCiphertext { + fn default() -> Self { + Self::zeroed() + } +} + +impl_from_str!( + TYPE = PodElGamalCiphertext, + BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN, + BASE64_LEN = ELGAMAL_CIPHERTEXT_MAX_BASE64_LEN +); + +impl_from_bytes!( + TYPE = PodElGamalCiphertext, + BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN +); + +/// The `ElGamalPubkey` type as a `Pod`. +#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] +#[repr(transparent)] +pub struct PodElGamalPubkey(pub [u8; ELGAMAL_PUBKEY_LEN]); + +impl fmt::Debug for PodElGamalPubkey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl fmt::Display for PodElGamalPubkey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", BASE64_STANDARD.encode(self.0)) + } +} + +impl_from_str!( + TYPE = PodElGamalPubkey, + BYTES_LEN = ELGAMAL_PUBKEY_LEN, + BASE64_LEN = ELGAMAL_PUBKEY_MAX_BASE64_LEN +); + +impl_from_bytes!(TYPE = PodElGamalPubkey, BYTES_LEN = ELGAMAL_PUBKEY_LEN); + +/// The `DecryptHandle` type as a `Pod`. +#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] +#[repr(transparent)] +pub struct PodDecryptHandle(pub [u8; DECRYPT_HANDLE_LEN]); + +impl fmt::Debug for PodDecryptHandle { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +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); diff --git a/zk-sdk-pod/src/encryption/grouped_elgamal.rs b/zk-sdk-pod/src/encryption/grouped_elgamal.rs new file mode 100644 index 0000000..93891fb --- /dev/null +++ b/zk-sdk-pod/src/encryption/grouped_elgamal.rs @@ -0,0 +1,138 @@ +//! Plain Old Data types for the Grouped ElGamal encryption scheme. + +use { + crate::{ + encryption::{ + elgamal::PodElGamalCiphertext, pedersen::PodPedersenCommitment, DECRYPT_HANDLE_LEN, + ELGAMAL_CIPHERTEXT_LEN, PEDERSEN_COMMITMENT_LEN, + }, + macros::{impl_from_bytes, impl_from_str}, + }, + base64::{prelude::BASE64_STANDARD, Engine}, + bytemuck::Zeroable, + std::fmt, +}; + +/// Maximum length of a base64 encoded grouped ElGamal ciphertext with 2 handles +const GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_MAX_BASE64_LEN: usize = 132; + +/// Maximum length of a base64 encoded grouped ElGamal ciphertext with 3 handles +const GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_MAX_BASE64_LEN: usize = 176; + +macro_rules! impl_extract { + (TYPE = $type:ident) => { + impl $type { + /// Extract the commitment component from a grouped ciphertext + pub fn extract_commitment(&self) -> PodPedersenCommitment { + // `GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES` guaranteed to be at least `PEDERSEN_COMMITMENT_LEN` + let commitment = self.0[..PEDERSEN_COMMITMENT_LEN].try_into().unwrap(); + PodPedersenCommitment(commitment) + } + + /// Extract a regular ElGamal ciphertext using the decrypt handle at a specified index. + pub fn try_extract_ciphertext( + &self, + index: usize, + ) -> Result { + let mut ciphertext_bytes = [0u8; ELGAMAL_CIPHERTEXT_LEN]; + ciphertext_bytes[..PEDERSEN_COMMITMENT_LEN] + .copy_from_slice(&self.0[..PEDERSEN_COMMITMENT_LEN]); + + let handle_start = DECRYPT_HANDLE_LEN + .checked_mul(index) + .and_then(|n| n.checked_add(PEDERSEN_COMMITMENT_LEN)) + .ok_or(crate::errors::PodParseError::GroupedCiphertextIndexOutOfBounds)?; + let handle_end = handle_start + .checked_add(DECRYPT_HANDLE_LEN) + .ok_or(crate::errors::PodParseError::GroupedCiphertextIndexOutOfBounds)?; + ciphertext_bytes[PEDERSEN_COMMITMENT_LEN..].copy_from_slice( + self.0 + .get(handle_start..handle_end) + .ok_or(crate::errors::PodParseError::GroupedCiphertextIndexOutOfBounds)?, + ); + + Ok(PodElGamalCiphertext(ciphertext_bytes)) + } + } + }; +} + +/// Byte length of a grouped ElGamal ciphertext with 2 handles +const GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES: usize = + PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN; + +/// Byte length of a grouped ElGamal ciphertext with 3 handles +const GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES: usize = + PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN; + +/// The `GroupedElGamalCiphertext` type with two decryption handles as a `Pod` +#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] +#[repr(transparent)] +pub struct PodGroupedElGamalCiphertext2Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES]); + +impl fmt::Debug for PodGroupedElGamalCiphertext2Handles { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl Default for PodGroupedElGamalCiphertext2Handles { + fn default() -> Self { + Self::zeroed() + } +} + +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, + BASE64_LEN = GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_MAX_BASE64_LEN +); + +impl_from_bytes!( + TYPE = PodGroupedElGamalCiphertext2Handles, + BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES +); + +impl_extract!(TYPE = PodGroupedElGamalCiphertext2Handles); + +/// The `GroupedElGamalCiphertext` type with three decryption handles as a `Pod` +#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] +#[repr(transparent)] +pub struct PodGroupedElGamalCiphertext3Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES]); + +impl fmt::Debug for PodGroupedElGamalCiphertext3Handles { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl Default for PodGroupedElGamalCiphertext3Handles { + fn default() -> Self { + Self::zeroed() + } +} + +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, + BASE64_LEN = GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_MAX_BASE64_LEN +); + +impl_from_bytes!( + TYPE = PodGroupedElGamalCiphertext3Handles, + BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES +); + +impl_extract!(TYPE = PodGroupedElGamalCiphertext3Handles); diff --git a/zk-sdk-pod/src/encryption/mod.rs b/zk-sdk-pod/src/encryption/mod.rs new file mode 100644 index 0000000..7cc8039 --- /dev/null +++ b/zk-sdk-pod/src/encryption/mod.rs @@ -0,0 +1,34 @@ +use crate::{RISTRETTO_POINT_LEN, SCALAR_LEN}; + +pub mod auth_encryption; +pub mod elgamal; +pub mod grouped_elgamal; +pub mod pedersen; + +/// Byte length of an authenticated encryption secret key +pub const AE_KEY_LEN: usize = 16; + +/// Byte length of a complete authenticated encryption ciphertext component that includes the +/// ciphertext and nonce components +pub const AE_CIPHERTEXT_LEN: usize = 36; + +/// Byte length of a decrypt handle +pub const DECRYPT_HANDLE_LEN: usize = RISTRETTO_POINT_LEN; + +/// Byte length of an ElGamal ciphertext +pub const ELGAMAL_CIPHERTEXT_LEN: usize = PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN; + +/// Byte length of an ElGamal public key +pub const ELGAMAL_PUBKEY_LEN: usize = RISTRETTO_POINT_LEN; + +/// Byte length of an ElGamal secret key +pub const ELGAMAL_SECRET_KEY_LEN: usize = SCALAR_LEN; + +/// Byte length of an ElGamal keypair +pub const ELGAMAL_KEYPAIR_LEN: usize = ELGAMAL_PUBKEY_LEN + ELGAMAL_SECRET_KEY_LEN; + +/// Byte length of a Pedersen opening. +pub const PEDERSEN_OPENING_LEN: usize = SCALAR_LEN; + +/// Byte length of a Pedersen commitment. +pub const PEDERSEN_COMMITMENT_LEN: usize = RISTRETTO_POINT_LEN; diff --git a/zk-sdk-pod/src/encryption/pedersen.rs b/zk-sdk-pod/src/encryption/pedersen.rs new file mode 100644 index 0000000..9bd6573 --- /dev/null +++ b/zk-sdk-pod/src/encryption/pedersen.rs @@ -0,0 +1,42 @@ +//! Plain Old Data type for the Pedersen commitment scheme. + +use { + crate::{ + encryption::PEDERSEN_COMMITMENT_LEN, + macros::{impl_from_bytes, impl_from_str}, + }, + base64::{prelude::BASE64_STANDARD, Engine}, + bytemuck_derive::{Pod, Zeroable}, + std::fmt, +}; + +/// Maximum length of a base64 encoded ElGamal public key +const PEDERSEN_COMMITMENT_MAX_BASE64_LEN: usize = 44; + +/// The `PedersenCommitment` type as a `Pod`. +#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)] +#[repr(transparent)] +pub struct PodPedersenCommitment(pub [u8; PEDERSEN_COMMITMENT_LEN]); + +impl fmt::Debug for PodPedersenCommitment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +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, + BASE64_LEN = PEDERSEN_COMMITMENT_MAX_BASE64_LEN +); + +impl_from_bytes!( + TYPE = PodPedersenCommitment, + BYTES_LEN = PEDERSEN_COMMITMENT_LEN +); diff --git a/zk-sdk-pod/src/errors.rs b/zk-sdk-pod/src/errors.rs new file mode 100644 index 0000000..2ed8e78 --- /dev/null +++ b/zk-sdk-pod/src/errors.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +#[derive(Error, Debug, Clone, Eq, PartialEq)] +pub enum PodParseError { + #[error("String is the wrong size")] + WrongSize, + #[error("Invalid Base64 string")] + Invalid, + #[error("Grouped ciphertext index out of bounds")] + GroupedCiphertextIndexOutOfBounds, +} diff --git a/zk-sdk-pod/src/lib.rs b/zk-sdk-pod/src/lib.rs new file mode 100644 index 0000000..27cb817 --- /dev/null +++ b/zk-sdk-pod/src/lib.rs @@ -0,0 +1,14 @@ +pub mod encryption; +pub mod errors; +pub mod macros; +pub mod num; +pub mod proof_data; +pub mod range_proof; +pub mod sigma_proofs; + +/// Byte length of a compressed Ristretto point or scalar in Curve255519 +const UNIT_LEN: usize = 32; +/// Byte length of a compressed Ristretto point in Curve25519 +const RISTRETTO_POINT_LEN: usize = UNIT_LEN; +/// Byte length of a scalar in Curve25519 +const SCALAR_LEN: usize = UNIT_LEN; diff --git a/zk-sdk-pod/src/macros.rs b/zk-sdk-pod/src/macros.rs new file mode 100644 index 0000000..953ae97 --- /dev/null +++ b/zk-sdk-pod/src/macros.rs @@ -0,0 +1,34 @@ +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::PodParseError; + + fn from_str(s: &str) -> Result { + 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; diff --git a/zk-sdk-pod/src/num.rs b/zk-sdk-pod/src/num.rs new file mode 100644 index 0000000..2240b5c --- /dev/null +++ b/zk-sdk-pod/src/num.rs @@ -0,0 +1,29 @@ +use bytemuck_derive::{Pod, Zeroable}; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodU16([u8; 2]); +impl From for PodU16 { + fn from(n: u16) -> Self { + Self(n.to_le_bytes()) + } +} +impl From for u16 { + fn from(pod: PodU16) -> Self { + Self::from_le_bytes(pod.0) + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Pod, Zeroable)] +#[repr(transparent)] +pub struct PodU64([u8; 8]); +impl From for PodU64 { + fn from(n: u64) -> Self { + Self(n.to_le_bytes()) + } +} +impl From for u64 { + fn from(pod: PodU64) -> Self { + Self::from_le_bytes(pod.0) + } +} diff --git a/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs new file mode 100644 index 0000000..21d7365 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs @@ -0,0 +1,34 @@ +use { + crate::{ + encryption::{ + elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext2Handles, + }, + sigma_proofs::PodBatchedGroupedCiphertext2HandlesValidityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyBatchedGroupedCiphertextValidity` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext2HandlesValidityProofData { + pub context: BatchedGroupedCiphertext2HandlesValidityProofContext, + + pub proof: PodBatchedGroupedCiphertext2HandlesValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext2HandlesValidityProofContext { + pub first_pubkey: PodElGamalPubkey, // 32 bytes + + pub second_pubkey: PodElGamalPubkey, // 32 bytes + + pub grouped_ciphertext_lo: PodGroupedElGamalCiphertext2Handles, // 96 bytes + + pub grouped_ciphertext_hi: PodGroupedElGamalCiphertext2Handles, // 96 bytes +} diff --git a/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs new file mode 100644 index 0000000..be8e7ca --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs @@ -0,0 +1,36 @@ +use { + crate::{ + encryption::{ + elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext3Handles, + }, + sigma_proofs::PodBatchedGroupedCiphertext3HandlesValidityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext3HandlesValidityProofData { + pub context: BatchedGroupedCiphertext3HandlesValidityProofContext, + + pub proof: PodBatchedGroupedCiphertext3HandlesValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedGroupedCiphertext3HandlesValidityProofContext { + pub first_pubkey: PodElGamalPubkey, // 32 bytes + + pub second_pubkey: PodElGamalPubkey, // 32 bytes + + pub third_pubkey: PodElGamalPubkey, // 32 bytes + + pub grouped_ciphertext_lo: PodGroupedElGamalCiphertext3Handles, // 128 bytes + + pub grouped_ciphertext_hi: PodGroupedElGamalCiphertext3Handles, // 128 bytes +} diff --git a/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/mod.rs b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/mod.rs new file mode 100644 index 0000000..70d2a7e --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_grouped_ciphertext_validity/mod.rs @@ -0,0 +1,13 @@ +mod handles_2; +mod handles_3; + +pub use { + handles_2::{ + BatchedGroupedCiphertext2HandlesValidityProofContext, + BatchedGroupedCiphertext2HandlesValidityProofData, + }, + handles_3::{ + BatchedGroupedCiphertext3HandlesValidityProofContext, + BatchedGroupedCiphertext3HandlesValidityProofData, + }, +}; diff --git a/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u128.rs b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u128.rs new file mode 100644 index 0000000..c307949 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u128.rs @@ -0,0 +1,21 @@ +use { + crate::{ + proof_data::batched_range_proof::BatchedRangeProofContext, range_proof::PodRangeProofU128, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyBatchedRangeProofU128` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedRangeProofU128Data { + /// The context data for a batched range proof + pub context: BatchedRangeProofContext, + + /// The batched range proof + pub proof: PodRangeProofU128, +} diff --git a/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u256.rs b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u256.rs new file mode 100644 index 0000000..1a0063e --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u256.rs @@ -0,0 +1,21 @@ +use { + crate::{ + proof_data::batched_range_proof::BatchedRangeProofContext, range_proof::PodRangeProofU256, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::BatchedRangeProofU256Data` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedRangeProofU256Data { + /// The context data for a batched range proof + pub context: BatchedRangeProofContext, + + /// The batched range proof + pub proof: PodRangeProofU256, +} diff --git a/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u64.rs b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u64.rs new file mode 100644 index 0000000..46aed4b --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_range_proof/batched_range_proof_u64.rs @@ -0,0 +1,21 @@ +use { + crate::{ + proof_data::batched_range_proof::BatchedRangeProofContext, range_proof::PodRangeProofU64, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyBatchedRangeProofU64` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedRangeProofU64Data { + /// The context data for a batched range proof + pub context: BatchedRangeProofContext, + + /// The batched range proof + pub proof: PodRangeProofU64, +} diff --git a/zk-sdk-pod/src/proof_data/batched_range_proof/mod.rs b/zk-sdk-pod/src/proof_data/batched_range_proof/mod.rs new file mode 100644 index 0000000..ee6bfda --- /dev/null +++ b/zk-sdk-pod/src/proof_data/batched_range_proof/mod.rs @@ -0,0 +1,29 @@ +use { + crate::encryption::pedersen::PodPedersenCommitment, + bytemuck_derive::{Pod, Zeroable}, +}; + +pub mod batched_range_proof_u128; +pub mod batched_range_proof_u256; +pub mod batched_range_proof_u64; + +pub use { + batched_range_proof_u128::BatchedRangeProofU128Data, + batched_range_proof_u256::BatchedRangeProofU256Data, + batched_range_proof_u64::BatchedRangeProofU64Data, +}; + +/// The maximum number of Pedersen commitments that can be processed in a single batched range proof. +const MAX_COMMITMENTS: usize = 8; + +/// The context data needed to verify a range-proof for a Pedersen committed value. +/// +/// This struct holds the public information that a batched range proof certifies. It includes the +/// Pedersen commitments and their corresponding bit lengths. This context is shared by all +/// `VerifyBatchedRangeProof{N}` instructions. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct BatchedRangeProofContext { + pub commitments: [PodPedersenCommitment; MAX_COMMITMENTS], + pub bit_lengths: [u8; MAX_COMMITMENTS], +} diff --git a/zk-sdk-pod/src/proof_data/ciphertext_ciphertext_equality.rs b/zk-sdk-pod/src/proof_data/ciphertext_ciphertext_equality.rs new file mode 100644 index 0000000..7bac7a8 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/ciphertext_ciphertext_equality.rs @@ -0,0 +1,33 @@ +use { + crate::{ + encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + sigma_proofs::PodCiphertextCiphertextEqualityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyCiphertextCiphertextEquality` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCiphertextEqualityProofData { + pub context: CiphertextCiphertextEqualityProofContext, + + pub proof: PodCiphertextCiphertextEqualityProof, +} + +/// The context data needed to verify a ciphertext-ciphertext equality proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCiphertextEqualityProofContext { + pub first_pubkey: PodElGamalPubkey, // 32 bytes + + pub second_pubkey: PodElGamalPubkey, // 32 bytes + + pub first_ciphertext: PodElGamalCiphertext, // 64 bytes + + pub second_ciphertext: PodElGamalCiphertext, // 64 bytes +} diff --git a/zk-sdk-pod/src/proof_data/ciphertext_commitment_equality.rs b/zk-sdk-pod/src/proof_data/ciphertext_commitment_equality.rs new file mode 100644 index 0000000..7836a70 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/ciphertext_commitment_equality.rs @@ -0,0 +1,36 @@ +use { + crate::{ + encryption::{ + elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, + }, + sigma_proofs::PodCiphertextCommitmentEqualityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCommitmentEqualityProofData { + pub context: CiphertextCommitmentEqualityProofContext, + pub proof: PodCiphertextCommitmentEqualityProof, +} + +/// The context data needed to verify a ciphertext-commitment equality proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct CiphertextCommitmentEqualityProofContext { + /// The ElGamal pubkey + pub pubkey: PodElGamalPubkey, // 32 bytes + + /// The ciphertext encrypted under the ElGamal pubkey + pub ciphertext: PodElGamalCiphertext, // 64 bytes + + /// The Pedersen commitment + pub commitment: PodPedersenCommitment, // 32 bytes +} diff --git a/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_2.rs b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_2.rs new file mode 100644 index 0000000..2197510 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_2.rs @@ -0,0 +1,32 @@ +use { + crate::{ + encryption::{ + elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext2Handles, + }, + sigma_proofs::PodGroupedCiphertext2HandlesValidityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyGroupedCiphertextValidity` +/// instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext2HandlesValidityProofData { + pub context: GroupedCiphertext2HandlesValidityProofContext, + + pub proof: PodGroupedCiphertext2HandlesValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext2HandlesValidityProofContext { + pub first_pubkey: PodElGamalPubkey, // 32 bytes + + pub second_pubkey: PodElGamalPubkey, // 32 bytes + + pub grouped_ciphertext: PodGroupedElGamalCiphertext2Handles, // 96 bytes +} diff --git a/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_3.rs b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_3.rs new file mode 100644 index 0000000..86259d4 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/handles_3.rs @@ -0,0 +1,34 @@ +use { + crate::{ + encryption::{ + elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext3Handles, + }, + sigma_proofs::PodGroupedCiphertext3HandlesValidityProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the +/// `ProofInstruction::VerifyGroupedCiphertext3HandlesValidity` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext3HandlesValidityProofData { + pub context: GroupedCiphertext3HandlesValidityProofContext, + + pub proof: PodGroupedCiphertext3HandlesValidityProof, +} + +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct GroupedCiphertext3HandlesValidityProofContext { + pub first_pubkey: PodElGamalPubkey, // 32 bytes + + pub second_pubkey: PodElGamalPubkey, // 32 bytes + + pub third_pubkey: PodElGamalPubkey, // 32 bytes + + pub grouped_ciphertext: PodGroupedElGamalCiphertext3Handles, // 128 bytes +} diff --git a/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/mod.rs b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/mod.rs new file mode 100644 index 0000000..7f1149b --- /dev/null +++ b/zk-sdk-pod/src/proof_data/grouped_ciphertext_validity/mod.rs @@ -0,0 +1,11 @@ +mod handles_2; +mod handles_3; + +pub use { + handles_2::{ + GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, + }, + handles_3::{ + GroupedCiphertext3HandlesValidityProofContext, GroupedCiphertext3HandlesValidityProofData, + }, +}; diff --git a/zk-sdk-pod/src/proof_data/mod.rs b/zk-sdk-pod/src/proof_data/mod.rs new file mode 100644 index 0000000..396badc --- /dev/null +++ b/zk-sdk-pod/src/proof_data/mod.rs @@ -0,0 +1,8 @@ +pub mod batched_grouped_ciphertext_validity; +pub mod batched_range_proof; +pub mod ciphertext_ciphertext_equality; +pub mod ciphertext_commitment_equality; +pub mod grouped_ciphertext_validity; +pub mod percentage_with_cap; +pub mod pubkey_validity; +pub mod zero_ciphertext; diff --git a/zk-sdk-pod/src/proof_data/percentage_with_cap.rs b/zk-sdk-pod/src/proof_data/percentage_with_cap.rs new file mode 100644 index 0000000..f43d164 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/percentage_with_cap.rs @@ -0,0 +1,42 @@ +use { + crate::{ + encryption::pedersen::PodPedersenCommitment, num::PodU64, + sigma_proofs::PodPercentageWithCapProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyPercentageWithCap` +/// instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PercentageWithCapProofData { + pub context: PercentageWithCapProofContext, + + pub proof: PodPercentageWithCapProof, +} + +/// The context data needed to verify a percentage-with-cap proof. +/// +/// We refer to [`ZK ElGamal proof`] for the formal details on how the percentage-with-cap proof is +/// computed. +/// +/// [`ZK ElGamal proof`]: https://docs.solanalabs.com/runtime/zk-token-proof +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PercentageWithCapProofContext { + /// The Pedersen commitment to the percentage amount. + pub percentage_commitment: PodPedersenCommitment, + + /// The Pedersen commitment to the delta amount. + pub delta_commitment: PodPedersenCommitment, + + /// The Pedersen commitment to the claimed amount. + pub claimed_commitment: PodPedersenCommitment, + + /// The maximum cap bound. + pub max_value: PodU64, +} diff --git a/zk-sdk-pod/src/proof_data/pubkey_validity.rs b/zk-sdk-pod/src/proof_data/pubkey_validity.rs new file mode 100644 index 0000000..eb34335 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/pubkey_validity.rs @@ -0,0 +1,27 @@ +use { + crate::{encryption::elgamal::PodElGamalPubkey, sigma_proofs::PodPubkeyValidityProof}, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyPubkeyValidity` +/// instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PubkeyValidityProofData { + /// The context data for the public key validity proof + pub context: PubkeyValidityProofContext, // 32 bytes + + /// Proof that the public key is well-formed + pub proof: PodPubkeyValidityProof, // 64 bytes +} + +/// The context data needed to verify a pubkey validity proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct PubkeyValidityProofContext { + /// The public key to be proved + pub pubkey: PodElGamalPubkey, // 32 bytes +} diff --git a/zk-sdk-pod/src/proof_data/zero_ciphertext.rs b/zk-sdk-pod/src/proof_data/zero_ciphertext.rs new file mode 100644 index 0000000..aed0f78 --- /dev/null +++ b/zk-sdk-pod/src/proof_data/zero_ciphertext.rs @@ -0,0 +1,32 @@ +use { + crate::{ + encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + sigma_proofs::PodZeroCiphertextProof, + }, + bytemuck_derive::{Pod, Zeroable}, +}; + +/// The instruction data that is needed for the `ProofInstruction::VerifyZeroCiphertext` instruction. +/// +/// It includes the cryptographic proof as well as the context data information needed to verify +/// the proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct ZeroCiphertextProofData { + /// The context data for the zero-ciphertext proof + pub context: ZeroCiphertextProofContext, // 96 bytes + + /// Proof that the ciphertext is zero + pub proof: PodZeroCiphertextProof, // 96 bytes +} + +/// The context data needed to verify a zero-ciphertext proof. +#[derive(Clone, Copy, Pod, Zeroable)] +#[repr(C)] +pub struct ZeroCiphertextProofContext { + /// The ElGamal pubkey associated with the ElGamal ciphertext + pub pubkey: PodElGamalPubkey, // 32 bytes + + /// The ElGamal ciphertext that encrypts zero + pub ciphertext: PodElGamalCiphertext, // 64 bytes +} diff --git a/zk-sdk-pod/src/range_proof.rs b/zk-sdk-pod/src/range_proof.rs new file mode 100644 index 0000000..1fcb364 --- /dev/null +++ b/zk-sdk-pod/src/range_proof.rs @@ -0,0 +1,111 @@ +//! Plain Old Data types for range proofs. + +use { + crate::{ + macros::{impl_from_bytes, impl_from_str}, + RISTRETTO_POINT_LEN, SCALAR_LEN, + }, + base64::{prelude::BASE64_STANDARD, Engine}, + bytemuck::{Pod, Zeroable}, + std::fmt, +}; + +/// Byte length of a range proof excluding the inner-product proof component +pub const RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN: usize = + 5 * RISTRETTO_POINT_LEN + 2 * SCALAR_LEN; + +/// Byte length of an inner-product proof for a vector of length 64 +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; // 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; // 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; // 800 bytes + +/// The `RangeProof` type as a `Pod` restricted to proofs on 64-bit numbers. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct PodRangeProofU64(pub [u8; RANGE_PROOF_U64_LEN]); + +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)] +pub struct PodRangeProofU128(pub [u8; RANGE_PROOF_U128_LEN]); + +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)] +pub struct PodRangeProofU256(pub [u8; RANGE_PROOF_U256_LEN]); + +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); + +// The range 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 range proof pod types. +unsafe impl Zeroable for PodRangeProofU64 {} +unsafe impl Pod for PodRangeProofU64 {} + +unsafe impl Zeroable for PodRangeProofU128 {} +unsafe impl Pod for PodRangeProofU128 {} + +unsafe impl Zeroable for PodRangeProofU256 {} +unsafe impl Pod for PodRangeProofU256 {} diff --git a/zk-sdk/src/sigma_proofs/pod.rs b/zk-sdk-pod/src/sigma_proofs.rs similarity index 54% rename from zk-sdk/src/sigma_proofs/pod.rs rename to zk-sdk-pod/src/sigma_proofs.rs index 3ce3f38..07ae754 100644 --- a/zk-sdk/src/sigma_proofs/pod.rs +++ b/zk-sdk-pod/src/sigma_proofs.rs @@ -1,52 +1,43 @@ //! Plain Old Data types for sigma proofs. -#[cfg(not(target_os = "solana"))] -use crate::sigma_proofs::{ - batched_grouped_ciphertext_validity::{ - BatchedGroupedCiphertext2HandlesValidityProof, - BatchedGroupedCiphertext3HandlesValidityProof, - }, - ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProof, - ciphertext_commitment_equality::CiphertextCommitmentEqualityProof, - grouped_ciphertext_validity::{ - GroupedCiphertext2HandlesValidityProof, GroupedCiphertext3HandlesValidityProof, - }, - percentage_with_cap::PercentageWithCapProof, - pubkey_validity::PubkeyValidityProof, - zero_ciphertext::ZeroCiphertextProof, -}; use { - crate::{ - pod::{impl_from_bytes, impl_from_str}, - sigma_proofs::{errors::*, *}, - }, + crate::macros::{impl_from_bytes, impl_from_str}, base64::{prelude::BASE64_STANDARD, Engine}, bytemuck::{Pod, Zeroable}, std::fmt, }; -/// The `CiphertextCommitmentEqualityProof` type as a `Pod`. -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct PodCiphertextCommitmentEqualityProof( - pub(crate) [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN], -); +/// Byte length of a ciphertext-commitment equality proof +pub const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN: usize = 192; -#[cfg(not(target_os = "solana"))] -impl From for PodCiphertextCommitmentEqualityProof { - fn from(decoded_proof: CiphertextCommitmentEqualityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} +/// Byte length of a ciphertext-ciphertext equality proof +pub const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN: usize = 224; -#[cfg(not(target_os = "solana"))] -impl TryFrom for CiphertextCommitmentEqualityProof { - type Error = EqualityProofVerificationError; +/// Byte length of a grouped ciphertext for 2 handles validity proof +pub const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160; - fn try_from(pod_proof: PodCiphertextCommitmentEqualityProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} +/// Byte length of a grouped ciphertext for 3 handles validity proof +pub const GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN: usize = 192; + +/// Byte length of a batched grouped ciphertext for 2 handles validity proof +pub const BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160; + +/// Byte length of a batched grouped ciphertext for 3 handles validity proof +pub const BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN: usize = 192; + +/// Byte length of a zero-ciphertext proof +pub const ZERO_CIPHERTEXT_PROOF_LEN: usize = 96; + +/// Byte length of a percentage with cap proof +pub const PERCENTAGE_WITH_CAP_PROOF_LEN: usize = 256; + +/// Byte length of a public key validity proof +pub const PUBKEY_VALIDITY_PROOF_LEN: usize = 64; + +/// The `CiphertextCommitmentEqualityProof` type as a `Pod`. +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct PodCiphertextCommitmentEqualityProof(pub [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN]); const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_MAX_BASE64_LEN: usize = 256; @@ -70,25 +61,7 @@ impl_from_bytes!( /// The `CiphertextCiphertextEqualityProof` type as a `Pod`. #[derive(Clone, Copy)] #[repr(transparent)] -pub struct PodCiphertextCiphertextEqualityProof( - pub(crate) [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN], -); - -#[cfg(not(target_os = "solana"))] -impl From for PodCiphertextCiphertextEqualityProof { - fn from(decoded_proof: CiphertextCiphertextEqualityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for CiphertextCiphertextEqualityProof { - type Error = EqualityProofVerificationError; - - fn try_from(pod_proof: PodCiphertextCiphertextEqualityProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} +pub struct PodCiphertextCiphertextEqualityProof(pub [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN]); const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_MAX_BASE64_LEN: usize = 300; @@ -113,25 +86,9 @@ impl_from_bytes!( #[derive(Clone, Copy)] #[repr(transparent)] pub struct PodGroupedCiphertext2HandlesValidityProof( - pub(crate) [u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN], + pub [u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN], ); -#[cfg(not(target_os = "solana"))] -impl From for PodGroupedCiphertext2HandlesValidityProof { - fn from(decoded_proof: GroupedCiphertext2HandlesValidityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for GroupedCiphertext2HandlesValidityProof { - type Error = ValidityProofVerificationError; - - fn try_from(pod_proof: PodGroupedCiphertext2HandlesValidityProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 216; impl fmt::Display for PodGroupedCiphertext2HandlesValidityProof { @@ -155,25 +112,9 @@ impl_from_bytes!( #[derive(Clone, Copy)] #[repr(transparent)] pub struct PodGroupedCiphertext3HandlesValidityProof( - pub(crate) [u8; GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN], + pub [u8; GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN], ); -#[cfg(not(target_os = "solana"))] -impl From for PodGroupedCiphertext3HandlesValidityProof { - fn from(decoded_proof: GroupedCiphertext3HandlesValidityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for GroupedCiphertext3HandlesValidityProof { - type Error = ValidityProofVerificationError; - - fn try_from(pod_proof: PodGroupedCiphertext3HandlesValidityProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - const GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 256; impl fmt::Display for PodGroupedCiphertext3HandlesValidityProof { @@ -197,31 +138,9 @@ impl_from_bytes!( #[derive(Clone, Copy)] #[repr(transparent)] pub struct PodBatchedGroupedCiphertext2HandlesValidityProof( - pub(crate) [u8; BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN], + pub [u8; BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN], ); -#[cfg(not(target_os = "solana"))] -impl From - for PodBatchedGroupedCiphertext2HandlesValidityProof -{ - fn from(decoded_proof: BatchedGroupedCiphertext2HandlesValidityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom - for BatchedGroupedCiphertext2HandlesValidityProof -{ - type Error = ValidityProofVerificationError; - - fn try_from( - pod_proof: PodBatchedGroupedCiphertext2HandlesValidityProof, - ) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - const BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 216; impl fmt::Display for PodBatchedGroupedCiphertext2HandlesValidityProof { @@ -245,31 +164,9 @@ impl_from_bytes!( #[derive(Clone, Copy)] #[repr(transparent)] pub struct PodBatchedGroupedCiphertext3HandlesValidityProof( - pub(crate) [u8; BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN], + pub [u8; BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN], ); -#[cfg(not(target_os = "solana"))] -impl From - for PodBatchedGroupedCiphertext3HandlesValidityProof -{ - fn from(decoded_proof: BatchedGroupedCiphertext3HandlesValidityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom - for BatchedGroupedCiphertext3HandlesValidityProof -{ - type Error = ValidityProofVerificationError; - - fn try_from( - pod_proof: PodBatchedGroupedCiphertext3HandlesValidityProof, - ) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - const BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 256; impl fmt::Display for PodBatchedGroupedCiphertext3HandlesValidityProof { @@ -292,23 +189,7 @@ impl_from_bytes!( /// The `ZeroCiphertextProof` type as a `Pod`. #[derive(Clone, Copy)] #[repr(transparent)] -pub struct PodZeroCiphertextProof(pub(crate) [u8; ZERO_CIPHERTEXT_PROOF_LEN]); - -#[cfg(not(target_os = "solana"))] -impl From for PodZeroCiphertextProof { - fn from(decoded_proof: ZeroCiphertextProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for ZeroCiphertextProof { - type Error = ZeroCiphertextProofVerificationError; - - fn try_from(pod_proof: PodZeroCiphertextProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} +pub struct PodZeroCiphertextProof(pub [u8; ZERO_CIPHERTEXT_PROOF_LEN]); const ZERO_CIPHERTEXT_PROOF_MAX_BASE64_LEN: usize = 128; @@ -332,23 +213,7 @@ impl_from_bytes!( /// The `PercentageWithCapProof` type as a `Pod`. #[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable)] #[repr(transparent)] -pub struct PodPercentageWithCapProof(pub(crate) [u8; PERCENTAGE_WITH_CAP_PROOF_LEN]); - -#[cfg(not(target_os = "solana"))] -impl From for PodPercentageWithCapProof { - fn from(decoded_proof: PercentageWithCapProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PercentageWithCapProof { - type Error = PercentageWithCapProofVerificationError; - - fn try_from(pod_proof: PodPercentageWithCapProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} +pub struct PodPercentageWithCapProof(pub [u8; PERCENTAGE_WITH_CAP_PROOF_LEN]); const PERCENTAGE_WITH_CAP_PROOF_MAX_BASE64_LEN: usize = 344; @@ -372,23 +237,7 @@ impl_from_bytes!( /// The `PubkeyValidityProof` type as a `Pod`. #[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable)] #[repr(transparent)] -pub struct PodPubkeyValidityProof(pub(crate) [u8; PUBKEY_VALIDITY_PROOF_LEN]); - -#[cfg(not(target_os = "solana"))] -impl From for PodPubkeyValidityProof { - fn from(decoded_proof: PubkeyValidityProof) -> Self { - Self(decoded_proof.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PubkeyValidityProof { - type Error = PubkeyValidityProofVerificationError; - - fn try_from(pod_proof: PodPubkeyValidityProof) -> Result { - Self::from_bytes(&pod_proof.0) - } -} +pub struct PodPubkeyValidityProof(pub [u8; PUBKEY_VALIDITY_PROOF_LEN]); const PUBKEY_VALIDITY_PROOF_MAX_BASE64_LEN: usize = 88; diff --git a/zk-sdk-wasm-js/Cargo.toml b/zk-sdk-wasm-js/Cargo.toml index cad7eeb..219a3cf 100644 --- a/zk-sdk-wasm-js/Cargo.toml +++ b/zk-sdk-wasm-js/Cargo.toml @@ -21,6 +21,7 @@ test-browser = [] [dependencies] solana-zk-sdk = { workspace = true } +solana-zk-sdk-pod = { workspace = true } bytemuck = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs b/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs index 966fda2..4c0b692 100644 --- a/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_2.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - batched_grouped_ciphertext_validity, ZkProofData, + BatchedGroupedCiphertext2HandlesValidityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::batched_grouped_ciphertext_validity, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs b/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs index ac14960..5b76486 100644 --- a/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk-wasm-js/src/proof_data/batched_grouped_ciphertext_validity/handles_3.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - batched_grouped_ciphertext_validity, ZkProofData, + BatchedGroupedCiphertext3HandlesValidityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::batched_grouped_ciphertext_validity, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/ciphertext_ciphertext_equality.rs b/zk-sdk-wasm-js/src/proof_data/ciphertext_ciphertext_equality.rs index 75d064b..9d818c0 100644 --- a/zk-sdk-wasm-js/src/proof_data/ciphertext_ciphertext_equality.rs +++ b/zk-sdk-wasm-js/src/proof_data/ciphertext_ciphertext_equality.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - ciphertext_ciphertext_equality, ZkProofData, + CiphertextCiphertextEqualityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::ciphertext_ciphertext_equality, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/ciphertext_commitment_equality.rs b/zk-sdk-wasm-js/src/proof_data/ciphertext_commitment_equality.rs index 03d9782..385f8fe 100644 --- a/zk-sdk-wasm-js/src/proof_data/ciphertext_commitment_equality.rs +++ b/zk-sdk-wasm-js/src/proof_data/ciphertext_commitment_equality.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - ciphertext_commitment_equality, ZkProofData, + CiphertextCommitmentEqualityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::ciphertext_commitment_equality, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_2.rs b/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_2.rs index af344aa..cf5462d 100644 --- a/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_2.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - grouped_ciphertext_validity, ZkProofData, + GroupedCiphertext2HandlesValidityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::grouped_ciphertext_validity, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_3.rs b/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_3.rs index 8fb0fc7..df7563b 100644 --- a/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk-wasm-js/src/proof_data/grouped_ciphertext_validity/handles_3.rs @@ -5,8 +5,9 @@ use { }, js_sys::Uint8Array, solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ - grouped_ciphertext_validity, ZkProofData, + GroupedCiphertext3HandlesValidityProofDataExt, ZkProofData, }, + solana_zk_sdk_pod::proof_data::grouped_ciphertext_validity, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/mod.rs b/zk-sdk-wasm-js/src/proof_data/mod.rs index acbfe65..c974db2 100644 --- a/zk-sdk-wasm-js/src/proof_data/mod.rs +++ b/zk-sdk-wasm-js/src/proof_data/mod.rs @@ -1,35 +1,35 @@ -pub mod batched_grouped_ciphertext_validity; -pub mod batched_range_proof; +// pub mod batched_grouped_ciphertext_validity; +// pub mod batched_range_proof; pub mod ciphertext_ciphertext_equality; pub mod ciphertext_commitment_equality; pub mod grouped_ciphertext_validity; pub mod percentage_with_cap; pub mod pubkey_validity; -pub mod zero_ciphertext; +// pub mod zero_ciphertext; -pub use { - batched_grouped_ciphertext_validity::{ - BatchedGroupedCiphertext2HandlesValidityProofContext, - BatchedGroupedCiphertext2HandlesValidityProofData, - BatchedGroupedCiphertext3HandlesValidityProofContext, - BatchedGroupedCiphertext3HandlesValidityProofData, - }, - batched_range_proof::{ - batched_range_proof_u128::BatchedRangeProofU128Data, - batched_range_proof_u256::BatchedRangeProofU256Data, - batched_range_proof_u64::BatchedRangeProofU64Data, BatchedRangeProofContext, - }, - ciphertext_ciphertext_equality::{ - CiphertextCiphertextEqualityProofContext, CiphertextCiphertextEqualityProofData, - }, - ciphertext_commitment_equality::{ - CiphertextCommitmentEqualityProofContext, CiphertextCommitmentEqualityProofData, - }, - grouped_ciphertext_validity::{ - GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, - GroupedCiphertext3HandlesValidityProofContext, GroupedCiphertext3HandlesValidityProofData, - }, - percentage_with_cap::{PercentageWithCapProofContext, PercentageWithCapProofData}, - pubkey_validity::{PubkeyValidityProofContext, PubkeyValidityProofData}, - zero_ciphertext::{ZeroCiphertextProofContext, ZeroCiphertextProofData}, -}; +// pub use { +// batched_grouped_ciphertext_validity::{ +// BatchedGroupedCiphertext2HandlesValidityProofContext, +// BatchedGroupedCiphertext2HandlesValidityProofData, +// BatchedGroupedCiphertext3HandlesValidityProofContext, +// BatchedGroupedCiphertext3HandlesValidityProofData, +// }, +// batched_range_proof::{ +// batched_range_proof_u128::BatchedRangeProofU128Data, +// batched_range_proof_u256::BatchedRangeProofU256Data, +// batched_range_proof_u64::BatchedRangeProofU64Data, BatchedRangeProofContext, +// }, +// ciphertext_ciphertext_equality::{ +// CiphertextCiphertextEqualityProofContext, CiphertextCiphertextEqualityProofData, +// }, +// ciphertext_commitment_equality::{ +// CiphertextCommitmentEqualityProofContext, CiphertextCommitmentEqualityProofData, +// }, +// grouped_ciphertext_validity::{ +// GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, +// GroupedCiphertext3HandlesValidityProofContext, GroupedCiphertext3HandlesValidityProofData, +// }, +// percentage_with_cap::{PercentageWithCapProofContext, PercentageWithCapProofData}, +// pubkey_validity::{PubkeyValidityProofContext, PubkeyValidityProofData}, +// zero_ciphertext::{ZeroCiphertextProofContext, ZeroCiphertextProofData}, +// }; diff --git a/zk-sdk-wasm-js/src/proof_data/percentage_with_cap.rs b/zk-sdk-wasm-js/src/proof_data/percentage_with_cap.rs index 1127f8a..629e249 100644 --- a/zk-sdk-wasm-js/src/proof_data/percentage_with_cap.rs +++ b/zk-sdk-wasm-js/src/proof_data/percentage_with_cap.rs @@ -1,7 +1,10 @@ use { crate::encryption::pedersen::{PedersenCommitment, PedersenOpening}, js_sys::Uint8Array, - solana_zk_sdk::zk_elgamal_proof_program::proof_data::{percentage_with_cap, ZkProofData}, + solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ + PercentageWithCapProofDataExt, ZkProofData, + }, + solana_zk_sdk_pod::proof_data::percentage_with_cap, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/pubkey_validity.rs b/zk-sdk-wasm-js/src/proof_data/pubkey_validity.rs index bfa6ba7..e5e6819 100644 --- a/zk-sdk-wasm-js/src/proof_data/pubkey_validity.rs +++ b/zk-sdk-wasm-js/src/proof_data/pubkey_validity.rs @@ -1,7 +1,10 @@ use { crate::encryption::elgamal::ElGamalKeypair, js_sys::Uint8Array, - solana_zk_sdk::zk_elgamal_proof_program::proof_data::{pubkey_validity, ZkProofData}, + solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ + PubkeyValidityProofDataExt, ZkProofData, + }, + solana_zk_sdk_pod::proof_data::pubkey_validity, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk-wasm-js/src/proof_data/zero_ciphertext.rs b/zk-sdk-wasm-js/src/proof_data/zero_ciphertext.rs index 1556c34..9a1fc0e 100644 --- a/zk-sdk-wasm-js/src/proof_data/zero_ciphertext.rs +++ b/zk-sdk-wasm-js/src/proof_data/zero_ciphertext.rs @@ -1,7 +1,10 @@ use { crate::encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair}, js_sys::Uint8Array, - solana_zk_sdk::zk_elgamal_proof_program::proof_data::{zero_ciphertext, ZkProofData}, + solana_zk_sdk::zk_elgamal_proof_program::proof_data::{ + ZeroCiphertextProofDataExt, ZkProofData, + }, + solana_zk_sdk_pod::proof_data::zero_ciphertext, wasm_bindgen::prelude::*, }; diff --git a/zk-sdk/Cargo.toml b/zk-sdk/Cargo.toml index 10d9a56..d98b9db 100644 --- a/zk-sdk/Cargo.toml +++ b/zk-sdk/Cargo.toml @@ -21,6 +21,7 @@ num-traits = { workspace = true } solana-instruction = { workspace = true, features = ["std"] } solana-pubkey = { workspace = true, features = ["bytemuck"] } solana-sdk-ids = { workspace = true } +solana-zk-sdk-pod = { workspace = true } thiserror = { workspace = true } [target.'cfg(not(target_os = "solana"))'.dependencies] diff --git a/zk-sdk/src/encryption/auth_encryption.rs b/zk-sdk/src/encryption/auth_encryption.rs index f409b01..5218979 100644 --- a/zk-sdk/src/encryption/auth_encryption.rs +++ b/zk-sdk/src/encryption/auth_encryption.rs @@ -20,6 +20,7 @@ use { solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, solana_signature::Signature, solana_signer::{EncodableKey, Signer, SignerError}, + solana_zk_sdk_pod::encryption::auth_encryption::PodAeCiphertext, std::{ convert::TryInto, error, fmt, @@ -279,11 +280,27 @@ impl fmt::Display for AeCiphertext { } } +#[cfg(not(target_os = "solana"))] +impl From for PodAeCiphertext { + fn from(decoded_ciphertext: AeCiphertext) -> Self { + Self(decoded_ciphertext.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for AeCiphertext { + type Error = AuthenticatedEncryptionError; + + fn try_from(pod_ciphertext: PodAeCiphertext) -> Result { + Self::from_bytes(&pod_ciphertext.0).ok_or(AuthenticatedEncryptionError::Deserialization) + } +} + #[cfg(test)] mod tests { use { super::*, solana_keypair::Keypair, solana_pubkey::Pubkey, - solana_signer::null_signer::NullSigner, + solana_signer::null_signer::NullSigner, std::str::FromStr, }; #[test] @@ -392,4 +409,15 @@ mod tests { assert_ne!(ciphertext1.to_bytes(), ciphertext2.to_bytes()); } + + #[test] + fn ae_ciphertext_fromstr() { + let ae_key = AeKey::new_rand(); + let expected_ae_ciphertext: PodAeCiphertext = ae_key.encrypt(0_u64).into(); + + let ae_ciphertext_base64_str = format!("{}", expected_ae_ciphertext); + let computed_ae_ciphertext = PodAeCiphertext::from_str(&ae_ciphertext_base64_str).unwrap(); + + assert_eq!(expected_ae_ciphertext, computed_ae_ciphertext); + } } diff --git a/zk-sdk/src/encryption/elgamal.rs b/zk-sdk/src/encryption/elgamal.rs index 93e0cc6..4ac7c3f 100644 --- a/zk-sdk/src/encryption/elgamal.rs +++ b/zk-sdk/src/encryption/elgamal.rs @@ -39,6 +39,9 @@ use { solana_seed_phrase::generate_seed_from_seed_phrase_and_passphrase, solana_signature::Signature, solana_signer::{EncodableKey, EncodableKeypair, Signer, SignerError}, + solana_zk_sdk_pod::encryption::elgamal::{ + PodDecryptHandle, PodElGamalCiphertext, PodElGamalPubkey, + }, std::{ convert::TryInto, error, fmt, @@ -442,6 +445,22 @@ impl From<&ElGamalPubkey> for [u8; ELGAMAL_PUBKEY_LEN] { } } +#[cfg(not(target_os = "solana"))] +impl From for PodElGamalPubkey { + fn from(decoded_pubkey: ElGamalPubkey) -> Self { + Self(decoded_pubkey.into()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for ElGamalPubkey { + type Error = ElGamalError; + + fn try_from(pod_pubkey: PodElGamalPubkey) -> Result { + Self::try_from(pod_pubkey.0.as_slice()) + } +} + /// Secret key for the ElGamal encryption scheme. /// /// Instances of ElGamal secret key are zeroized on drop. @@ -708,6 +727,22 @@ impl fmt::Display for ElGamalCiphertext { } } +#[cfg(not(target_os = "solana"))] +impl From for PodElGamalCiphertext { + fn from(decoded_ciphertext: ElGamalCiphertext) -> Self { + Self(decoded_ciphertext.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for ElGamalCiphertext { + type Error = ElGamalError; + + fn try_from(pod_ciphertext: PodElGamalCiphertext) -> Result { + Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) + } +} + impl<'b> Add<&'b ElGamalCiphertext> for &ElGamalCiphertext { type Output = ElGamalCiphertext; @@ -804,6 +839,22 @@ impl DecryptHandle { } } +#[cfg(not(target_os = "solana"))] +impl From for PodDecryptHandle { + fn from(decoded_handle: DecryptHandle) -> Self { + Self(decoded_handle.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for DecryptHandle { + type Error = ElGamalError; + + fn try_from(pod_handle: PodDecryptHandle) -> Result { + Self::from_bytes(&pod_handle.0).ok_or(ElGamalError::CiphertextDeserialization) + } +} + impl<'b> Add<&'b DecryptHandle> for &DecryptHandle { type Output = DecryptHandle; @@ -861,7 +912,10 @@ mod tests { solana_keypair::Keypair, solana_pubkey::Pubkey, solana_signer::null_signer::NullSigner, - std::fs::{self, File}, + std::{ + fs::{self, File}, + str::FromStr, + }, }; #[test] @@ -1234,4 +1288,29 @@ mod tests { Err(ElGamalError::SecretKeyDeserialization) )); } + + #[test] + fn elgamal_pubkey_fromstr() { + let elgamal_keypair = ElGamalKeypair::new_rand(); + let expected_elgamal_pubkey: PodElGamalPubkey = (*elgamal_keypair.pubkey()).into(); + + let elgamal_pubkey_base64_str = format!("{}", expected_elgamal_pubkey); + let computed_elgamal_pubkey = + PodElGamalPubkey::from_str(&elgamal_pubkey_base64_str).unwrap(); + + assert_eq!(expected_elgamal_pubkey, computed_elgamal_pubkey); + } + + #[test] + fn elgamal_ciphertext_fromstr() { + let elgamal_keypair = ElGamalKeypair::new_rand(); + let expected_elgamal_ciphertext: PodElGamalCiphertext = + elgamal_keypair.pubkey().encrypt(0_u64).into(); + + let elgamal_ciphertext_base64_str = format!("{}", expected_elgamal_ciphertext); + let computed_elgamal_ciphertext = + PodElGamalCiphertext::from_str(&elgamal_ciphertext_base64_str).unwrap(); + + assert_eq!(expected_elgamal_ciphertext, computed_elgamal_ciphertext); + } } diff --git a/zk-sdk/src/encryption/grouped_elgamal.rs b/zk-sdk/src/encryption/grouped_elgamal.rs index f4c2457..d0e93a2 100644 --- a/zk-sdk/src/encryption/grouped_elgamal.rs +++ b/zk-sdk/src/encryption/grouped_elgamal.rs @@ -19,9 +19,13 @@ use { elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey}, pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, }, + errors::ElGamalError, RISTRETTO_POINT_LEN, }, curve25519_dalek::scalar::Scalar, + solana_zk_sdk_pod::encryption::grouped_elgamal::{ + PodGroupedElGamalCiphertext2Handles, PodGroupedElGamalCiphertext3Handles, + }, thiserror::Error, }; @@ -217,9 +221,48 @@ impl GroupedElGamalCiphertext { } } +#[cfg(not(target_os = "solana"))] +impl From> for PodGroupedElGamalCiphertext2Handles { + fn from(decoded_ciphertext: GroupedElGamalCiphertext<2>) -> Self { + Self(decoded_ciphertext.to_bytes().try_into().unwrap()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for GroupedElGamalCiphertext<2> { + type Error = ElGamalError; + + fn try_from(pod_ciphertext: PodGroupedElGamalCiphertext2Handles) -> Result { + Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) + } +} + +#[cfg(not(target_os = "solana"))] +impl From> for PodGroupedElGamalCiphertext3Handles { + fn from(decoded_ciphertext: GroupedElGamalCiphertext<3>) -> Self { + Self(decoded_ciphertext.to_bytes().try_into().unwrap()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for GroupedElGamalCiphertext<3> { + type Error = ElGamalError; + + fn try_from(pod_ciphertext: PodGroupedElGamalCiphertext3Handles) -> Result { + Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) + } +} + #[cfg(test)] mod tests { - use {super::*, crate::encryption::elgamal::ElGamalKeypair}; + use { + super::*, + crate::encryption::elgamal::ElGamalKeypair, + solana_zk_sdk_pod::{ + encryption::{elgamal::PodElGamalCiphertext, pedersen::PodPedersenCommitment}, + errors::PodParseError, + }, + }; #[test] fn test_grouped_elgamal_encrypt_decrypt_correctness() { @@ -374,4 +417,84 @@ mod tests { bytes[32] = 1; assert!(GroupedElGamalCiphertext::<1>::from_bytes(&bytes).is_none()); } + + #[test] + fn test_2_handles_ciphertext_extraction() { + let elgamal_keypair_0 = ElGamalKeypair::new_rand(); + let elgamal_keypair_1 = ElGamalKeypair::new_rand(); + + let amount: u64 = 10; + let (commitment, opening) = Pedersen::new(amount); + + let grouped_ciphertext = GroupedElGamal::encrypt_with( + [elgamal_keypair_0.pubkey(), elgamal_keypair_1.pubkey()], + amount, + &opening, + ); + let pod_grouped_ciphertext: PodGroupedElGamalCiphertext2Handles = grouped_ciphertext.into(); + + let expected_pod_commitment: PodPedersenCommitment = commitment.into(); + let actual_pod_commitment = pod_grouped_ciphertext.extract_commitment(); + assert_eq!(expected_pod_commitment, actual_pod_commitment); + + let expected_ciphertext_0 = elgamal_keypair_0.pubkey().encrypt_with(amount, &opening); + let expected_pod_ciphertext_0: PodElGamalCiphertext = expected_ciphertext_0.into(); + let actual_pod_ciphertext_0 = pod_grouped_ciphertext.try_extract_ciphertext(0).unwrap(); + assert_eq!(expected_pod_ciphertext_0, actual_pod_ciphertext_0); + + let expected_ciphertext_1 = elgamal_keypair_1.pubkey().encrypt_with(amount, &opening); + let expected_pod_ciphertext_1: PodElGamalCiphertext = expected_ciphertext_1.into(); + let actual_pod_ciphertext_1 = pod_grouped_ciphertext.try_extract_ciphertext(1).unwrap(); + assert_eq!(expected_pod_ciphertext_1, actual_pod_ciphertext_1); + + let err = pod_grouped_ciphertext + .try_extract_ciphertext(2) + .unwrap_err(); + assert_eq!(err, PodParseError::GroupedCiphertextIndexOutOfBounds); + } + + #[test] + fn test_3_handles_ciphertext_extraction() { + let elgamal_keypair_0 = ElGamalKeypair::new_rand(); + let elgamal_keypair_1 = ElGamalKeypair::new_rand(); + let elgamal_keypair_2 = ElGamalKeypair::new_rand(); + + let amount: u64 = 10; + let (commitment, opening) = Pedersen::new(amount); + + let grouped_ciphertext = GroupedElGamal::encrypt_with( + [ + elgamal_keypair_0.pubkey(), + elgamal_keypair_1.pubkey(), + elgamal_keypair_2.pubkey(), + ], + amount, + &opening, + ); + let pod_grouped_ciphertext: PodGroupedElGamalCiphertext3Handles = grouped_ciphertext.into(); + + let expected_pod_commitment: PodPedersenCommitment = commitment.into(); + let actual_pod_commitment = pod_grouped_ciphertext.extract_commitment(); + assert_eq!(expected_pod_commitment, actual_pod_commitment); + + let expected_ciphertext_0 = elgamal_keypair_0.pubkey().encrypt_with(amount, &opening); + let expected_pod_ciphertext_0: PodElGamalCiphertext = expected_ciphertext_0.into(); + let actual_pod_ciphertext_0 = pod_grouped_ciphertext.try_extract_ciphertext(0).unwrap(); + assert_eq!(expected_pod_ciphertext_0, actual_pod_ciphertext_0); + + let expected_ciphertext_1 = elgamal_keypair_1.pubkey().encrypt_with(amount, &opening); + let expected_pod_ciphertext_1: PodElGamalCiphertext = expected_ciphertext_1.into(); + let actual_pod_ciphertext_1 = pod_grouped_ciphertext.try_extract_ciphertext(1).unwrap(); + assert_eq!(expected_pod_ciphertext_1, actual_pod_ciphertext_1); + + let expected_ciphertext_2 = elgamal_keypair_2.pubkey().encrypt_with(amount, &opening); + let expected_pod_ciphertext_2: PodElGamalCiphertext = expected_ciphertext_2.into(); + let actual_pod_ciphertext_2 = pod_grouped_ciphertext.try_extract_ciphertext(2).unwrap(); + assert_eq!(expected_pod_ciphertext_2, actual_pod_ciphertext_2); + + let err = pod_grouped_ciphertext + .try_extract_ciphertext(3) + .unwrap_err(); + assert_eq!(err, PodParseError::GroupedCiphertextIndexOutOfBounds); + } } diff --git a/zk-sdk/src/encryption/mod.rs b/zk-sdk/src/encryption/mod.rs index 8cad621..a8d0cd8 100644 --- a/zk-sdk/src/encryption/mod.rs +++ b/zk-sdk/src/encryption/mod.rs @@ -25,7 +25,6 @@ pub mod elgamal; pub mod grouped_elgamal; #[cfg(not(target_os = "solana"))] pub mod pedersen; -pub mod pod; /// Byte length of an authenticated encryption secret key pub const AE_KEY_LEN: usize = 16; diff --git a/zk-sdk/src/encryption/pedersen.rs b/zk-sdk/src/encryption/pedersen.rs index 6d52596..34e468a 100644 --- a/zk-sdk/src/encryption/pedersen.rs +++ b/zk-sdk/src/encryption/pedersen.rs @@ -1,7 +1,10 @@ //! Pedersen commitment implementation using the Ristretto prime-order group. use { - crate::encryption::{PEDERSEN_COMMITMENT_LEN, PEDERSEN_OPENING_LEN}, + crate::{ + encryption::{PEDERSEN_COMMITMENT_LEN, PEDERSEN_OPENING_LEN}, + errors::ElGamalError, + }, core::ops::{Add, Mul, Sub}, curve25519_dalek::{ constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT}, @@ -12,6 +15,7 @@ use { rand::rngs::OsRng, serde::{Deserialize, Serialize}, sha3::Sha3_512, + solana_zk_sdk_pod::encryption::pedersen::PodPedersenCommitment, std::{convert::TryInto, fmt}, subtle::{Choice, ConstantTimeEq}, zeroize::Zeroize, @@ -216,6 +220,22 @@ impl PedersenCommitment { } } +#[cfg(not(target_os = "solana"))] +impl From for PodPedersenCommitment { + fn from(decoded_commitment: PedersenCommitment) -> Self { + Self(decoded_commitment.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PedersenCommitment { + type Error = ElGamalError; + + fn try_from(pod_commitment: PodPedersenCommitment) -> Result { + Self::from_bytes(&pod_commitment.0).ok_or(ElGamalError::CiphertextDeserialization) + } +} + impl<'b> Add<&'b PedersenCommitment> for &PedersenCommitment { type Output = PedersenCommitment; diff --git a/zk-sdk/src/encryption/pod/elgamal.rs b/zk-sdk/src/encryption/pod/elgamal.rs deleted file mode 100644 index 6b30f27..0000000 --- a/zk-sdk/src/encryption/pod/elgamal.rs +++ /dev/null @@ -1,198 +0,0 @@ -//! Plain Old Data types for the ElGamal encryption scheme. - -#[cfg(not(target_os = "solana"))] -use { - crate::{ - encryption::elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey}, - errors::ElGamalError, - }, - 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; - -/// 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)] -pub struct PodElGamalCiphertext(pub(crate) [u8; ELGAMAL_CIPHERTEXT_LEN]); - -impl fmt::Debug for PodElGamalCiphertext { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl fmt::Display for PodElGamalCiphertext { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", BASE64_STANDARD.encode(self.0)) - } -} - -impl Default for PodElGamalCiphertext { - fn default() -> Self { - Self::zeroed() - } -} - -impl_from_str!( - TYPE = PodElGamalCiphertext, - BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN, - BASE64_LEN = ELGAMAL_CIPHERTEXT_MAX_BASE64_LEN -); - -impl_from_bytes!( - TYPE = PodElGamalCiphertext, - BYTES_LEN = ELGAMAL_CIPHERTEXT_LEN -); - -#[cfg(not(target_os = "solana"))] -impl From for PodElGamalCiphertext { - fn from(decoded_ciphertext: ElGamalCiphertext) -> Self { - Self(decoded_ciphertext.to_bytes()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for ElGamalCiphertext { - type Error = ElGamalError; - - fn try_from(pod_ciphertext: PodElGamalCiphertext) -> Result { - Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) - } -} - -/// The `ElGamalPubkey` type as a `Pod`. -#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] -#[repr(transparent)] -pub struct PodElGamalPubkey(pub(crate) [u8; ELGAMAL_PUBKEY_LEN]); - -impl fmt::Debug for PodElGamalPubkey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl fmt::Display for PodElGamalPubkey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", BASE64_STANDARD.encode(self.0)) - } -} - -impl_from_str!( - TYPE = PodElGamalPubkey, - BYTES_LEN = ELGAMAL_PUBKEY_LEN, - BASE64_LEN = ELGAMAL_PUBKEY_MAX_BASE64_LEN -); - -impl_from_bytes!(TYPE = PodElGamalPubkey, BYTES_LEN = ELGAMAL_PUBKEY_LEN); - -#[cfg(not(target_os = "solana"))] -impl From for PodElGamalPubkey { - fn from(decoded_pubkey: ElGamalPubkey) -> Self { - Self(decoded_pubkey.into()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for ElGamalPubkey { - type Error = ElGamalError; - - fn try_from(pod_pubkey: PodElGamalPubkey) -> Result { - Self::try_from(pod_pubkey.0.as_slice()) - } -} - -/// The `DecryptHandle` type as a `Pod`. -#[derive(Clone, Copy, Default, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] -#[repr(transparent)] -pub struct PodDecryptHandle(pub(crate) [u8; DECRYPT_HANDLE_LEN]); - -impl fmt::Debug for PodDecryptHandle { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -#[cfg(not(target_os = "solana"))] -impl From for PodDecryptHandle { - fn from(decoded_handle: DecryptHandle) -> Self { - Self(decoded_handle.to_bytes()) - } -} - -// For proof verification, interpret pod::DecryptHandle as CompressedRistretto -#[cfg(not(target_os = "solana"))] -impl From for CompressedRistretto { - fn from(pod_handle: PodDecryptHandle) -> Self { - Self(pod_handle.0) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for DecryptHandle { - type Error = ElGamalError; - - fn try_from(pod_handle: PodDecryptHandle) -> Result { - Self::from_bytes(&pod_handle.0).ok_or(ElGamalError::CiphertextDeserialization) - } -} - -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}; - - #[test] - fn elgamal_pubkey_fromstr() { - let elgamal_keypair = ElGamalKeypair::new_rand(); - let expected_elgamal_pubkey: PodElGamalPubkey = (*elgamal_keypair.pubkey()).into(); - - let elgamal_pubkey_base64_str = format!("{}", expected_elgamal_pubkey); - let computed_elgamal_pubkey = - PodElGamalPubkey::from_str(&elgamal_pubkey_base64_str).unwrap(); - - assert_eq!(expected_elgamal_pubkey, computed_elgamal_pubkey); - } - - #[test] - fn elgamal_ciphertext_fromstr() { - let elgamal_keypair = ElGamalKeypair::new_rand(); - let expected_elgamal_ciphertext: PodElGamalCiphertext = - elgamal_keypair.pubkey().encrypt(0_u64).into(); - - let elgamal_ciphertext_base64_str = format!("{}", expected_elgamal_ciphertext); - let computed_elgamal_ciphertext = - PodElGamalCiphertext::from_str(&elgamal_ciphertext_base64_str).unwrap(); - - assert_eq!(expected_elgamal_ciphertext, computed_elgamal_ciphertext); - } -} diff --git a/zk-sdk/src/encryption/pod/grouped_elgamal.rs b/zk-sdk/src/encryption/pod/grouped_elgamal.rs deleted file mode 100644 index 3a6dc48..0000000 --- a/zk-sdk/src/encryption/pod/grouped_elgamal.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Plain Old Data types for the Grouped ElGamal encryption scheme. - -#[cfg(not(target_os = "solana"))] -use crate::encryption::grouped_elgamal::GroupedElGamalCiphertext; -use { - crate::{ - encryption::{ - 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, - std::fmt, -}; - -/// Maximum length of a base64 encoded grouped ElGamal ciphertext with 2 handles -const GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_MAX_BASE64_LEN: usize = 132; - -/// Maximum length of a base64 encoded grouped ElGamal ciphertext with 3 handles -const GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_MAX_BASE64_LEN: usize = 176; - -macro_rules! impl_extract { - (TYPE = $type:ident) => { - impl $type { - /// Extract the commitment component from a grouped ciphertext - pub fn extract_commitment(&self) -> PodPedersenCommitment { - // `GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES` guaranteed to be at least `PEDERSEN_COMMITMENT_LEN` - let commitment = self.0[..PEDERSEN_COMMITMENT_LEN].try_into().unwrap(); - PodPedersenCommitment(commitment) - } - - /// Extract a regular ElGamal ciphertext using the decrypt handle at a specified index. - pub fn try_extract_ciphertext( - &self, - index: usize, - ) -> Result { - let mut ciphertext_bytes = [0u8; ELGAMAL_CIPHERTEXT_LEN]; - ciphertext_bytes[..PEDERSEN_COMMITMENT_LEN] - .copy_from_slice(&self.0[..PEDERSEN_COMMITMENT_LEN]); - - let handle_start = DECRYPT_HANDLE_LEN - .checked_mul(index) - .and_then(|n| n.checked_add(PEDERSEN_COMMITMENT_LEN)) - .ok_or(ElGamalError::CiphertextDeserialization)?; - let handle_end = handle_start - .checked_add(DECRYPT_HANDLE_LEN) - .ok_or(ElGamalError::CiphertextDeserialization)?; - ciphertext_bytes[PEDERSEN_COMMITMENT_LEN..].copy_from_slice( - self.0 - .get(handle_start..handle_end) - .ok_or(ElGamalError::CiphertextDeserialization)?, - ); - - Ok(PodElGamalCiphertext(ciphertext_bytes)) - } - } - }; -} - -/// Byte length of a grouped ElGamal ciphertext with 2 handles -const GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES: usize = - PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN; - -/// Byte length of a grouped ElGamal ciphertext with 3 handles -const GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES: usize = - PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN; - -/// The `GroupedElGamalCiphertext` type with two decryption handles as a `Pod` -#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] -#[repr(transparent)] -pub struct PodGroupedElGamalCiphertext2Handles( - pub(crate) [u8; GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES], -); - -impl fmt::Debug for PodGroupedElGamalCiphertext2Handles { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Default for PodGroupedElGamalCiphertext2Handles { - fn default() -> Self { - Self::zeroed() - } -} - -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, - BASE64_LEN = GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES_MAX_BASE64_LEN -); - -impl_from_bytes!( - TYPE = PodGroupedElGamalCiphertext2Handles, - BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES -); - -#[cfg(not(target_os = "solana"))] -impl From> for PodGroupedElGamalCiphertext2Handles { - fn from(decoded_ciphertext: GroupedElGamalCiphertext<2>) -> Self { - Self(decoded_ciphertext.to_bytes().try_into().unwrap()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for GroupedElGamalCiphertext<2> { - type Error = ElGamalError; - - fn try_from(pod_ciphertext: PodGroupedElGamalCiphertext2Handles) -> Result { - Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) - } -} - -impl_extract!(TYPE = PodGroupedElGamalCiphertext2Handles); - -/// The `GroupedElGamalCiphertext` type with three decryption handles as a `Pod` -#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable, PartialEq, Eq)] -#[repr(transparent)] -pub struct PodGroupedElGamalCiphertext3Handles( - pub(crate) [u8; GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES], -); - -impl fmt::Debug for PodGroupedElGamalCiphertext3Handles { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -impl Default for PodGroupedElGamalCiphertext3Handles { - fn default() -> Self { - Self::zeroed() - } -} - -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, - BASE64_LEN = GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES_MAX_BASE64_LEN -); - -impl_from_bytes!( - TYPE = PodGroupedElGamalCiphertext3Handles, - BYTES_LEN = GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES -); - -#[cfg(not(target_os = "solana"))] -impl From> for PodGroupedElGamalCiphertext3Handles { - fn from(decoded_ciphertext: GroupedElGamalCiphertext<3>) -> Self { - Self(decoded_ciphertext.to_bytes().try_into().unwrap()) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for GroupedElGamalCiphertext<3> { - type Error = ElGamalError; - - fn try_from(pod_ciphertext: PodGroupedElGamalCiphertext3Handles) -> Result { - Self::from_bytes(&pod_ciphertext.0).ok_or(ElGamalError::CiphertextDeserialization) - } -} - -impl_extract!(TYPE = PodGroupedElGamalCiphertext3Handles); - -#[cfg(test)] -mod tests { - use { - super::*, - crate::encryption::{ - elgamal::ElGamalKeypair, grouped_elgamal::GroupedElGamal, pedersen::Pedersen, - pod::pedersen::PodPedersenCommitment, - }, - }; - - #[test] - fn test_2_handles_ciphertext_extraction() { - let elgamal_keypair_0 = ElGamalKeypair::new_rand(); - let elgamal_keypair_1 = ElGamalKeypair::new_rand(); - - let amount: u64 = 10; - let (commitment, opening) = Pedersen::new(amount); - - let grouped_ciphertext = GroupedElGamal::encrypt_with( - [elgamal_keypair_0.pubkey(), elgamal_keypair_1.pubkey()], - amount, - &opening, - ); - let pod_grouped_ciphertext: PodGroupedElGamalCiphertext2Handles = grouped_ciphertext.into(); - - let expected_pod_commitment: PodPedersenCommitment = commitment.into(); - let actual_pod_commitment = pod_grouped_ciphertext.extract_commitment(); - assert_eq!(expected_pod_commitment, actual_pod_commitment); - - let expected_ciphertext_0 = elgamal_keypair_0.pubkey().encrypt_with(amount, &opening); - let expected_pod_ciphertext_0: PodElGamalCiphertext = expected_ciphertext_0.into(); - let actual_pod_ciphertext_0 = pod_grouped_ciphertext.try_extract_ciphertext(0).unwrap(); - assert_eq!(expected_pod_ciphertext_0, actual_pod_ciphertext_0); - - let expected_ciphertext_1 = elgamal_keypair_1.pubkey().encrypt_with(amount, &opening); - let expected_pod_ciphertext_1: PodElGamalCiphertext = expected_ciphertext_1.into(); - let actual_pod_ciphertext_1 = pod_grouped_ciphertext.try_extract_ciphertext(1).unwrap(); - assert_eq!(expected_pod_ciphertext_1, actual_pod_ciphertext_1); - - let err = pod_grouped_ciphertext - .try_extract_ciphertext(2) - .unwrap_err(); - assert_eq!(err, ElGamalError::CiphertextDeserialization); - } - - #[test] - fn test_3_handles_ciphertext_extraction() { - let elgamal_keypair_0 = ElGamalKeypair::new_rand(); - let elgamal_keypair_1 = ElGamalKeypair::new_rand(); - let elgamal_keypair_2 = ElGamalKeypair::new_rand(); - - let amount: u64 = 10; - let (commitment, opening) = Pedersen::new(amount); - - let grouped_ciphertext = GroupedElGamal::encrypt_with( - [ - elgamal_keypair_0.pubkey(), - elgamal_keypair_1.pubkey(), - elgamal_keypair_2.pubkey(), - ], - amount, - &opening, - ); - let pod_grouped_ciphertext: PodGroupedElGamalCiphertext3Handles = grouped_ciphertext.into(); - - let expected_pod_commitment: PodPedersenCommitment = commitment.into(); - let actual_pod_commitment = pod_grouped_ciphertext.extract_commitment(); - assert_eq!(expected_pod_commitment, actual_pod_commitment); - - let expected_ciphertext_0 = elgamal_keypair_0.pubkey().encrypt_with(amount, &opening); - let expected_pod_ciphertext_0: PodElGamalCiphertext = expected_ciphertext_0.into(); - let actual_pod_ciphertext_0 = pod_grouped_ciphertext.try_extract_ciphertext(0).unwrap(); - assert_eq!(expected_pod_ciphertext_0, actual_pod_ciphertext_0); - - let expected_ciphertext_1 = elgamal_keypair_1.pubkey().encrypt_with(amount, &opening); - let expected_pod_ciphertext_1: PodElGamalCiphertext = expected_ciphertext_1.into(); - let actual_pod_ciphertext_1 = pod_grouped_ciphertext.try_extract_ciphertext(1).unwrap(); - assert_eq!(expected_pod_ciphertext_1, actual_pod_ciphertext_1); - - let expected_ciphertext_2 = elgamal_keypair_2.pubkey().encrypt_with(amount, &opening); - let expected_pod_ciphertext_2: PodElGamalCiphertext = expected_ciphertext_2.into(); - let actual_pod_ciphertext_2 = pod_grouped_ciphertext.try_extract_ciphertext(2).unwrap(); - assert_eq!(expected_pod_ciphertext_2, actual_pod_ciphertext_2); - - let err = pod_grouped_ciphertext - .try_extract_ciphertext(3) - .unwrap_err(); - assert_eq!(err, ElGamalError::CiphertextDeserialization); - } -} diff --git a/zk-sdk/src/encryption/pod/mod.rs b/zk-sdk/src/encryption/pod/mod.rs deleted file mode 100644 index 90305a4..0000000 --- a/zk-sdk/src/encryption/pod/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod auth_encryption; -pub mod elgamal; -pub mod grouped_elgamal; -pub mod pedersen; diff --git a/zk-sdk/src/encryption/pod/pedersen.rs b/zk-sdk/src/encryption/pod/pedersen.rs deleted file mode 100644 index 96d2154..0000000 --- a/zk-sdk/src/encryption/pod/pedersen.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Plain Old Data type for the Pedersen commitment scheme. - -#[cfg(not(target_os = "solana"))] -use { - crate::{encryption::pedersen::PedersenCommitment, errors::ElGamalError}, - curve25519_dalek::ristretto::CompressedRistretto, -}; -use { - crate::{ - encryption::PEDERSEN_COMMITMENT_LEN, - pod::{impl_from_bytes, impl_from_str}, - }, - base64::{prelude::BASE64_STANDARD, Engine}, - bytemuck_derive::{Pod, Zeroable}, - std::fmt, -}; - -/// Maximum length of a base64 encoded ElGamal public key -const PEDERSEN_COMMITMENT_MAX_BASE64_LEN: usize = 44; - -/// The `PedersenCommitment` type as a `Pod`. -#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)] -#[repr(transparent)] -pub struct PodPedersenCommitment(pub(crate) [u8; PEDERSEN_COMMITMENT_LEN]); - -impl fmt::Debug for PodPedersenCommitment { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.0) - } -} - -#[cfg(not(target_os = "solana"))] -impl From for PodPedersenCommitment { - fn from(decoded_commitment: PedersenCommitment) -> Self { - Self(decoded_commitment.to_bytes()) - } -} - -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, - BASE64_LEN = PEDERSEN_COMMITMENT_MAX_BASE64_LEN -); - -impl_from_bytes!( - TYPE = PodPedersenCommitment, - BYTES_LEN = PEDERSEN_COMMITMENT_LEN -); - -// For proof verification, interpret pod::PedersenCommitment directly as CompressedRistretto -#[cfg(not(target_os = "solana"))] -impl From for CompressedRistretto { - fn from(pod_commitment: PodPedersenCommitment) -> Self { - Self(pod_commitment.0) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PedersenCommitment { - type Error = ElGamalError; - - fn try_from(pod_commitment: PodPedersenCommitment) -> Result { - Self::from_bytes(&pod_commitment.0).ok_or(ElGamalError::CiphertextDeserialization) - } -} diff --git a/zk-sdk/src/lib.rs b/zk-sdk/src/lib.rs index 36e14d6..10762a5 100644 --- a/zk-sdk/src/lib.rs +++ b/zk-sdk/src/lib.rs @@ -21,7 +21,6 @@ pub mod encryption; pub mod errors; -pub mod pod; mod range_proof; mod sigma_proofs; #[cfg(not(target_os = "solana"))] diff --git a/zk-sdk/src/pod.rs b/zk-sdk/src/pod.rs index d939e47..2240b5c 100644 --- a/zk-sdk/src/pod.rs +++ b/zk-sdk/src/pod.rs @@ -27,38 +27,3 @@ impl From 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 { - 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; diff --git a/zk-sdk/src/range_proof/mod.rs b/zk-sdk/src/range_proof/mod.rs index c31eafb..d83e1e4 100644 --- a/zk-sdk/src/range_proof/mod.rs +++ b/zk-sdk/src/range_proof/mod.rs @@ -14,7 +14,6 @@ #![allow(dead_code)] -use crate::{RISTRETTO_POINT_LEN, SCALAR_LEN}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -25,6 +24,7 @@ use { inner_product::InnerProductProof, }, transcript::TranscriptProtocol, + UNIT_LEN, }, core::iter, curve25519_dalek::traits::MultiscalarMul, @@ -35,13 +35,12 @@ use { }, merlin::Transcript, rand::rngs::OsRng, + solana_zk_sdk_pod::range_proof::{PodRangeProofU128, PodRangeProofU256, PodRangeProofU64}, subtle::{Choice, ConditionallySelectable}, zeroize::Zeroize, }; pub mod errors; -pub mod pod; - #[cfg(not(target_os = "solana"))] pub mod generators; #[cfg(not(target_os = "solana"))] @@ -49,30 +48,12 @@ pub mod inner_product; #[cfg(not(target_os = "solana"))] pub mod util; -/// Byte length of a range proof excluding the inner-product proof component -pub const RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN: usize = - 5 * RISTRETTO_POINT_LEN + 2 * SCALAR_LEN; - -/// Byte length of an inner-product proof for a vector of length 64 -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; // 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; // 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; // 800 bytes +#[cfg(not(target_os = "solana"))] +pub use solana_zk_sdk_pod::range_proof::{ + INNER_PRODUCT_PROOF_U128_LEN, INNER_PRODUCT_PROOF_U256_LEN, INNER_PRODUCT_PROOF_U64_LEN, + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN, RANGE_PROOF_U128_LEN, RANGE_PROOF_U256_LEN, + RANGE_PROOF_U64_LEN, +}; /// A Bulletproofs range proof. #[allow(non_snake_case)] @@ -491,6 +472,102 @@ impl RangeProof { } } +#[cfg(not(target_os = "solana"))] +impl TryFrom for RangeProof { + type Error = RangeProofVerificationError; + + fn try_from(pod_proof: PodRangeProofU64) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PodRangeProofU64 { + type Error = RangeProofVerificationError; + + fn try_from(decoded_proof: RangeProof) -> Result { + if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U64_LEN { + return Err(RangeProofVerificationError::Deserialization); + } + + let mut buf = [0_u8; RANGE_PROOF_U64_LEN]; + copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); + buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U64_LEN] + .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); + Ok(PodRangeProofU64(buf)) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for RangeProof { + type Error = RangeProofVerificationError; + + fn try_from(pod_proof: PodRangeProofU128) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PodRangeProofU128 { + type Error = RangeProofVerificationError; + + fn try_from(decoded_proof: RangeProof) -> Result { + if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U128_LEN { + return Err(RangeProofVerificationError::Deserialization); + } + + let mut buf = [0_u8; RANGE_PROOF_U128_LEN]; + copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); + buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U128_LEN] + .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); + Ok(PodRangeProofU128(buf)) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for RangeProof { + type Error = RangeProofVerificationError; + + fn try_from(pod_proof: PodRangeProofU256) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PodRangeProofU256 { + type Error = RangeProofVerificationError; + + fn try_from(decoded_proof: RangeProof) -> Result { + if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U256_LEN { + return Err(RangeProofVerificationError::Deserialization); + } + + let mut buf = [0_u8; RANGE_PROOF_U256_LEN]; + copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); + buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U256_LEN] + .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); + Ok(PodRangeProofU256(buf)) + } +} + +#[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); + chunks.next().unwrap().copy_from_slice(proof.A.as_bytes()); + chunks.next().unwrap().copy_from_slice(proof.S.as_bytes()); + chunks.next().unwrap().copy_from_slice(proof.T_1.as_bytes()); + chunks.next().unwrap().copy_from_slice(proof.T_2.as_bytes()); + chunks.next().unwrap().copy_from_slice(proof.t_x.as_bytes()); + chunks + .next() + .unwrap() + .copy_from_slice(proof.t_x_blinding.as_bytes()); + chunks + .next() + .unwrap() + .copy_from_slice(proof.e_blinding.as_bytes()); +} + /// Computes the `delta(y,z)` term for the verification equation. /// /// This term is a function of the challenges `y` and `z` and the proof dimensions. @@ -514,11 +591,7 @@ fn delta(bit_lengths: &[usize], y: &Scalar, z: &Scalar) -> Scalar { #[cfg(test)] mod tests { use { - super::*, - crate::{ - encryption::pod::pedersen::PodPedersenCommitment, range_proof::pod::PodRangeProofU128, - }, - std::str::FromStr, + super::*, solana_zk_sdk_pod::encryption::pedersen::PodPedersenCommitment, std::str::FromStr, }; #[test] diff --git a/zk-sdk/src/range_proof/pod.rs b/zk-sdk/src/range_proof/pod.rs deleted file mode 100644 index 2860408..0000000 --- a/zk-sdk/src/range_proof/pod.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! Plain Old Data types for range proofs. - -#[cfg(not(target_os = "solana"))] -use crate::{ - range_proof::{errors::RangeProofVerificationError, RangeProof}, - UNIT_LEN, -}; -use { - 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. -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct PodRangeProofU64(pub(crate) [u8; RANGE_PROOF_U64_LEN]); - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PodRangeProofU64 { - type Error = RangeProofVerificationError; - - fn try_from(decoded_proof: RangeProof) -> Result { - if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U64_LEN { - return Err(RangeProofVerificationError::Deserialization); - } - - let mut buf = [0_u8; RANGE_PROOF_U64_LEN]; - copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); - buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U64_LEN] - .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); - Ok(PodRangeProofU64(buf)) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for RangeProof { - type Error = RangeProofVerificationError; - - fn try_from(pod_proof: PodRangeProofU64) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - -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)] -pub struct PodRangeProofU128(pub(crate) [u8; RANGE_PROOF_U128_LEN]); - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PodRangeProofU128 { - type Error = RangeProofVerificationError; - - fn try_from(decoded_proof: RangeProof) -> Result { - if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U128_LEN { - return Err(RangeProofVerificationError::Deserialization); - } - - let mut buf = [0_u8; RANGE_PROOF_U128_LEN]; - copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); - buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U128_LEN] - .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); - Ok(PodRangeProofU128(buf)) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for RangeProof { - type Error = RangeProofVerificationError; - - fn try_from(pod_proof: PodRangeProofU128) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - -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)] -pub struct PodRangeProofU256(pub(crate) [u8; RANGE_PROOF_U256_LEN]); - -#[cfg(not(target_os = "solana"))] -impl TryFrom for PodRangeProofU256 { - type Error = RangeProofVerificationError; - - fn try_from(decoded_proof: RangeProof) -> Result { - if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U256_LEN { - return Err(RangeProofVerificationError::Deserialization); - } - - let mut buf = [0_u8; RANGE_PROOF_U256_LEN]; - copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf); - buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U256_LEN] - .copy_from_slice(&decoded_proof.ipp_proof.to_bytes()); - Ok(PodRangeProofU256(buf)) - } -} - -#[cfg(not(target_os = "solana"))] -impl TryFrom for RangeProof { - type Error = RangeProofVerificationError; - - fn try_from(pod_proof: PodRangeProofU256) -> Result { - Self::from_bytes(&pod_proof.0) - } -} - -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); - chunks.next().unwrap().copy_from_slice(proof.A.as_bytes()); - chunks.next().unwrap().copy_from_slice(proof.S.as_bytes()); - chunks.next().unwrap().copy_from_slice(proof.T_1.as_bytes()); - chunks.next().unwrap().copy_from_slice(proof.T_2.as_bytes()); - chunks.next().unwrap().copy_from_slice(proof.t_x.as_bytes()); - chunks - .next() - .unwrap() - .copy_from_slice(proof.t_x_blinding.as_bytes()); - chunks - .next() - .unwrap() - .copy_from_slice(proof.e_blinding.as_bytes()); -} - -// The range 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 range proof pod types. -unsafe impl Zeroable for PodRangeProofU64 {} -unsafe impl Pod for PodRangeProofU64 {} - -unsafe impl Zeroable for PodRangeProofU128 {} -unsafe impl Pod for PodRangeProofU128 {} - -unsafe impl Zeroable for PodRangeProofU256 {} -unsafe impl Pod for PodRangeProofU256 {} diff --git a/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_2.rs b/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_2.rs index 2121ddd..849b4b7 100644 --- a/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_2.rs @@ -23,6 +23,7 @@ use { elgamal::{DecryptHandle, ElGamalPubkey}, pedersen::{PedersenCommitment, PedersenOpening}, }, + solana_zk_sdk_pod::sigma_proofs::PodBatchedGroupedCiphertext2HandlesValidityProof, zeroize::Zeroize, }; use { @@ -142,20 +143,36 @@ impl BatchedGroupedCiphertext2HandlesValidityProof { } } +#[cfg(not(target_os = "solana"))] +impl From + for PodBatchedGroupedCiphertext2HandlesValidityProof +{ + fn from(decoded_proof: BatchedGroupedCiphertext2HandlesValidityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom + for BatchedGroupedCiphertext2HandlesValidityProof +{ + type Error = ValidityProofVerificationError; + + fn try_from( + pod_proof: PodBatchedGroupedCiphertext2HandlesValidityProof, + ) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::ElGamalKeypair, - pedersen::Pedersen, - pod::{ - elgamal::{PodDecryptHandle, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - }, - sigma_proofs::pod::PodBatchedGroupedCiphertext2HandlesValidityProof, + crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen}, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodDecryptHandle, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, }, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_3.rs b/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_3.rs index 9a4252a..8cf4b4e 100644 --- a/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity/handles_3.rs @@ -27,6 +27,7 @@ use { elgamal::{DecryptHandle, ElGamalPubkey}, pedersen::{PedersenCommitment, PedersenOpening}, }, + solana_zk_sdk_pod::sigma_proofs::PodBatchedGroupedCiphertext3HandlesValidityProof, zeroize::Zeroize, }; use { @@ -153,20 +154,36 @@ impl BatchedGroupedCiphertext3HandlesValidityProof { } } +#[cfg(not(target_os = "solana"))] +impl From + for PodBatchedGroupedCiphertext3HandlesValidityProof +{ + fn from(decoded_proof: BatchedGroupedCiphertext3HandlesValidityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom + for BatchedGroupedCiphertext3HandlesValidityProof +{ + type Error = ValidityProofVerificationError; + + fn try_from( + pod_proof: PodBatchedGroupedCiphertext3HandlesValidityProof, + ) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::ElGamalKeypair, - pedersen::Pedersen, - pod::{ - elgamal::{PodDecryptHandle, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - }, - sigma_proofs::pod::PodBatchedGroupedCiphertext3HandlesValidityProof, + crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen}, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodDecryptHandle, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, }, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/ciphertext_ciphertext_equality.rs b/zk-sdk/src/sigma_proofs/ciphertext_ciphertext_equality.rs index 96e3e29..d8358b1 100644 --- a/zk-sdk/src/sigma_proofs/ciphertext_ciphertext_equality.rs +++ b/zk-sdk/src/sigma_proofs/ciphertext_ciphertext_equality.rs @@ -15,6 +15,7 @@ use { }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodCiphertextCiphertextEqualityProof, zeroize::Zeroize, }; use { @@ -274,14 +275,27 @@ impl CiphertextCiphertextEqualityProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodCiphertextCiphertextEqualityProof { + fn from(decoded_proof: CiphertextCiphertextEqualityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for CiphertextCiphertextEqualityProof { + type Error = EqualityProofVerificationError; + + fn try_from(pod_proof: PodCiphertextCiphertextEqualityProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - sigma_proofs::pod::PodCiphertextCiphertextEqualityProof, - }, + solana_zk_sdk_pod::encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/ciphertext_commitment_equality.rs b/zk-sdk/src/sigma_proofs/ciphertext_commitment_equality.rs index b9bab3a..69f4fc8 100644 --- a/zk-sdk/src/sigma_proofs/ciphertext_commitment_equality.rs +++ b/zk-sdk/src/sigma_proofs/ciphertext_commitment_equality.rs @@ -20,6 +20,7 @@ use { }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodCiphertextCommitmentEqualityProof, zeroize::Zeroize, }; use { @@ -250,20 +251,30 @@ impl CiphertextCommitmentEqualityProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodCiphertextCommitmentEqualityProof { + fn from(decoded_proof: CiphertextCommitmentEqualityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for CiphertextCommitmentEqualityProof { + type Error = EqualityProofVerificationError; + + fn try_from(pod_proof: PodCiphertextCommitmentEqualityProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::ElGamalSecretKey, - pedersen::Pedersen, - pod::{ - elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - }, - sigma_proofs::pod::PodCiphertextCommitmentEqualityProof, + crate::encryption::{elgamal::ElGamalSecretKey, pedersen::Pedersen}, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, }, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs b/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs index df429aa..fcc55fa 100644 --- a/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_2.rs @@ -20,6 +20,7 @@ use { }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodGroupedCiphertext2HandlesValidityProof, zeroize::Zeroize, }; use { @@ -243,20 +244,30 @@ impl GroupedCiphertext2HandlesValidityProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodGroupedCiphertext2HandlesValidityProof { + fn from(decoded_proof: GroupedCiphertext2HandlesValidityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for GroupedCiphertext2HandlesValidityProof { + type Error = ValidityProofVerificationError; + + fn try_from(pod_proof: PodGroupedCiphertext2HandlesValidityProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::ElGamalKeypair, - pedersen::Pedersen, - pod::{ - elgamal::{PodDecryptHandle, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - }, - sigma_proofs::pod::PodGroupedCiphertext2HandlesValidityProof, + crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen}, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodDecryptHandle, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, }, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_3.rs b/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_3.rs index 7ad2512..c93c302 100644 --- a/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk/src/sigma_proofs/grouped_ciphertext_validity/handles_3.rs @@ -20,6 +20,7 @@ use { }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodGroupedCiphertext3HandlesValidityProof, zeroize::Zeroize, }; use { @@ -274,20 +275,30 @@ impl GroupedCiphertext3HandlesValidityProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodGroupedCiphertext3HandlesValidityProof { + fn from(decoded_proof: GroupedCiphertext3HandlesValidityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for GroupedCiphertext3HandlesValidityProof { + type Error = ValidityProofVerificationError; + + fn try_from(pod_proof: PodGroupedCiphertext3HandlesValidityProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::ElGamalKeypair, - pedersen::Pedersen, - pod::{ - elgamal::{PodDecryptHandle, PodElGamalCiphertext, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - }, - sigma_proofs::pod::PodGroupedCiphertext3HandlesValidityProof, + crate::encryption::{elgamal::ElGamalKeypair, pedersen::Pedersen}, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodDecryptHandle, PodElGamalCiphertext, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, }, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/mod.rs b/zk-sdk/src/sigma_proofs/mod.rs index c8b847a..abc2263 100644 --- a/zk-sdk/src/sigma_proofs/mod.rs +++ b/zk-sdk/src/sigma_proofs/mod.rs @@ -8,7 +8,6 @@ #![allow(dead_code, unused_imports)] pub mod errors; -pub mod pod; #[cfg(not(target_os = "solana"))] pub mod batched_grouped_ciphertext_validity; @@ -25,36 +24,20 @@ pub mod pubkey_validity; #[cfg(not(target_os = "solana"))] pub mod zero_ciphertext; -/// Byte length of a ciphertext-commitment equality proof -pub const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN: usize = 192; - -/// Byte length of a ciphertext-ciphertext equality proof -pub const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN: usize = 224; - -/// Byte length of a grouped ciphertext for 2 handles validity proof -pub const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160; - -/// Byte length of a grouped ciphertext for 3 handles validity proof -pub const GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN: usize = 192; - -/// Byte length of a batched grouped ciphertext for 2 handles validity proof -pub const BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160; - -/// Byte length of a batched grouped ciphertext for 3 handles validity proof -pub const BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN: usize = 192; - -/// Byte length of a zero-ciphertext proof -pub const ZERO_CIPHERTEXT_PROOF_LEN: usize = 96; - -/// Byte length of a percentage with cap proof -pub const PERCENTAGE_WITH_CAP_PROOF_LEN: usize = 256; - -/// Byte length of a public key validity proof -pub const PUBKEY_VALIDITY_PROOF_LEN: usize = 64; - +pub use solana_zk_sdk_pod::sigma_proofs::{ + BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN, + BATCHED_GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN, + CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN, CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN, + GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN, + GROUPED_CIPHERTEXT_3_HANDLES_VALIDITY_PROOF_LEN, PERCENTAGE_WITH_CAP_PROOF_LEN, + PUBKEY_VALIDITY_PROOF_LEN, ZERO_CIPHERTEXT_PROOF_LEN, +}; #[cfg(not(target_os = "solana"))] use { - crate::{sigma_proofs::errors::SigmaProofVerificationError, RISTRETTO_POINT_LEN, SCALAR_LEN}, + crate::{ + sigma_proofs::errors::SigmaProofVerificationError, RISTRETTO_POINT_LEN, SCALAR_LEN, + UNIT_LEN, + }, curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar}, }; diff --git a/zk-sdk/src/sigma_proofs/percentage_with_cap.rs b/zk-sdk/src/sigma_proofs/percentage_with_cap.rs index aa75401..3c47469 100644 --- a/zk-sdk/src/sigma_proofs/percentage_with_cap.rs +++ b/zk-sdk/src/sigma_proofs/percentage_with_cap.rs @@ -26,6 +26,7 @@ use { UNIT_LEN, }, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodPercentageWithCapProof, zeroize::Zeroize, }; use { @@ -608,15 +609,27 @@ fn conditional_select_ristretto( CompressedRistretto(bytes) } +#[cfg(not(target_os = "solana"))] +impl From for PodPercentageWithCapProof { + fn from(decoded_proof: PercentageWithCapProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PercentageWithCapProof { + type Error = PercentageWithCapProofVerificationError; + + fn try_from(pod_proof: PodPercentageWithCapProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { - super::*, - crate::{ - encryption::{pedersen::Pedersen, pod::pedersen::PodPedersenCommitment}, - sigma_proofs::pod::PodPercentageWithCapProof, - }, - std::str::FromStr, + super::*, crate::encryption::pedersen::Pedersen, + solana_zk_sdk_pod::encryption::pedersen::PodPedersenCommitment, std::str::FromStr, }; #[test] diff --git a/zk-sdk/src/sigma_proofs/pubkey_validity.rs b/zk-sdk/src/sigma_proofs/pubkey_validity.rs index 948204e..76d940f 100644 --- a/zk-sdk/src/sigma_proofs/pubkey_validity.rs +++ b/zk-sdk/src/sigma_proofs/pubkey_validity.rs @@ -14,6 +14,7 @@ use { UNIT_LEN, }, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodPubkeyValidityProof, zeroize::Zeroize, }; use { @@ -143,17 +144,27 @@ impl PubkeyValidityProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodPubkeyValidityProof { + fn from(decoded_proof: PubkeyValidityProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for PubkeyValidityProof { + type Error = PubkeyValidityProofVerificationError; + + fn try_from(pod_proof: PodPubkeyValidityProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { - super::*, - crate::{ - encryption::pod::elgamal::PodElGamalPubkey, sigma_proofs::pod::PodPubkeyValidityProof, - }, - bytemuck::Zeroable, - curve25519_dalek::traits::Identity, - solana_keypair::Keypair, - solana_pubkey::Pubkey, + super::*, bytemuck::Zeroable, curve25519_dalek::traits::Identity, solana_keypair::Keypair, + solana_pubkey::Pubkey, solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, std::str::FromStr, }; diff --git a/zk-sdk/src/sigma_proofs/zero_ciphertext.rs b/zk-sdk/src/sigma_proofs/zero_ciphertext.rs index 1932459..a45dd1e 100644 --- a/zk-sdk/src/sigma_proofs/zero_ciphertext.rs +++ b/zk-sdk/src/sigma_proofs/zero_ciphertext.rs @@ -15,6 +15,7 @@ use { }, curve25519_dalek::traits::MultiscalarMul, rand::rngs::OsRng, + solana_zk_sdk_pod::sigma_proofs::PodZeroCiphertextProof, zeroize::Zeroize, }; use { @@ -177,18 +178,31 @@ impl ZeroCiphertextProof { } } +#[cfg(not(target_os = "solana"))] +impl From for PodZeroCiphertextProof { + fn from(decoded_proof: ZeroCiphertextProof) -> Self { + Self(decoded_proof.to_bytes()) + } +} + +#[cfg(not(target_os = "solana"))] +impl TryFrom for ZeroCiphertextProof { + type Error = ZeroCiphertextProofVerificationError; + + fn try_from(pod_proof: PodZeroCiphertextProof) -> Result { + Self::from_bytes(&pod_proof.0) + } +} + #[cfg(test)] mod test { use { super::*, - crate::{ - encryption::{ - elgamal::{DecryptHandle, ElGamalKeypair}, - pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, - pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - }, - sigma_proofs::pod::PodZeroCiphertextProof, + crate::encryption::{ + elgamal::{DecryptHandle, ElGamalKeypair}, + pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, }, + solana_zk_sdk_pod::encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, std::str::FromStr, }; diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_2.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_2.rs index e648a96..03a26ea 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_2.rs @@ -6,14 +6,11 @@ //! grouped-ciphertext validity proofs. use { - crate::{ - encryption::pod::{ - elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext2Handles, - }, - sigma_proofs::pod::PodBatchedGroupedCiphertext2HandlesValidityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::batched_grouped_ciphertext_validity::{ + BatchedGroupedCiphertext2HandlesValidityProofContext, + BatchedGroupedCiphertext2HandlesValidityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -23,41 +20,39 @@ use { pedersen::PedersenOpening, }, sigma_proofs::batched_grouped_ciphertext_validity::BatchedGroupedCiphertext2HandlesValidityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, }; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyBatchedGroupedCiphertextValidity` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedGroupedCiphertext2HandlesValidityProofData { - pub context: BatchedGroupedCiphertext2HandlesValidityProofContext, - - pub proof: PodBatchedGroupedCiphertext2HandlesValidityProof, -} - -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedGroupedCiphertext2HandlesValidityProofContext { - pub first_pubkey: PodElGamalPubkey, // 32 bytes - - pub second_pubkey: PodElGamalPubkey, // 32 bytes - - pub grouped_ciphertext_lo: PodGroupedElGamalCiphertext2Handles, // 96 bytes - - pub grouped_ciphertext_hi: PodGroupedElGamalCiphertext2Handles, // 96 bytes +#[cfg(not(target_os = "solana"))] +pub trait BatchedGroupedCiphertext2HandlesValidityProofDataExt { + #[allow(clippy::too_many_arguments)] + fn new( + first_pubkey: &ElGamalPubkey, + second_pubkey: &ElGamalPubkey, + grouped_ciphertext_lo: &GroupedElGamalCiphertext<2>, + grouped_ciphertext_hi: &GroupedElGamalCiphertext<2>, + amount_lo: u64, + amount_hi: u64, + opening_lo: &PedersenOpening, + opening_hi: &PedersenOpening, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl BatchedGroupedCiphertext2HandlesValidityProofData { +impl BatchedGroupedCiphertext2HandlesValidityProofDataExt + for BatchedGroupedCiphertext2HandlesValidityProofData +{ #[allow(clippy::too_many_arguments)] - pub fn new( + fn new( first_pubkey: &ElGamalPubkey, second_pubkey: &ElGamalPubkey, grouped_ciphertext_lo: &GroupedElGamalCiphertext<2>, @@ -141,7 +136,7 @@ impl ZkProofData } #[cfg(not(target_os = "solana"))] -impl BatchedGroupedCiphertext2HandlesValidityProofContext { +impl ProofContext for BatchedGroupedCiphertext2HandlesValidityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"batched-grouped-ciphertext-validity-2-handles-instruction"); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_3.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_3.rs index aee6e6f..39e4288 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/handles_3.rs @@ -6,14 +6,11 @@ //! grouped-ciphertext validity proofs. use { - crate::{ - encryption::pod::{ - elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext3Handles, - }, - sigma_proofs::pod::PodBatchedGroupedCiphertext3HandlesValidityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::batched_grouped_ciphertext_validity::{ + BatchedGroupedCiphertext3HandlesValidityProofContext, + BatchedGroupedCiphertext3HandlesValidityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -23,43 +20,40 @@ use { pedersen::PedersenOpening, }, sigma_proofs::batched_grouped_ciphertext_validity::BatchedGroupedCiphertext3HandlesValidityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, }; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedGroupedCiphertext3HandlesValidityProofData { - pub context: BatchedGroupedCiphertext3HandlesValidityProofContext, - - pub proof: PodBatchedGroupedCiphertext3HandlesValidityProof, -} - -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedGroupedCiphertext3HandlesValidityProofContext { - pub first_pubkey: PodElGamalPubkey, // 32 bytes - - pub second_pubkey: PodElGamalPubkey, // 32 bytes - - pub third_pubkey: PodElGamalPubkey, // 32 bytes - - pub grouped_ciphertext_lo: PodGroupedElGamalCiphertext3Handles, // 128 bytes - - pub grouped_ciphertext_hi: PodGroupedElGamalCiphertext3Handles, // 128 bytes +#[cfg(not(target_os = "solana"))] +pub trait BatchedGroupedCiphertext3HandlesValidityProofDataExt { + #[allow(clippy::too_many_arguments)] + fn new( + first_pubkey: &ElGamalPubkey, + second_pubkey: &ElGamalPubkey, + third_pubkey: &ElGamalPubkey, + grouped_ciphertext_lo: &GroupedElGamalCiphertext<3>, + grouped_ciphertext_hi: &GroupedElGamalCiphertext<3>, + amount_lo: u64, + amount_hi: u64, + opening_lo: &PedersenOpening, + opening_hi: &PedersenOpening, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl BatchedGroupedCiphertext3HandlesValidityProofData { +impl BatchedGroupedCiphertext3HandlesValidityProofDataExt + for BatchedGroupedCiphertext3HandlesValidityProofData +{ #[allow(clippy::too_many_arguments)] - pub fn new( + fn new( first_pubkey: &ElGamalPubkey, second_pubkey: &ElGamalPubkey, third_pubkey: &ElGamalPubkey, @@ -153,7 +147,7 @@ impl ZkProofData } #[cfg(not(target_os = "solana"))] -impl BatchedGroupedCiphertext3HandlesValidityProofContext { +impl ProofContext for BatchedGroupedCiphertext3HandlesValidityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"batched-grouped-ciphertext-validity-3-handles-instruction"); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/mod.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/mod.rs index 70d2a7e..4b88952 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/mod.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_grouped_ciphertext_validity/mod.rs @@ -1,13 +1,8 @@ mod handles_2; mod handles_3; +#[cfg(not(target_os = "solana"))] pub use { - handles_2::{ - BatchedGroupedCiphertext2HandlesValidityProofContext, - BatchedGroupedCiphertext2HandlesValidityProofData, - }, - handles_3::{ - BatchedGroupedCiphertext3HandlesValidityProofContext, - BatchedGroupedCiphertext3HandlesValidityProofData, - }, + handles_2::BatchedGroupedCiphertext2HandlesValidityProofDataExt, + handles_3::BatchedGroupedCiphertext3HandlesValidityProofDataExt, }; diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u128.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u128.rs index df292c1..7e0403f 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u128.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u128.rs @@ -1,5 +1,11 @@ //! The 128-bit batched range proof instruction. +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::batched_range_proof::{ + BatchedRangeProofContext, BatchedRangeProofU128Data, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -7,39 +13,31 @@ use { range_proof::RangeProof, zk_elgamal_proof_program::{ errors::{ProofGenerationError, ProofVerificationError}, - proof_data::batched_range_proof::MAX_COMMITMENTS, + proof_data::{ + batched_range_proof::{BatchedRangeProofContextExt, MAX_COMMITMENTS}, + ProofContext, + }, }, }, + solana_zk_sdk_pod::range_proof::PodRangeProofU128, std::convert::TryInto, }; -use { - crate::{ - range_proof::pod::PodRangeProofU128, - zk_elgamal_proof_program::proof_data::{ - batched_range_proof::BatchedRangeProofContext, ProofType, ZkProofData, - }, - }, - bytemuck_derive::{Pod, Zeroable}, -}; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyBatchedRangeProofU128` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedRangeProofU128Data { - /// The context data for a batched range proof - pub context: BatchedRangeProofContext, - - /// The batched range proof - pub proof: PodRangeProofU128, +#[cfg(not(target_os = "solana"))] +pub trait BatchedRangeProofU128DataExt { + fn new( + commitments: Vec<&PedersenCommitment>, + amounts: Vec, + bit_lengths: Vec, + openings: Vec<&PedersenOpening>, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl BatchedRangeProofU128Data { - pub fn new( +impl BatchedRangeProofU128DataExt for BatchedRangeProofU128Data { + fn new( commitments: Vec<&PedersenCommitment>, amounts: Vec, bit_lengths: Vec, @@ -81,7 +79,7 @@ impl ZkProofData for BatchedRangeProofU128Data { #[cfg(not(target_os = "solana"))] fn verify_proof(&self) -> Result<(), ProofVerificationError> { - let (commitments, bit_lengths) = self.context.try_into()?; + let (commitments, bit_lengths) = self.context.try_into_commitment_bit_lengths_vec()?; let num_commitments = commitments.len(); if num_commitments > MAX_COMMITMENTS { diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u256.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u256.rs index 42ace83..dc1dedf 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u256.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u256.rs @@ -1,5 +1,11 @@ //! The 256-bit batched range proof instruction. +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::batched_range_proof::{ + BatchedRangeProofContext, BatchedRangeProofU256Data, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -7,42 +13,35 @@ use { range_proof::RangeProof, zk_elgamal_proof_program::{ errors::{ProofGenerationError, ProofVerificationError}, - proof_data::batched_range_proof::{MAX_COMMITMENTS, MAX_SINGLE_BIT_LENGTH}, + proof_data::{ + batched_range_proof::{ + BatchedRangeProofContextExt, MAX_COMMITMENTS, MAX_SINGLE_BIT_LENGTH, + }, + ProofContext, + }, }, }, std::convert::TryInto, }; -use { - crate::{ - range_proof::pod::PodRangeProofU256, - zk_elgamal_proof_program::proof_data::{ - batched_range_proof::BatchedRangeProofContext, ProofType, ZkProofData, - }, - }, - bytemuck_derive::{Pod, Zeroable}, -}; #[cfg(not(target_os = "solana"))] const BATCHED_RANGE_PROOF_U256_BIT_LENGTH: usize = 256; -/// The instruction data that is needed for the -/// `ProofInstruction::BatchedRangeProofU256Data` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedRangeProofU256Data { - /// The context data for a batched range proof - pub context: BatchedRangeProofContext, - - /// The batched range proof - pub proof: PodRangeProofU256, +#[cfg(not(target_os = "solana"))] +pub trait BatchedRangeProofU256DataExt { + fn new( + commitments: Vec<&PedersenCommitment>, + amounts: Vec, + bit_lengths: Vec, + openings: Vec<&PedersenOpening>, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl BatchedRangeProofU256Data { - pub fn new( +impl BatchedRangeProofU256DataExt for BatchedRangeProofU256Data { + fn new( commitments: Vec<&PedersenCommitment>, amounts: Vec, bit_lengths: Vec, @@ -88,7 +87,7 @@ impl ZkProofData for BatchedRangeProofU256Data { #[cfg(not(target_os = "solana"))] fn verify_proof(&self) -> Result<(), ProofVerificationError> { - let (commitments, bit_lengths) = self.context.try_into()?; + let (commitments, bit_lengths) = self.context.try_into_commitment_bit_lengths_vec()?; let num_commitments = commitments.len(); // This check is unique to the 256-bit proof. For 64-bit and 128-bit proofs, diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u64.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u64.rs index 3104fcc..252e845 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u64.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/batched_range_proof_u64.rs @@ -1,5 +1,11 @@ //! The 64-bit batched range proof instruction. +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::batched_range_proof::{ + BatchedRangeProofContext, BatchedRangeProofU64Data, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ @@ -7,39 +13,30 @@ use { range_proof::RangeProof, zk_elgamal_proof_program::{ errors::{ProofGenerationError, ProofVerificationError}, - proof_data::batched_range_proof::MAX_COMMITMENTS, + proof_data::{ + batched_range_proof::{BatchedRangeProofContextExt, MAX_COMMITMENTS}, + ProofContext, + }, }, }, std::convert::TryInto, }; -use { - crate::{ - range_proof::pod::PodRangeProofU64, - zk_elgamal_proof_program::proof_data::{ - batched_range_proof::BatchedRangeProofContext, ProofType, ZkProofData, - }, - }, - bytemuck_derive::{Pod, Zeroable}, -}; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyBatchedRangeProofU64` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct BatchedRangeProofU64Data { - /// The context data for a batched range proof - pub context: BatchedRangeProofContext, - - /// The batched range proof - pub proof: PodRangeProofU64, +#[cfg(not(target_os = "solana"))] +pub trait BatchedRangeProofU64DataExt { + fn new( + commitments: Vec<&PedersenCommitment>, + amounts: Vec, + bit_lengths: Vec, + openings: Vec<&PedersenOpening>, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl BatchedRangeProofU64Data { - pub fn new( +impl BatchedRangeProofU64DataExt for BatchedRangeProofU64Data { + fn new( commitments: Vec<&PedersenCommitment>, amounts: Vec, bit_lengths: Vec, @@ -80,7 +77,7 @@ impl ZkProofData for BatchedRangeProofU64Data { #[cfg(not(target_os = "solana"))] fn verify_proof(&self) -> Result<(), ProofVerificationError> { - let (commitments, bit_lengths) = self.context.try_into()?; + let (commitments, bit_lengths) = self.context.try_into_commitment_bit_lengths_vec()?; let num_commitments = commitments.len(); if num_commitments > MAX_COMMITMENTS { diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/mod.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/mod.rs index a003a21..0af68b4 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/mod.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/batched_range_proof/mod.rs @@ -21,20 +21,27 @@ pub mod batched_range_proof_u128; pub mod batched_range_proof_u256; pub mod batched_range_proof_u64; -use crate::encryption::pod::pedersen::PodPedersenCommitment; #[cfg(not(target_os = "solana"))] use { crate::{ encryption::pedersen::{PedersenCommitment, PedersenOpening}, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::{bytes_of, Zeroable}, curve25519_dalek::traits::IsIdentity, merlin::Transcript, + solana_zk_sdk_pod::{ + encryption::pedersen::PodPedersenCommitment, + proof_data::batched_range_proof::BatchedRangeProofContext, + }, std::convert::TryInto, }; /// The maximum number of Pedersen commitments that can be processed in a single batched range proof. +#[cfg(not(target_os = "solana"))] const MAX_COMMITMENTS: usize = 8; /// A bit length in a batched range proof must be at most 128. @@ -44,28 +51,25 @@ const MAX_COMMITMENTS: usize = 8; #[cfg(not(target_os = "solana"))] const MAX_SINGLE_BIT_LENGTH: usize = 128; -/// The context data needed to verify a range-proof for a Pedersen committed value. -/// -/// This struct holds the public information that a batched range proof certifies. It includes the -/// Pedersen commitments and their corresponding bit lengths. This context is shared by all -/// `VerifyBatchedRangeProof{N}` instructions. -#[derive(Clone, Copy, bytemuck_derive::Pod, bytemuck_derive::Zeroable)] -#[repr(C)] -pub struct BatchedRangeProofContext { - pub commitments: [PodPedersenCommitment; MAX_COMMITMENTS], - pub bit_lengths: [u8; MAX_COMMITMENTS], +#[cfg(not(target_os = "solana"))] +pub trait BatchedRangeProofContextExt { + fn new( + commitments: &[&PedersenCommitment], + amounts: &[u64], + bit_lengths: &[usize], + openings: &[&PedersenOpening], + ) -> Result + where + Self: Sized; + + fn try_into_commitment_bit_lengths_vec( + &self, + ) -> Result<(Vec, Vec), ProofVerificationError>; } #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl BatchedRangeProofContext { - fn new_transcript(&self) -> Transcript { - let mut transcript = Transcript::new(b"batched-range-proof-instruction"); - transcript.append_message(b"commitments", bytes_of(&self.commitments)); - transcript.append_message(b"bit-lengths", bytes_of(&self.bit_lengths)); - transcript - } - +impl BatchedRangeProofContextExt for BatchedRangeProofContext { fn new( commitments: &[&PedersenCommitment], amounts: &[u64], @@ -106,13 +110,10 @@ impl BatchedRangeProofContext { bit_lengths: pod_bit_lengths, }) } -} -#[cfg(not(target_os = "solana"))] -impl TryInto<(Vec, Vec)> for BatchedRangeProofContext { - type Error = ProofVerificationError; - - fn try_into(self) -> Result<(Vec, Vec), Self::Error> { + fn try_into_commitment_bit_lengths_vec( + &self, + ) -> Result<(Vec, Vec), ProofVerificationError> { let commitments = self .commitments .into_iter() @@ -131,3 +132,13 @@ impl TryInto<(Vec, Vec)> for BatchedRangeProofContext Ok((commitments, bit_lengths)) } } + +#[cfg(not(target_os = "solana"))] +impl ProofContext for BatchedRangeProofContext { + fn new_transcript(&self) -> Transcript { + let mut transcript = Transcript::new(b"batched-range-proof-instruction"); + transcript.append_message(b"commitments", bytes_of(&self.commitments)); + transcript.append_message(b"bit-lengths", bytes_of(&self.bit_lengths)); + transcript + } +} diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_ciphertext_equality.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_ciphertext_equality.rs index 21437bd..0d29efd 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_ciphertext_equality.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_ciphertext_equality.rs @@ -6,12 +6,10 @@ //! used to generate the second ciphertext. use { - crate::{ - encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - sigma_proofs::pod::PodCiphertextCiphertextEqualityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::ciphertext_ciphertext_equality::{ + CiphertextCiphertextEqualityProofContext, CiphertextCiphertextEqualityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -21,42 +19,34 @@ use { pedersen::PedersenOpening, }, sigma_proofs::ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, std::convert::TryInto, }; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyCiphertextCiphertextEquality` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct CiphertextCiphertextEqualityProofData { - pub context: CiphertextCiphertextEqualityProofContext, - - pub proof: PodCiphertextCiphertextEqualityProof, -} - -/// The context data needed to verify a ciphertext-ciphertext equality proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct CiphertextCiphertextEqualityProofContext { - pub first_pubkey: PodElGamalPubkey, // 32 bytes - - pub second_pubkey: PodElGamalPubkey, // 32 bytes - - pub first_ciphertext: PodElGamalCiphertext, // 64 bytes - - pub second_ciphertext: PodElGamalCiphertext, // 64 bytes +#[cfg(not(target_os = "solana"))] +pub trait CiphertextCiphertextEqualityProofDataExt { + fn new( + first_keypair: &ElGamalKeypair, + second_pubkey: &ElGamalPubkey, + first_ciphertext: &ElGamalCiphertext, + second_ciphertext: &ElGamalCiphertext, + second_opening: &PedersenOpening, + amount: u64, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl CiphertextCiphertextEqualityProofData { - pub fn new( +impl CiphertextCiphertextEqualityProofDataExt for CiphertextCiphertextEqualityProofData { + fn new( first_keypair: &ElGamalKeypair, second_pubkey: &ElGamalPubkey, first_ciphertext: &ElGamalCiphertext, @@ -125,7 +115,7 @@ impl ZkProofData #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl CiphertextCiphertextEqualityProofContext { +impl ProofContext for CiphertextCiphertextEqualityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"ciphertext-ciphertext-equality-instruction"); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_commitment_equality.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_commitment_equality.rs index bb09343..2bf6493 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_commitment_equality.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/ciphertext_commitment_equality.rs @@ -6,15 +6,10 @@ //! key for the first ciphertext and the Pedersen opening for the commitment. use { - crate::{ - encryption::pod::{ - elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - pedersen::PodPedersenCommitment, - }, - sigma_proofs::pod::PodCiphertextCommitmentEqualityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::ciphertext_commitment_equality::{ + CiphertextCommitmentEqualityProofContext, CiphertextCommitmentEqualityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -24,41 +19,36 @@ use { pedersen::{PedersenCommitment, PedersenOpening}, }, sigma_proofs::ciphertext_commitment_equality::CiphertextCommitmentEqualityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::{ + elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + pedersen::PodPedersenCommitment, + }, std::convert::TryInto, }; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct CiphertextCommitmentEqualityProofData { - pub context: CiphertextCommitmentEqualityProofContext, - pub proof: PodCiphertextCommitmentEqualityProof, -} - -/// The context data needed to verify a ciphertext-commitment equality proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct CiphertextCommitmentEqualityProofContext { - /// The ElGamal pubkey - pub pubkey: PodElGamalPubkey, // 32 bytes - - /// The ciphertext encrypted under the ElGamal pubkey - pub ciphertext: PodElGamalCiphertext, // 64 bytes - /// The Pedersen commitment - pub commitment: PodPedersenCommitment, // 32 bytes +#[cfg(not(target_os = "solana"))] +pub trait CiphertextCommitmentEqualityProofDataExt { + fn new( + keypair: &ElGamalKeypair, + ciphertext: &ElGamalCiphertext, + commitment: &PedersenCommitment, + opening: &PedersenOpening, + amount: u64, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl CiphertextCommitmentEqualityProofData { - pub fn new( +impl CiphertextCommitmentEqualityProofDataExt for CiphertextCommitmentEqualityProofData { + fn new( keypair: &ElGamalKeypair, ciphertext: &ElGamalCiphertext, commitment: &PedersenCommitment, @@ -111,7 +101,7 @@ impl ZkProofData #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl CiphertextCommitmentEqualityProofContext { +impl ProofContext for CiphertextCommitmentEqualityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"ciphertext-commitment-equality-instruction"); transcript.append_message(b"pubkey", bytes_of(&self.pubkey)); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs index df0668b..c54a84d 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_2.rs @@ -6,14 +6,10 @@ //! associated with the grouped ciphertext's commitment. use { - crate::{ - encryption::pod::{ - elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext2Handles, - }, - sigma_proofs::pod::PodGroupedCiphertext2HandlesValidityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::grouped_ciphertext_validity::{ + GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -23,38 +19,32 @@ use { pedersen::PedersenOpening, }, sigma_proofs::grouped_ciphertext_validity::GroupedCiphertext2HandlesValidityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, }; -/// The instruction data that is needed for the `ProofInstruction::VerifyGroupedCiphertextValidity` -/// instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct GroupedCiphertext2HandlesValidityProofData { - pub context: GroupedCiphertext2HandlesValidityProofContext, - - pub proof: PodGroupedCiphertext2HandlesValidityProof, -} - -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct GroupedCiphertext2HandlesValidityProofContext { - pub first_pubkey: PodElGamalPubkey, // 32 bytes - - pub second_pubkey: PodElGamalPubkey, // 32 bytes - - pub grouped_ciphertext: PodGroupedElGamalCiphertext2Handles, // 96 bytes +#[cfg(not(target_os = "solana"))] +pub trait GroupedCiphertext2HandlesValidityProofDataExt { + fn new( + first_pubkey: &ElGamalPubkey, + second_pubkey: &ElGamalPubkey, + grouped_ciphertext: &GroupedElGamalCiphertext<2>, + amount: u64, + opening: &PedersenOpening, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl GroupedCiphertext2HandlesValidityProofData { - pub fn new( +impl GroupedCiphertext2HandlesValidityProofDataExt for GroupedCiphertext2HandlesValidityProofData { + fn new( first_pubkey: &ElGamalPubkey, second_pubkey: &ElGamalPubkey, grouped_ciphertext: &GroupedElGamalCiphertext<2>, @@ -123,7 +113,7 @@ impl ZkProofData } #[cfg(not(target_os = "solana"))] -impl GroupedCiphertext2HandlesValidityProofContext { +impl ProofContext for GroupedCiphertext2HandlesValidityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"grouped-ciphertext-validity-2-handles-instruction"); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_3.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_3.rs index 1ceaa17..f636a48 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_3.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/handles_3.rs @@ -6,14 +6,10 @@ //! associated with the grouped ciphertext's commitment. use { - crate::{ - encryption::pod::{ - elgamal::PodElGamalPubkey, grouped_elgamal::PodGroupedElGamalCiphertext3Handles, - }, - sigma_proofs::pod::PodGroupedCiphertext3HandlesValidityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::grouped_ciphertext_validity::{ + GroupedCiphertext3HandlesValidityProofContext, GroupedCiphertext3HandlesValidityProofData, }, - bytemuck_derive::{Pod, Zeroable}, }; #[cfg(not(target_os = "solana"))] use { @@ -23,40 +19,33 @@ use { pedersen::PedersenOpening, }, sigma_proofs::grouped_ciphertext_validity::GroupedCiphertext3HandlesValidityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, }; -/// The instruction data that is needed for the -/// `ProofInstruction::VerifyGroupedCiphertext3HandlesValidity` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct GroupedCiphertext3HandlesValidityProofData { - pub context: GroupedCiphertext3HandlesValidityProofContext, - - pub proof: PodGroupedCiphertext3HandlesValidityProof, -} - -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct GroupedCiphertext3HandlesValidityProofContext { - pub first_pubkey: PodElGamalPubkey, // 32 bytes - - pub second_pubkey: PodElGamalPubkey, // 32 bytes - - pub third_pubkey: PodElGamalPubkey, // 32 bytes - - pub grouped_ciphertext: PodGroupedElGamalCiphertext3Handles, // 128 bytes +#[cfg(not(target_os = "solana"))] +pub trait GroupedCiphertext3HandlesValidityProofDataExt { + fn new( + first_pubkey: &ElGamalPubkey, + second_pubkey: &ElGamalPubkey, + third_pubkey: &ElGamalPubkey, + grouped_ciphertext: &GroupedElGamalCiphertext<3>, + amount: u64, + opening: &PedersenOpening, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl GroupedCiphertext3HandlesValidityProofData { - pub fn new( +impl GroupedCiphertext3HandlesValidityProofDataExt for GroupedCiphertext3HandlesValidityProofData { + fn new( first_pubkey: &ElGamalPubkey, second_pubkey: &ElGamalPubkey, third_pubkey: &ElGamalPubkey, @@ -133,7 +122,7 @@ impl ZkProofData } #[cfg(not(target_os = "solana"))] -impl GroupedCiphertext3HandlesValidityProofContext { +impl ProofContext for GroupedCiphertext3HandlesValidityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"grouped-ciphertext-validity-3-handles-instruction"); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/mod.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/mod.rs index 7f1149b..98230e1 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/mod.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/grouped_ciphertext_validity/mod.rs @@ -1,11 +1,8 @@ mod handles_2; mod handles_3; +#[cfg(not(target_os = "solana"))] pub use { - handles_2::{ - GroupedCiphertext2HandlesValidityProofContext, GroupedCiphertext2HandlesValidityProofData, - }, - handles_3::{ - GroupedCiphertext3HandlesValidityProofContext, GroupedCiphertext3HandlesValidityProofData, - }, + handles_2::GroupedCiphertext2HandlesValidityProofDataExt, + handles_3::GroupedCiphertext3HandlesValidityProofDataExt, }; diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/mod.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/mod.rs index 3d061bd..785a763 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/mod.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/mod.rs @@ -1,5 +1,5 @@ #[cfg(not(target_os = "solana"))] -use crate::zk_elgamal_proof_program::errors::ProofVerificationError; +use {crate::zk_elgamal_proof_program::errors::ProofVerificationError, merlin::Transcript}; use { bytemuck::Pod, num_derive::{FromPrimitive, ToPrimitive}, @@ -16,7 +16,7 @@ pub mod pod; pub mod pubkey_validity; pub mod zero_ciphertext; -pub use { +pub use solana_zk_sdk_pod::proof_data::{ batched_grouped_ciphertext_validity::{ BatchedGroupedCiphertext2HandlesValidityProofContext, BatchedGroupedCiphertext2HandlesValidityProofData, @@ -42,6 +42,27 @@ pub use { pubkey_validity::{PubkeyValidityProofContext, PubkeyValidityProofData}, zero_ciphertext::{ZeroCiphertextProofContext, ZeroCiphertextProofData}, }; +#[cfg(not(target_os = "solana"))] +pub use { + batched_grouped_ciphertext_validity::{ + BatchedGroupedCiphertext2HandlesValidityProofDataExt, + BatchedGroupedCiphertext3HandlesValidityProofDataExt, + }, + batched_range_proof::{ + batched_range_proof_u128::BatchedRangeProofU128DataExt, + batched_range_proof_u256::BatchedRangeProofU256DataExt, + batched_range_proof_u64::BatchedRangeProofU64DataExt, BatchedRangeProofContextExt, + }, + ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProofDataExt, + ciphertext_commitment_equality::CiphertextCommitmentEqualityProofDataExt, + grouped_ciphertext_validity::{ + GroupedCiphertext2HandlesValidityProofDataExt, + GroupedCiphertext3HandlesValidityProofDataExt, + }, + percentage_with_cap::PercentageWithCapProofDataExt, + pubkey_validity::PubkeyValidityProofDataExt, + zero_ciphertext::ZeroCiphertextProofDataExt, +}; #[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive, PartialEq, Eq)] #[repr(u8)] @@ -70,3 +91,8 @@ pub trait ZkProofData { #[cfg(not(target_os = "solana"))] fn verify_proof(&self) -> Result<(), ProofVerificationError>; } + +#[cfg(not(target_os = "solana"))] +trait ProofContext { + fn new_transcript(&self) -> Transcript; +} diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/percentage_with_cap.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/percentage_with_cap.rs index 9c62eaf..b354f07 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/percentage_with_cap.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/percentage_with_cap.rs @@ -6,66 +6,50 @@ //! - the `percentage` amount is equal to a constant (referred to as the `max_value`) //! - the `delta` and `claimed` amounts are equal +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::percentage_with_cap::{ + PercentageWithCapProofContext, PercentageWithCapProofData, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ encryption::pedersen::{PedersenCommitment, PedersenOpening}, sigma_proofs::percentage_with_cap::PercentageWithCapProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::pedersen::PodPedersenCommitment, std::convert::TryInto, }; -use { - crate::{ - encryption::pod::pedersen::PodPedersenCommitment, - pod::PodU64, - sigma_proofs::pod::PodPercentageWithCapProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, - }, - bytemuck_derive::{Pod, Zeroable}, -}; -/// The instruction data that is needed for the `ProofInstruction::VerifyPercentageWithCap` -/// instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct PercentageWithCapProofData { - pub context: PercentageWithCapProofContext, - - pub proof: PodPercentageWithCapProof, -} - -/// The context data needed to verify a percentage-with-cap proof. -/// -/// We refer to [`ZK ElGamal proof`] for the formal details on how the percentage-with-cap proof is -/// computed. -/// -/// [`ZK ElGamal proof`]: https://docs.solanalabs.com/runtime/zk-token-proof -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct PercentageWithCapProofContext { - /// The Pedersen commitment to the percentage amount. - pub percentage_commitment: PodPedersenCommitment, - - /// The Pedersen commitment to the delta amount. - pub delta_commitment: PodPedersenCommitment, - - /// The Pedersen commitment to the claimed amount. - pub claimed_commitment: PodPedersenCommitment, - - /// The maximum cap bound. - pub max_value: PodU64, +#[cfg(not(target_os = "solana"))] +pub trait PercentageWithCapProofDataExt { + #[allow(clippy::too_many_arguments)] + fn new( + percentage_commitment: &PedersenCommitment, + percentage_opening: &PedersenOpening, + percentage_amount: u64, + delta_commitment: &PedersenCommitment, + delta_opening: &PedersenOpening, + delta_amount: u64, + claimed_commitment: &PedersenCommitment, + claimed_opening: &PedersenOpening, + max_value: u64, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl PercentageWithCapProofData { +impl PercentageWithCapProofDataExt for PercentageWithCapProofData { #[allow(clippy::too_many_arguments)] - pub fn new( + fn new( percentage_commitment: &PedersenCommitment, percentage_opening: &PedersenOpening, percentage_amount: u64, @@ -138,7 +122,7 @@ impl ZkProofData for PercentageWithCapProofData { } #[cfg(not(target_os = "solana"))] -impl PercentageWithCapProofContext { +impl ProofContext for PercentageWithCapProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"percentage-with-cap-instruction"); transcript.append_message( diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/pubkey_validity.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/pubkey_validity.rs index 8b8ceb9..d66d11b 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/pubkey_validity.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/pubkey_validity.rs @@ -5,52 +5,38 @@ //! corresponding secret key). To generate the proof, a prover must provide the secret key for the //! public key. +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::pubkey_validity::{ + PubkeyValidityProofContext, PubkeyValidityProofData, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ encryption::elgamal::ElGamalKeypair, sigma_proofs::pubkey_validity::PubkeyValidityProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::PodElGamalPubkey, std::convert::TryInto, }; -use { - crate::{ - encryption::pod::elgamal::PodElGamalPubkey, - sigma_proofs::pod::PodPubkeyValidityProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, - }, - bytemuck_derive::{Pod, Zeroable}, -}; - -/// The instruction data that is needed for the `ProofInstruction::VerifyPubkeyValidity` -/// instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct PubkeyValidityProofData { - /// The context data for the public key validity proof - pub context: PubkeyValidityProofContext, // 32 bytes - /// Proof that the public key is well-formed - pub proof: PodPubkeyValidityProof, // 64 bytes -} - -/// The context data needed to verify a pubkey validity proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct PubkeyValidityProofContext { - /// The public key to be proved - pub pubkey: PodElGamalPubkey, // 32 bytes +#[cfg(not(target_os = "solana"))] +pub trait PubkeyValidityProofDataExt { + fn new(keypair: &ElGamalKeypair) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl PubkeyValidityProofData { - pub fn new(keypair: &ElGamalKeypair) -> Result { +impl PubkeyValidityProofDataExt for PubkeyValidityProofData { + fn new(keypair: &ElGamalKeypair) -> Result { let pod_pubkey = PodElGamalPubkey(keypair.pubkey().into()); let context = PubkeyValidityProofContext { pubkey: pod_pubkey }; @@ -80,7 +66,7 @@ impl ZkProofData for PubkeyValidityProofData { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl PubkeyValidityProofContext { +impl ProofContext for PubkeyValidityProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"pubkey-validity-instruction"); transcript.append_message(b"pubkey", bytes_of(&self.pubkey)); diff --git a/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs b/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs index 4da673b..a7b6605 100644 --- a/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs +++ b/zk-sdk/src/zk_elgamal_proof_program/proof_data/zero_ciphertext.rs @@ -4,54 +4,41 @@ //! certifies that a given ciphertext encrypts the message 0 in the field (`Scalar::zero()`). To //! generate the proof, a prover must provide the decryption key for the ciphertext. +use { + crate::zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, + solana_zk_sdk_pod::proof_data::zero_ciphertext::{ + ZeroCiphertextProofContext, ZeroCiphertextProofData, + }, +}; #[cfg(not(target_os = "solana"))] use { crate::{ encryption::elgamal::{ElGamalCiphertext, ElGamalKeypair}, sigma_proofs::zero_ciphertext::ZeroCiphertextProof, - zk_elgamal_proof_program::errors::{ProofGenerationError, ProofVerificationError}, + zk_elgamal_proof_program::{ + errors::{ProofGenerationError, ProofVerificationError}, + proof_data::ProofContext, + }, }, bytemuck::bytes_of, merlin::Transcript, + solana_zk_sdk_pod::encryption::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, std::convert::TryInto, }; -use { - crate::{ - encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, - sigma_proofs::pod::PodZeroCiphertextProof, - zk_elgamal_proof_program::proof_data::{ProofType, ZkProofData}, - }, - bytemuck_derive::{Pod, Zeroable}, -}; -/// The instruction data that is needed for the `ProofInstruction::VerifyZeroCiphertext` instruction. -/// -/// It includes the cryptographic proof as well as the context data information needed to verify -/// the proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct ZeroCiphertextProofData { - /// The context data for the zero-ciphertext proof - pub context: ZeroCiphertextProofContext, // 96 bytes - - /// Proof that the ciphertext is zero - pub proof: PodZeroCiphertextProof, // 96 bytes -} - -/// The context data needed to verify a zero-ciphertext proof. -#[derive(Clone, Copy, Pod, Zeroable)] -#[repr(C)] -pub struct ZeroCiphertextProofContext { - /// The ElGamal pubkey associated with the ElGamal ciphertext - pub pubkey: PodElGamalPubkey, // 32 bytes - - /// The ElGamal ciphertext that encrypts zero - pub ciphertext: PodElGamalCiphertext, // 64 bytes +#[cfg(not(target_os = "solana"))] +pub trait ZeroCiphertextProofDataExt { + fn new( + keypair: &ElGamalKeypair, + ciphertext: &ElGamalCiphertext, + ) -> Result + where + Self: Sized; } #[cfg(not(target_os = "solana"))] -impl ZeroCiphertextProofData { - pub fn new( +impl ZeroCiphertextProofDataExt for ZeroCiphertextProofData { + fn new( keypair: &ElGamalKeypair, ciphertext: &ElGamalCiphertext, ) -> Result { @@ -91,7 +78,7 @@ impl ZkProofData for ZeroCiphertextProofData { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] -impl ZeroCiphertextProofContext { +impl ProofContext for ZeroCiphertextProofContext { fn new_transcript(&self) -> Transcript { let mut transcript = Transcript::new(b"zero-ciphertext-instruction");