zk-token-sdk: generalize range proof (#23506)

* zk-token-sdk: update range proof in transfers for more flexible setting of params

* zk-token-sdk: clippy
This commit is contained in:
samkim-crypto
2022-03-07 12:27:56 -05:00
committed by GitHub
parent 3a0271c113
commit 08c9a650db
4 changed files with 250 additions and 116 deletions

View File

@@ -24,6 +24,8 @@ pub enum ProofError {
"`zk_token_elgamal::pod::ElGamalCiphertext` contains invalid ElGamalCiphertext ciphertext" "`zk_token_elgamal::pod::ElGamalCiphertext` contains invalid ElGamalCiphertext ciphertext"
)] )]
InconsistentCTData, InconsistentCTData,
#[error("failed to decrypt ciphertext from transfer data")]
Decryption,
} }
#[derive(Error, Clone, Debug, Eq, PartialEq)] #[derive(Error, Clone, Debug, Eq, PartialEq)]

View File

@@ -21,10 +21,6 @@ pub use {
withdraw_withheld::WithdrawWithheldTokensData, withdraw_withheld::WithdrawWithheldTokensData,
}; };
/// Constant for 2^32
#[cfg(not(target_arch = "bpf"))]
const TWO_32: u64 = 4294967296;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
pub trait Verifiable { pub trait Verifiable {
fn verify(&self) -> Result<(), ProofError>; fn verify(&self) -> Result<(), ProofError>;
@@ -38,35 +34,45 @@ pub enum Role {
Auditor, Auditor,
} }
/// Split u64 number into two u32 numbers /// Takes in a 64-bit number `amount` and a bit length `bit_length`. It returns:
/// - the `bit_length` low bits of `amount` interpretted as u64
/// - the (64 - `bit_length`) high bits of `amount` interpretted as u64
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
pub fn split_u64_into_u32(amount: u64) -> (u32, u32) { pub fn split_u64(amount: u64, bit_length: usize) -> (u64, u64) {
let lo = amount as u32; assert!(bit_length <= 64);
let hi = (amount >> 32) as u32;
let lo = amount << (64 - bit_length) >> (64 - bit_length);
let hi = amount >> bit_length;
(lo, hi) (lo, hi)
} }
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
fn combine_u32_ciphertexts( fn combine_lo_hi_ciphertexts(
ciphertext_lo: &ElGamalCiphertext, ciphertext_lo: &ElGamalCiphertext,
ciphertext_hi: &ElGamalCiphertext, ciphertext_hi: &ElGamalCiphertext,
bit_length: usize,
) -> ElGamalCiphertext { ) -> ElGamalCiphertext {
ciphertext_lo + &(ciphertext_hi * &Scalar::from(TWO_32)) let two_power = (1_u64) << bit_length;
ciphertext_lo + &(ciphertext_hi * &Scalar::from(two_power))
} }
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
pub fn combine_u32_commitments( pub fn combine_lo_hi_commitments(
comm_lo: &PedersenCommitment, comm_lo: &PedersenCommitment,
comm_hi: &PedersenCommitment, comm_hi: &PedersenCommitment,
bit_length: usize,
) -> PedersenCommitment { ) -> PedersenCommitment {
comm_lo + comm_hi * &Scalar::from(TWO_32) let two_power = (1_u64) << bit_length;
comm_lo + comm_hi * &Scalar::from(two_power)
} }
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
pub fn combine_u32_openings( pub fn combine_lo_hi_openings(
opening_lo: &PedersenOpening, opening_lo: &PedersenOpening,
opening_hi: &PedersenOpening, opening_hi: &PedersenOpening,
bit_length: usize,
) -> PedersenOpening { ) -> PedersenOpening {
opening_lo + opening_hi * &Scalar::from(TWO_32) let two_power = (1_u64) << bit_length;
opening_lo + opening_hi * &Scalar::from(two_power)
} }

View File

@@ -12,7 +12,7 @@ use {
pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
}, },
errors::ProofError, errors::ProofError,
instruction::{combine_u32_ciphertexts, split_u64_into_u32, Role, Verifiable, TWO_32}, instruction::{combine_lo_hi_ciphertexts, split_u64, Role, Verifiable},
range_proof::RangeProof, range_proof::RangeProof,
sigma_proofs::{ sigma_proofs::{
equality_proof::CtxtCommEqualityProof, validity_proof::AggregatedValidityProof, equality_proof::CtxtCommEqualityProof, validity_proof::AggregatedValidityProof,
@@ -27,10 +27,18 @@ use {
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_SOURCE_AMOUNT_BIT_LENGTH: usize = 64; const TRANSFER_SOURCE_AMOUNT_BIT_LENGTH: usize = 64;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 32; const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 16;
#[cfg(not(target_arch = "bpf"))]
const TRANSFER_AMOUNT_LO_NEGATED_BIT_LENGTH: usize = 16;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32; const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32;
#[cfg(not(target_arch = "bpf"))]
lazy_static::lazy_static! {
pub static ref COMMITMENT_MAX: PedersenCommitment = Pedersen::encode(1_u64 <<
TRANSFER_AMOUNT_LO_NEGATED_BIT_LENGTH);
}
#[derive(Clone)] #[derive(Clone)]
#[repr(C)] #[repr(C)]
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
@@ -44,7 +52,7 @@ pub struct TransferAmountEncryption {
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
impl TransferAmountEncryption { impl TransferAmountEncryption {
pub fn new( pub fn new(
amount: u32, amount: u64,
source_pubkey: &ElGamalPubkey, source_pubkey: &ElGamalPubkey,
destination_pubkey: &ElGamalPubkey, destination_pubkey: &ElGamalPubkey,
auditor_pubkey: &ElGamalPubkey, auditor_pubkey: &ElGamalPubkey,
@@ -99,7 +107,7 @@ impl TransferData {
(destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey), (destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey),
) -> Result<Self, ProofError> { ) -> Result<Self, ProofError> {
// split and encrypt transfer amount // split and encrypt transfer amount
let (amount_lo, amount_hi) = split_u64_into_u32(transfer_amount); let (amount_lo, amount_hi) = split_u64(transfer_amount, TRANSFER_AMOUNT_LO_BIT_LENGTH);
let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new( let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new(
amount_lo, amount_lo,
@@ -107,6 +115,7 @@ impl TransferData {
destination_pubkey, destination_pubkey,
auditor_pubkey, auditor_pubkey,
); );
let (ciphertext_hi, opening_hi) = TransferAmountEncryption::new( let (ciphertext_hi, opening_hi) = TransferAmountEncryption::new(
amount_hi, amount_hi,
&source_keypair.public, &source_keypair.public,
@@ -130,7 +139,11 @@ impl TransferData {
}; };
let new_source_ciphertext = ciphertext_old_source let new_source_ciphertext = ciphertext_old_source
- combine_u32_ciphertexts(&transfer_amount_lo_source, &transfer_amount_hi_source); - combine_lo_hi_ciphertexts(
&transfer_amount_lo_source,
&transfer_amount_hi_source,
TRANSFER_AMOUNT_LO_BIT_LENGTH,
);
// generate transcript and append all public inputs // generate transcript and append all public inputs
let pod_transfer_pubkeys = pod::TransferPubkeys { let pod_transfer_pubkeys = pod::TransferPubkeys {
@@ -201,11 +214,6 @@ impl TransferData {
} }
/// Decrypts transfer amount from transfer data /// Decrypts transfer amount from transfer data
///
/// TODO: This function should run in constant time. Use `subtle::Choice` for the if statement
/// and make sure that the function does not terminate prematurely due to errors
///
/// TODO: Define specific error type for decryption error
pub fn decrypt_amount(&self, role: Role, sk: &ElGamalSecretKey) -> Result<u64, ProofError> { pub fn decrypt_amount(&self, role: Role, sk: &ElGamalSecretKey) -> Result<u64, ProofError> {
let ciphertext_lo = self.ciphertext_lo(role)?; let ciphertext_lo = self.ciphertext_lo(role)?;
let ciphertext_hi = self.ciphertext_hi(role)?; let ciphertext_hi = self.ciphertext_hi(role)?;
@@ -214,9 +222,10 @@ impl TransferData {
let amount_hi = ciphertext_hi.decrypt_u32(sk); let amount_hi = ciphertext_hi.decrypt_u32(sk);
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) { if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
Ok(amount_lo + TWO_32 * amount_hi) let two_power = 1 << TRANSFER_AMOUNT_LO_BIT_LENGTH;
Ok(amount_lo + two_power * amount_hi)
} else { } else {
Err(ProofError::Verification) Err(ProofError::Decryption)
} }
} }
} }
@@ -252,7 +261,7 @@ impl Verifiable for TransferData {
#[repr(C)] #[repr(C)]
pub struct TransferProof { pub struct TransferProof {
/// New Pedersen commitment for the remaining balance in source /// New Pedersen commitment for the remaining balance in source
pub commitment_new_source: pod::PedersenCommitment, pub new_source_commitment: pod::PedersenCommitment,
/// Associated equality proof /// Associated equality proof
pub equality_proof: pod::CtxtCommEqualityProof, pub equality_proof: pod::CtxtCommEqualityProof,
@@ -295,7 +304,7 @@ impl TransferProof {
} }
pub fn new( pub fn new(
(transfer_amount_lo, transfer_amount_hi): (u32, u32), (transfer_amount_lo, transfer_amount_hi): (u64, u64),
source_keypair: &ElGamalKeypair, source_keypair: &ElGamalKeypair,
(destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey), (destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey),
opening_lo: &PedersenOpening, opening_lo: &PedersenOpening,
@@ -304,10 +313,10 @@ impl TransferProof {
transcript: &mut Transcript, transcript: &mut Transcript,
) -> Self { ) -> Self {
// generate a Pedersen commitment for the remaining balance in source // generate a Pedersen commitment for the remaining balance in source
let (commitment_new_source, source_opening) = Pedersen::new(source_new_balance); let (new_source_commitment, source_opening) = Pedersen::new(source_new_balance);
let pod_commitment_new_source: pod::PedersenCommitment = commitment_new_source.into(); let pod_new_source_commitment: pod::PedersenCommitment = new_source_commitment.into();
transcript.append_commitment(b"commitment-new-source", &pod_commitment_new_source); transcript.append_commitment(b"commitment-new-source", &pod_new_source_commitment);
// generate equality_proof // generate equality_proof
let equality_proof = CtxtCommEqualityProof::new( let equality_proof = CtxtCommEqualityProof::new(
@@ -327,23 +336,46 @@ impl TransferProof {
); );
// generate the range proof // generate the range proof
let range_proof = RangeProof::new( let range_proof = if TRANSFER_AMOUNT_LO_BIT_LENGTH == 32 {
vec![ RangeProof::new(
source_new_balance, vec![
transfer_amount_lo as u64, source_new_balance,
transfer_amount_hi as u64, transfer_amount_lo as u64,
], transfer_amount_hi as u64,
vec![ ],
TRANSFER_SOURCE_AMOUNT_BIT_LENGTH, vec![
TRANSFER_AMOUNT_LO_BIT_LENGTH, TRANSFER_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_AMOUNT_HI_BIT_LENGTH, TRANSFER_AMOUNT_LO_BIT_LENGTH,
], TRANSFER_AMOUNT_HI_BIT_LENGTH,
vec![&source_opening, opening_lo, opening_hi], ],
transcript, vec![&source_opening, opening_lo, opening_hi],
); transcript,
)
} else {
let transfer_amount_lo_negated =
(1 << TRANSFER_AMOUNT_LO_NEGATED_BIT_LENGTH) - transfer_amount_lo as u64;
let opening_lo_negated = &PedersenOpening::default() - opening_lo;
RangeProof::new(
vec![
source_new_balance,
transfer_amount_lo as u64,
transfer_amount_lo_negated,
transfer_amount_hi as u64,
],
vec![
TRANSFER_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_AMOUNT_LO_BIT_LENGTH,
TRANSFER_AMOUNT_LO_NEGATED_BIT_LENGTH,
TRANSFER_AMOUNT_HI_BIT_LENGTH,
],
vec![&source_opening, opening_lo, &opening_lo_negated, opening_hi],
transcript,
)
};
Self { Self {
commitment_new_source: pod_commitment_new_source, new_source_commitment: pod_new_source_commitment,
equality_proof: equality_proof.into(), equality_proof: equality_proof.into(),
validity_proof: validity_proof.into(), validity_proof: validity_proof.into(),
range_proof: range_proof.try_into().expect("range proof: length error"), range_proof: range_proof.try_into().expect("range proof: length error"),
@@ -358,9 +390,9 @@ impl TransferProof {
ciphertext_new_spendable: &ElGamalCiphertext, ciphertext_new_spendable: &ElGamalCiphertext,
transcript: &mut Transcript, transcript: &mut Transcript,
) -> Result<(), ProofError> { ) -> Result<(), ProofError> {
transcript.append_commitment(b"commitment-new-source", &self.commitment_new_source); transcript.append_commitment(b"commitment-new-source", &self.new_source_commitment);
let commitment: PedersenCommitment = self.commitment_new_source.try_into()?; let commitment: PedersenCommitment = self.new_source_commitment.try_into()?;
let equality_proof: CtxtCommEqualityProof = self.equality_proof.try_into()?; let equality_proof: CtxtCommEqualityProof = self.equality_proof.try_into()?;
let aggregated_validity_proof: AggregatedValidityProof = self.validity_proof.try_into()?; let aggregated_validity_proof: AggregatedValidityProof = self.validity_proof.try_into()?;
let range_proof: RangeProof = self.range_proof.try_into()?; let range_proof: RangeProof = self.range_proof.try_into()?;
@@ -391,16 +423,40 @@ impl TransferProof {
)?; )?;
// verify range proof // verify range proof
let commitment_new_source = self.commitment_new_source.try_into()?; let new_source_commitment = self.new_source_commitment.try_into()?;
range_proof.verify( if TRANSFER_AMOUNT_LO_BIT_LENGTH == 32 {
vec![ range_proof.verify(
&commitment_new_source, vec![
&ciphertext_lo.commitment, &new_source_commitment,
&ciphertext_hi.commitment, &ciphertext_lo.commitment,
], &ciphertext_hi.commitment,
vec![64_usize, 32_usize, 32_usize], ],
transcript, vec![
)?; TRANSFER_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_AMOUNT_LO_BIT_LENGTH,
TRANSFER_AMOUNT_HI_BIT_LENGTH,
],
transcript,
)?;
} else {
let commitment_lo_negated = &(*COMMITMENT_MAX) - &ciphertext_lo.commitment;
range_proof.verify(
vec![
&new_source_commitment,
&ciphertext_lo.commitment,
&commitment_lo_negated,
&ciphertext_hi.commitment,
],
vec![
TRANSFER_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_AMOUNT_LO_BIT_LENGTH,
TRANSFER_AMOUNT_LO_NEGATED_BIT_LENGTH,
TRANSFER_AMOUNT_HI_BIT_LENGTH,
],
transcript,
)?;
}
Ok(()) Ok(())
} }
@@ -492,11 +548,11 @@ mod test {
} = ElGamalKeypair::new_rand(); } = ElGamalKeypair::new_rand();
// create source account spendable ciphertext // create source account spendable ciphertext
let spendable_balance: u64 = 77; let spendable_balance: u64 = 770000;
let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance); let spendable_ciphertext = source_keypair.public.encrypt(spendable_balance);
// transfer amount // transfer amount
let transfer_amount: u64 = 55; let transfer_amount: u64 = 550000;
// create transfer data // create transfer data
let transfer_data = TransferData::new( let transfer_data = TransferData::new(
@@ -511,19 +567,19 @@ mod test {
transfer_data transfer_data
.decrypt_amount(Role::Source, &source_keypair.secret) .decrypt_amount(Role::Source, &source_keypair.secret)
.unwrap(), .unwrap(),
55_u64, 550000_u64,
); );
assert_eq!( assert_eq!(
transfer_data.decrypt_amount(Role::Dest, &dest_sk).unwrap(), transfer_data.decrypt_amount(Role::Dest, &dest_sk).unwrap(),
55_u64, 550000_u64,
); );
assert_eq!( assert_eq!(
transfer_data transfer_data
.decrypt_amount(Role::Auditor, &auditor_sk) .decrypt_amount(Role::Auditor, &auditor_sk)
.unwrap(), .unwrap(),
55_u64, 550000_u64,
); );
} }
} }

View File

@@ -13,8 +13,8 @@ use {
}, },
errors::ProofError, errors::ProofError,
instruction::{ instruction::{
combine_u32_ciphertexts, combine_u32_commitments, combine_u32_openings, combine_lo_hi_ciphertexts, combine_lo_hi_commitments, combine_lo_hi_openings,
split_u64_into_u32, transfer::TransferAmountEncryption, Role, Verifiable, TWO_32, split_u64, transfer::TransferAmountEncryption, Role, Verifiable,
}, },
range_proof::RangeProof, range_proof::RangeProof,
sigma_proofs::{ sigma_proofs::{
@@ -39,7 +39,9 @@ const ONE_IN_BASIS_POINTS: u128 = MAX_FEE_BASIS_POINTS as u128;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH: usize = 64; const TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH: usize = 64;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH: usize = 32; const TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH: usize = 16;
#[cfg(not(target_arch = "bpf"))]
const TRANSFER_WITH_FEE_AMOUNT_LO_NEGATED_BIT_LENGTH: usize = 16;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
const TRANSFER_WITH_FEE_AMOUNT_HI_BIT_LENGTH: usize = 32; const TRANSFER_WITH_FEE_AMOUNT_HI_BIT_LENGTH: usize = 32;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
@@ -47,6 +49,8 @@ const TRANSFER_WITH_FEE_DELTA_BIT_LENGTH: usize = 64;
#[cfg(not(target_arch = "bpf"))] #[cfg(not(target_arch = "bpf"))]
lazy_static::lazy_static! { lazy_static::lazy_static! {
pub static ref COMMITMENT_MAX: PedersenCommitment = Pedersen::encode(1_u64 <<
TRANSFER_WITH_FEE_AMOUNT_LO_NEGATED_BIT_LENGTH);
pub static ref COMMITMENT_MAX_FEE_BASIS_POINTS: PedersenCommitment = Pedersen::encode(MAX_FEE_BASIS_POINTS); pub static ref COMMITMENT_MAX_FEE_BASIS_POINTS: PedersenCommitment = Pedersen::encode(MAX_FEE_BASIS_POINTS);
} }
@@ -87,7 +91,8 @@ impl TransferWithFeeData {
withdraw_withheld_authority_pubkey: &ElGamalPubkey, withdraw_withheld_authority_pubkey: &ElGamalPubkey,
) -> Result<Self, ProofError> { ) -> Result<Self, ProofError> {
// split and encrypt transfer amount // split and encrypt transfer amount
let (amount_lo, amount_hi) = split_u64_into_u32(transfer_amount); let (amount_lo, amount_hi) =
split_u64(transfer_amount, TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH);
let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new( let (ciphertext_lo, opening_lo) = TransferAmountEncryption::new(
amount_lo, amount_lo,
@@ -118,7 +123,11 @@ impl TransferWithFeeData {
}; };
let new_source_ciphertext = old_source_ciphertext let new_source_ciphertext = old_source_ciphertext
- combine_u32_ciphertexts(&transfer_amount_lo_source, &transfer_amount_hi_source); - combine_lo_hi_ciphertexts(
&transfer_amount_lo_source,
&transfer_amount_hi_source,
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
);
// calculate and encrypt fee // calculate and encrypt fee
let (fee_amount, delta_fee) = let (fee_amount, delta_fee) =
@@ -212,11 +221,6 @@ impl TransferWithFeeData {
} }
/// Decrypts transfer amount from transfer-with-fee data /// Decrypts transfer amount from transfer-with-fee data
///
/// TODO: This function should run in constant time. Use `subtle::Choice` for the if statement
/// and make sure that the function does not terminate prematurely due to errors
///
/// TODO: Define specific error type for decryption error
pub fn decrypt_amount(&self, role: Role, sk: &ElGamalSecretKey) -> Result<u64, ProofError> { pub fn decrypt_amount(&self, role: Role, sk: &ElGamalSecretKey) -> Result<u64, ProofError> {
let ciphertext_lo = self.ciphertext_lo(role)?; let ciphertext_lo = self.ciphertext_lo(role)?;
let ciphertext_hi = self.ciphertext_hi(role)?; let ciphertext_hi = self.ciphertext_hi(role)?;
@@ -225,7 +229,8 @@ impl TransferWithFeeData {
let amount_hi = ciphertext_hi.decrypt_u32(sk); let amount_hi = ciphertext_hi.decrypt_u32(sk);
if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) { if let (Some(amount_lo), Some(amount_hi)) = (amount_lo, amount_hi) {
Ok(amount_lo + TWO_32 * amount_hi) let two_power = 1 << TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH;
Ok(amount_lo + two_power * amount_hi)
} else { } else {
Err(ProofError::Verification) Err(ProofError::Verification)
} }
@@ -324,8 +329,8 @@ impl TransferWithFeeProof {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[allow(clippy::many_single_char_names)] #[allow(clippy::many_single_char_names)]
pub fn new( pub fn new(
transfer_amount_lo_data: (u32, &TransferAmountEncryption, &PedersenOpening), transfer_amount_lo_data: (u64, &TransferAmountEncryption, &PedersenOpening),
transfer_amount_hi_data: (u32, &TransferAmountEncryption, &PedersenOpening), transfer_amount_hi_data: (u64, &TransferAmountEncryption, &PedersenOpening),
source_keypair: &ElGamalKeypair, source_keypair: &ElGamalKeypair,
(destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey), (destination_pubkey, auditor_pubkey): (&ElGamalPubkey, &ElGamalPubkey),
(source_new_balance, new_source_ciphertext): (u64, &ElGamalCiphertext), (source_new_balance, new_source_ciphertext): (u64, &ElGamalCiphertext),
@@ -388,31 +393,66 @@ impl TransferWithFeeProof {
transcript, transcript,
); );
// generate the range proof
let opening_claimed_negated = &PedersenOpening::default() - &opening_claimed; let opening_claimed_negated = &PedersenOpening::default() - &opening_claimed;
let range_proof = RangeProof::new( let range_proof = if TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH == 32 {
vec![ RangeProof::new(
source_new_balance, vec![
transfer_amount_lo as u64, source_new_balance,
transfer_amount_hi as u64, transfer_amount_lo as u64,
delta_fee, transfer_amount_hi as u64,
MAX_FEE_BASIS_POINTS - delta_fee, delta_fee,
], MAX_FEE_BASIS_POINTS - delta_fee,
vec![ ],
TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH, vec![
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH, TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_WITH_FEE_AMOUNT_HI_BIT_LENGTH, TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
TRANSFER_WITH_FEE_DELTA_BIT_LENGTH, TRANSFER_WITH_FEE_AMOUNT_HI_BIT_LENGTH,
TRANSFER_WITH_FEE_DELTA_BIT_LENGTH, TRANSFER_WITH_FEE_DELTA_BIT_LENGTH,
], TRANSFER_WITH_FEE_DELTA_BIT_LENGTH,
vec![ ],
&opening_source, vec![
opening_lo, &opening_source,
opening_hi, opening_lo,
&opening_claimed, opening_hi,
&opening_claimed_negated, &opening_claimed,
], &opening_claimed_negated,
transcript, ],
); transcript,
)
} else {
let transfer_amount_lo_negated =
(1 << TRANSFER_WITH_FEE_AMOUNT_LO_NEGATED_BIT_LENGTH) - transfer_amount_lo as u64;
let opening_lo_negated = &PedersenOpening::default() - opening_lo;
RangeProof::new(
vec![
source_new_balance,
transfer_amount_lo as u64,
transfer_amount_lo_negated,
transfer_amount_hi as u64,
delta_fee,
MAX_FEE_BASIS_POINTS - delta_fee,
],
vec![
TRANSFER_WITH_FEE_SOURCE_AMOUNT_BIT_LENGTH,
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
TRANSFER_WITH_FEE_AMOUNT_LO_NEGATED_BIT_LENGTH,
TRANSFER_WITH_FEE_AMOUNT_HI_BIT_LENGTH,
TRANSFER_WITH_FEE_DELTA_BIT_LENGTH,
TRANSFER_WITH_FEE_DELTA_BIT_LENGTH,
],
vec![
&opening_source,
opening_lo,
&opening_lo_negated,
opening_hi,
&opening_claimed,
&opening_claimed_negated,
],
transcript,
)
};
Self { Self {
new_source_commitment: pod_new_source_commitment, new_source_commitment: pod_new_source_commitment,
@@ -458,8 +498,6 @@ impl TransferWithFeeProof {
transcript, transcript,
)?; )?;
println!("here");
// verify that the transfer amount is encrypted correctly // verify that the transfer amount is encrypted correctly
ciphertext_amount_validity_proof.verify( ciphertext_amount_validity_proof.verify(
( (
@@ -504,18 +542,38 @@ impl TransferWithFeeProof {
transcript, transcript,
)?; )?;
// verify range proof
let new_source_commitment = self.new_source_commitment.try_into()?;
let claimed_commitment_negated = &(*COMMITMENT_MAX_FEE_BASIS_POINTS) - &claimed_commitment; let claimed_commitment_negated = &(*COMMITMENT_MAX_FEE_BASIS_POINTS) - &claimed_commitment;
range_proof.verify(
vec![ if TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH == 32 {
&new_source_commitment, range_proof.verify(
&ciphertext_lo.commitment, vec![
&ciphertext_hi.commitment, &new_source_commitment,
&claimed_commitment, &ciphertext_lo.commitment,
&claimed_commitment_negated, &ciphertext_hi.commitment,
], &claimed_commitment,
vec![64, 32, 32, 64, 64], &claimed_commitment_negated,
transcript, ],
)?; vec![64, 32, 32, 64, 64],
transcript,
)?;
} else {
let commitment_lo_negated = &(*COMMITMENT_MAX) - &ciphertext_lo.commitment;
range_proof.verify(
vec![
&new_source_commitment,
&ciphertext_lo.commitment,
&commitment_lo_negated,
&ciphertext_hi.commitment,
&claimed_commitment,
&claimed_commitment_negated,
],
vec![64, 16, 16, 32, 64, 64],
transcript,
)?;
}
Ok(()) Ok(())
} }
@@ -656,10 +714,18 @@ fn compute_delta_commitment_and_opening(
let fee_rate_scalar = Scalar::from(fee_rate_basis_points); let fee_rate_scalar = Scalar::from(fee_rate_basis_points);
let delta_commitment = fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) let delta_commitment = fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS)
- &(&combine_u32_commitments(commitment_lo, commitment_hi) * &fee_rate_scalar); - &(&combine_lo_hi_commitments(
commitment_lo,
commitment_hi,
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
) * &fee_rate_scalar);
let opening_delta = opening_fee * Scalar::from(MAX_FEE_BASIS_POINTS) let opening_delta = opening_fee * Scalar::from(MAX_FEE_BASIS_POINTS)
- &(&combine_u32_openings(opening_lo, opening_hi) * &fee_rate_scalar); - &(&combine_lo_hi_openings(
opening_lo,
opening_hi,
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
) * &fee_rate_scalar);
(delta_commitment, opening_delta) (delta_commitment, opening_delta)
} }
@@ -674,7 +740,11 @@ fn compute_delta_commitment(
let fee_rate_scalar = Scalar::from(fee_rate_basis_points); let fee_rate_scalar = Scalar::from(fee_rate_basis_points);
fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS) fee_commitment * Scalar::from(MAX_FEE_BASIS_POINTS)
- &(&combine_u32_commitments(commitment_lo, commitment_hi) * &fee_rate_scalar) - &(&combine_lo_hi_commitments(
commitment_lo,
commitment_hi,
TRANSFER_WITH_FEE_AMOUNT_LO_BIT_LENGTH,
) * &fee_rate_scalar)
} }
#[cfg(test)] #[cfg(test)]