From 20be65e23c2d69ea60e7d517ac577d5a0f0f1617 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Wed, 28 Jun 2023 09:55:55 +0900 Subject: [PATCH] [zk-token-sdk](docs) Update docs to include a brief description of how fee sigma proof is computed (#32288) * function docs to focus on the action they perform * update docs for fee sigma proof * add link to zk token proof doc * Apply suggestions from code review Co-authored-by: Tyera --------- Co-authored-by: Tyera (cherry picked from commit 5624aaa1e59324b45ef652fbea3e0e8194466366) --- ...tched_grouped_ciphertext_validity_proof.rs | 4 +- .../ciphertext_ciphertext_equality_proof.rs | 5 +- .../ciphertext_commitment_equality_proof.rs | 4 +- zk-token-sdk/src/sigma_proofs/fee_proof.rs | 55 ++++++++++++++++--- zk-token-sdk/src/sigma_proofs/mod.rs | 18 ++---- zk-token-sdk/src/sigma_proofs/pubkey_proof.rs | 4 +- .../src/sigma_proofs/zero_balance_proof.rs | 4 +- 7 files changed, 60 insertions(+), 34 deletions(-) diff --git a/zk-token-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity_proof.rs b/zk-token-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity_proof.rs index 66207ba119c439..7247b3dfb7654b 100644 --- a/zk-token-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/batched_grouped_ciphertext_validity_proof.rs @@ -41,7 +41,7 @@ pub struct BatchedGroupedCiphertext2HandlesValidityProof(GroupedCiphertext2Handl #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] impl BatchedGroupedCiphertext2HandlesValidityProof { - /// Batched grouped ciphertext validity proof constructor. + /// Creates a batched grouped ciphertext validity proof. /// /// The function simply batches the input openings and invokes the standard grouped ciphertext /// validity proof constructor. @@ -66,7 +66,7 @@ impl BatchedGroupedCiphertext2HandlesValidityProof { )) } - /// Batched grouped ciphertext validity proof verifier. + /// Verifies a batched grouped ciphertext validity proof. /// /// The function does *not* hash the public keys, commitment, or decryption handles into the /// transcript. For security, the caller (the main protocol) should hash these public diff --git a/zk-token-sdk/src/sigma_proofs/ciphertext_ciphertext_equality_proof.rs b/zk-token-sdk/src/sigma_proofs/ciphertext_ciphertext_equality_proof.rs index 347eb7bb41b5f7..0ed819465021e7 100644 --- a/zk-token-sdk/src/sigma_proofs/ciphertext_ciphertext_equality_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/ciphertext_ciphertext_equality_proof.rs @@ -45,7 +45,7 @@ pub struct CiphertextCiphertextEqualityProof { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] impl CiphertextCiphertextEqualityProof { - /// The ciphertext-ciphertext equality proof constructor. + /// Creates a ciphertext-ciphertext equality proof. /// /// The function does *not* hash the public key, ciphertext, or commitment into the transcript. /// For security, the caller (the main protocol) should hash these public components prior to @@ -120,8 +120,7 @@ impl CiphertextCiphertextEqualityProof { } } - /// The ciphertext-ciphertext equality proof verifier. The proof is with respect to two - /// ciphertexts. + /// Verifies a ciphertext-ciphertext equality proof. /// /// * `source_pubkey` - The ElGamal pubkey associated with the first ciphertext to be proved /// * `destination_pubkey` - The ElGamal pubkey associated with the second ciphertext to be proved diff --git a/zk-token-sdk/src/sigma_proofs/ciphertext_commitment_equality_proof.rs b/zk-token-sdk/src/sigma_proofs/ciphertext_commitment_equality_proof.rs index 7077cde5d400c7..b3c2d577ddf0b5 100644 --- a/zk-token-sdk/src/sigma_proofs/ciphertext_commitment_equality_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/ciphertext_commitment_equality_proof.rs @@ -49,7 +49,7 @@ pub struct CiphertextCommitmentEqualityProof { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] impl CiphertextCommitmentEqualityProof { - /// Equality proof constructor. The proof is with respect to a ciphertext and commitment. + /// Creates a ciphertext-commitment equality proof. /// /// The function does *not* hash the public key, ciphertext, or commitment into the transcript. /// For security, the caller (the main protocol) should hash these public components prior to @@ -120,7 +120,7 @@ impl CiphertextCommitmentEqualityProof { } } - /// Equality proof verifier. The proof is with respect to a single ciphertext and commitment. + /// Verifies a ciphertext-commitment equality proof. /// /// * `source_pubkey` - The ElGamal pubkey associated with the ciphertext to be proved /// * `source_ciphertext` - The main ElGamal ciphertext to be proved diff --git a/zk-token-sdk/src/sigma_proofs/fee_proof.rs b/zk-token-sdk/src/sigma_proofs/fee_proof.rs index 07797aa44bd740..13ae7d8b9b3d6d 100644 --- a/zk-token-sdk/src/sigma_proofs/fee_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/fee_proof.rs @@ -1,6 +1,14 @@ -//! The sigma proofs for transfer fees. +//! The fee sigma proof. //! -//! TODO: Add detail on how the fee is calculated. +//! A fee sigma proof certifies that an ElGamal ciphertext encrypts a properly computed transfer fee. +//! +//! A detailed description of how transfer fees and proofs are calculated is provided in the [`ZK +//! Token proof program`] documentation. +//! +//! The protocol guarantees computational soundness (by the hardness of discrete log) and perfect +//! zero-knowledge in the random oracle model. +//! +//! [`ZK Token proof program`]: https://edge.docs.solana.com/developing/runtime-facilities/zk-token-proof #[cfg(not(target_os = "solana"))] use { @@ -27,9 +35,9 @@ use { /// Fee sigma proof. /// /// The proof consists of two main components: `fee_max_proof` and `fee_equality_proof`. If the fee -/// surpasses the maximum fee bound, then the `fee_max_proof` should properly be generated and -/// `fee_equality_proof` should be simulated. If the fee is below the maximum fee bound, then the -/// `fee_equality_proof` should be properly generated and `fee_max_proof` should be simulated. +/// is greater than the maximum fee bound, then the `fee_max_proof` is properly generated and +/// `fee_equality_proof` is simulated. If the fee is smaller than the maximum fee bound, the +/// `fee_equality_proof` is properly generated and `fee_max_proof` is simulated. #[derive(Clone)] pub struct FeeSigmaProof { /// Proof that the committed fee amount equals the maximum fee bound @@ -45,8 +53,36 @@ impl FeeSigmaProof { /// Creates a fee sigma proof assuming that the committed fee is greater than the maximum fee /// bound. /// - /// Note: the proof is generated twice via `create_proof_fee_above_max` and - /// `create_proof_fee_below_max` to enforce constant time execution. + /// A transfer fee amount `fee_amount` for a `transfer_amount` is determined by two parameters: + /// - the `fee_rate_basis_point`, which defines the fee rate in units of 0.01%, + /// - the `max_fee`, which defines the cap amount for a transfer fee. + /// + /// This means that there are two cases to consider. If `fee_amount >= max_fee`, then the + /// `fee_amount` must always equal `max_fee`. + /// + /// If `fee_amount < max_fee`, then assuming that there is no division rounding, the + /// `fee_amount` must satisfy the relation `transfer_amount * (fee_rate_basis_point / + /// 10_000) = fee_amount` or equivalently, `(transfer_amount * fee_rate_basis_point) - (10_000 + /// * fee_amount) = 0`. More generally, let `delta_fee = (transfer_amount * + /// fee_rate_basis_point) - (10_000 * fee_amount)`. Then assuming that a division rounding + /// could occur, the `delta_fee` must satisfy the bound `0 <= delta_fee < 10_000`. + /// + /// If `fee_amount >= max_fee`, then `fee_amount = max_fee` and therefore, the prover can + /// generate a proof certifying that a fee commitment exactly encodes `max_fee`. If + /// `fee_amount < max_fee`, then the prover can create a commitment to `delta_fee` and + /// create a range proof certifying that the committed value satisfies the bound `0 <= + /// delta_fee < 10_000`. + /// + /// Since the type of proof that a prover generates reveals information about the transfer + /// amount and transfer fee, the prover must generate and include both types of proof. If + /// `fee_amount >= max_fee`, then the prover generates a valid `fee_max_proof`, but commits + /// to 0 as the "claimed" delta value and simulates ("fakes") a proof (`fee_equality_proof`) + /// that this is valid. If `fee_amount > max_fee`, then the prover simulates a + /// `fee_max_proof`, and creates a valid `fee_equality_proof` certifying that the claimed delta + /// value is equal to the "real" delta value. + /// + /// Note: In the implementation, the proof is generated twice via `create_proof_fee_above_max` + /// and `create_proof_fee_below_max` to enforce that the function executes in constant time. /// /// * `(fee_amount, fee_commitment, fee_opening)` - The amount, Pedersen commitment, and /// opening of the transfer fee @@ -85,7 +121,7 @@ impl FeeSigmaProof { let below_max = u64::ct_gt(&max_fee, &fee_amount); // choose one of `proof_fee_above_max` or `proof_fee_below_max` according to whether the - // fee amount surpasses max fee + // fee amount is greater than `max_fee` or not let fee_max_proof = FeeMaxProof::conditional_select( &proof_fee_above_max.fee_max_proof, &proof_fee_below_max.fee_max_proof, @@ -259,7 +295,7 @@ impl FeeSigmaProof { } } - /// Fee sigma proof verifier. + /// Verifies a fee sigma proof /// /// * `fee_commitment` - The Pedersen commitment of the transfer fee /// * `delta_commitment` - The Pedersen commitment of the "real" delta value @@ -443,6 +479,7 @@ impl ConditionallySelectable for FeeEqualityProof { } } +/// Selects one of two Ristretto points in constant time. #[allow(clippy::needless_range_loop)] fn conditional_select_ristretto( a: &CompressedRistretto, diff --git a/zk-token-sdk/src/sigma_proofs/mod.rs b/zk-token-sdk/src/sigma_proofs/mod.rs index 41b7760ee0ecc3..db17c10fda2b88 100644 --- a/zk-token-sdk/src/sigma_proofs/mod.rs +++ b/zk-token-sdk/src/sigma_proofs/mod.rs @@ -1,19 +1,9 @@ -//! Collection of sigma proofs (more precisely, "arguments") that are used in the Solana zk-token -//! protocol. +//! Collection of sigma proofs that are used in the ZK Token proof program. //! -//! The module contains implementations of the following proof systems that work on Pedersen -//! commitments and twisted ElGamal ciphertexts: -//! - Equality proof: can be used to certify that a twisted ElGamal ciphertext encrypts the same -//! message as either a Pedersen commitment or another ElGamal ciphertext. -//! - Validity proof: can be used to certify that a twisted ElGamal ciphertext is a properly-formed -//! ciphertext with respect to a pair of ElGamal public keys. -//! - Zero-balance proof: can be used to certify that a twisted ElGamal ciphertext encrypts the -//! message 0. -//! - Fee proof: can be used to certify that an ElGamal ciphertext properly encrypts a transfer -//! fee. +//! Formal documentation and security proofs for the sigma proofs in this module can be found in +//! [`ZK Token proof`] program documentation. //! -//! We refer to the zk-token paper for the formal details and security proofs of these argument -//! systems. +//! [`ZK Token proof`]: https://edge.docs.solana.com/developing/runtime-facilities/zk-token-proof pub mod batched_grouped_ciphertext_validity_proof; pub mod ciphertext_ciphertext_equality_proof; diff --git a/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs index 1b4f9305e58cc3..2ac49178cbcc7a 100644 --- a/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/pubkey_proof.rs @@ -41,7 +41,7 @@ pub struct PubkeyValidityProof { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] impl PubkeyValidityProof { - /// Public-key proof constructor. + /// Creates a public key validity proof. /// /// The function does *not* hash the public key and ciphertext into the transcript. For /// security, the caller (the main protocol) should hash these public key components prior to @@ -80,7 +80,7 @@ impl PubkeyValidityProof { Self { Y, z } } - /// Public-key proof verifier. + /// Verifies a public key validity proof. /// /// * `elgamal_pubkey` - The ElGamal public key to be proved /// * `transcript` - The transcript that does the bookkeeping for the Fiat-Shamir heuristic diff --git a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs index 6df356e234944d..128181573dfe6c 100644 --- a/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs +++ b/zk-token-sdk/src/sigma_proofs/zero_balance_proof.rs @@ -41,7 +41,7 @@ pub struct ZeroBalanceProof { #[allow(non_snake_case)] #[cfg(not(target_os = "solana"))] impl ZeroBalanceProof { - /// Zero-balance proof constructor. + /// Creates a zero-balance proof. /// /// The function does *not* hash the public key and ciphertext into the transcript. For /// security, the caller (the main protocol) should hash these public components prior to @@ -88,7 +88,7 @@ impl ZeroBalanceProof { Self { Y_P, Y_D, z } } - /// Zero-balance proof verifier. + /// Verifies a zero-balance proof. /// /// * `elgamal_pubkey` - The ElGamal pubkey associated with the ciphertext to be proved /// * `ciphertext` - The main ElGamal ciphertext to be proved