gorb_ctpg/
lib.rs

1use {
2    curve25519_dalek::scalar::Scalar,
3    solana_zk_sdk::{
4        encryption::{
5            elgamal::ElGamalCiphertext,
6            pedersen::{PedersenCommitment, PedersenOpening},
7            pod::elgamal::PodElGamalCiphertext,
8        },
9        zk_elgamal_proof_program::proof_data::BatchedGroupedCiphertext3HandlesValidityProofData,
10    },
11};
12
13pub mod burn;
14pub mod encryption;
15pub mod errors;
16pub mod mint;
17pub mod transfer;
18pub mod transfer_with_fee;
19pub mod withdraw;
20
21/// The low bit length of the encrypted transfer amount
22pub const TRANSFER_AMOUNT_LO_BITS: usize = 16;
23/// The high bit length of the encrypted transfer amount
24pub const TRANSFER_AMOUNT_HI_BITS: usize = 32;
25/// The bit length of the encrypted remaining balance in a token account
26pub const REMAINING_BALANCE_BIT_LENGTH: usize = 64;
27
28/// Takes in a 64-bit number `amount` and a bit length `bit_length`. It returns:
29/// - the `bit_length` low bits of `amount` interpreted as `u64`
30/// - the `(64 - bit_length)` high bits of `amount` interpreted as `u64`
31pub fn try_split_u64(amount: u64, bit_length: usize) -> Option<(u64, u64)> {
32    match bit_length {
33        0 => Some((0, amount)),
34        1..=63 => {
35            let bit_length_complement = u64::BITS.checked_sub(bit_length as u32).unwrap();
36            // shifts are safe as long as `bit_length` and `bit_length_complement` < 64
37            let lo = amount
38                .checked_shl(bit_length_complement)?
39                .checked_shr(bit_length_complement)?;
40            let hi = amount.checked_shr(bit_length as u32)?;
41            Some((lo, hi))
42        }
43        64 => Some((amount, 0)),
44        _ => None,
45    }
46}
47
48/// Combine two numbers that are interpreted as the low and high bits of a
49/// target number. The `bit_length` parameter specifies the number of bits that
50/// `amount_hi` is to be shifted by.
51pub fn try_combine_lo_hi_u64(amount_lo: u64, amount_hi: u64, bit_length: usize) -> Option<u64> {
52    match bit_length {
53        0 => Some(amount_hi),
54        1..=63 => {
55            // shifts are safe as long as `bit_length` < 64
56            amount_hi
57                .checked_shl(bit_length as u32)?
58                .checked_add(amount_hi)
59        }
60        64 => Some(amount_lo),
61        _ => None,
62    }
63}
64
65#[allow(clippy::arithmetic_side_effects)]
66pub fn try_combine_lo_hi_ciphertexts(
67    ciphertext_lo: &ElGamalCiphertext,
68    ciphertext_hi: &ElGamalCiphertext,
69    bit_length: usize,
70) -> Option<ElGamalCiphertext> {
71    let two_power = 1_u64.checked_shl(bit_length as u32)?;
72    Some(ciphertext_lo + ciphertext_hi * Scalar::from(two_power))
73}
74
75#[allow(clippy::arithmetic_side_effects)]
76pub fn try_combine_lo_hi_commitments(
77    comm_lo: &PedersenCommitment,
78    comm_hi: &PedersenCommitment,
79    bit_length: usize,
80) -> Option<PedersenCommitment> {
81    let two_power = 1_u64.checked_shl(bit_length as u32)?;
82    Some(comm_lo + comm_hi * Scalar::from(two_power))
83}
84
85#[allow(clippy::arithmetic_side_effects)]
86pub fn try_combine_lo_hi_openings(
87    opening_lo: &PedersenOpening,
88    opening_hi: &PedersenOpening,
89    bit_length: usize,
90) -> Option<PedersenOpening> {
91    let two_power = 1_u64.checked_shl(bit_length as u32)?;
92    Some(opening_lo + opening_hi * Scalar::from(two_power))
93}
94
95/// A type that wraps a ciphertext validity proof along with two `lo` and `hi`
96/// ciphertexts.
97///
98/// Ciphertext validity proof data contains grouped ElGamal ciphertexts (`lo`
99/// and `hi`) and a proof containing the validity of these ciphertexts. Token
100/// client-side logic often requires a function to extract specific forms of
101/// the grouped ElGamal ciphertexts. This type is a convenience type that
102/// contains the proof data and the extracted ciphertexts.
103#[derive(Clone, Copy)]
104pub struct CiphertextValidityProofWithAuditorCiphertext {
105    pub proof_data: BatchedGroupedCiphertext3HandlesValidityProofData,
106    pub ciphertext_lo: PodElGamalCiphertext,
107    pub ciphertext_hi: PodElGamalCiphertext,
108}