[zk-token-sdk] replace hard-coded constants with constant variables (#32274)
* add ristretto and scalar byte length constants * add serialization and deserialization helper functions * remove hard-coded constants in the `sigma` module * remove hard-coded constants in the `encryption` module * remove hard-coded constants in the `zk-token-elgamal` module * Apply suggestions from code review Co-authored-by: Tyera <tyera@solana.com> * fix docs for range proof constants * Apply suggestions from code review Co-authored-by: Tyera <tyera@solana.com> * clippy --------- Co-authored-by: Tyera <tyera@solana.com>
This commit is contained in:
parent
5dee2e4d0c
commit
91186d3860
|
@ -31,6 +31,19 @@ use {
|
||||||
zeroize::Zeroize,
|
zeroize::Zeroize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of an authenticated encryption secret key
|
||||||
|
const AE_KEY_LEN: usize = 16;
|
||||||
|
|
||||||
|
/// Byte length of an authenticated encryption nonce component
|
||||||
|
const NONCE_LEN: usize = 12;
|
||||||
|
|
||||||
|
/// Byte lenth of an authenticated encryption ciphertext component
|
||||||
|
const CIPHERTEXT_LEN: usize = 24;
|
||||||
|
|
||||||
|
/// Byte length of a complete authenticated encryption ciphertext component that includes the
|
||||||
|
/// ciphertext and nonce components
|
||||||
|
const AE_CIPHERTEXT_LEN: usize = 36;
|
||||||
|
|
||||||
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum AuthenticatedEncryptionError {
|
pub enum AuthenticatedEncryptionError {
|
||||||
#[error("key derivation method not supported")]
|
#[error("key derivation method not supported")]
|
||||||
|
@ -46,7 +59,7 @@ impl AuthenticatedEncryption {
|
||||||
/// This function is randomized. It internally samples a 128-bit key using `OsRng`.
|
/// This function is randomized. It internally samples a 128-bit key using `OsRng`.
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
fn keygen() -> AeKey {
|
fn keygen() -> AeKey {
|
||||||
AeKey(OsRng.gen::<[u8; 16]>())
|
AeKey(OsRng.gen::<[u8; AE_KEY_LEN]>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// On input of an authenticated encryption key and an amount, the function returns a
|
/// On input of an authenticated encryption key and an amount, the function returns a
|
||||||
|
@ -54,7 +67,7 @@ impl AuthenticatedEncryption {
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
fn encrypt(key: &AeKey, balance: u64) -> AeCiphertext {
|
fn encrypt(key: &AeKey, balance: u64) -> AeCiphertext {
|
||||||
let mut plaintext = balance.to_le_bytes();
|
let mut plaintext = balance.to_le_bytes();
|
||||||
let nonce: Nonce = OsRng.gen::<[u8; 12]>();
|
let nonce: Nonce = OsRng.gen::<[u8; NONCE_LEN]>();
|
||||||
|
|
||||||
// The balance and the nonce have fixed length and therefore, encryption should not fail.
|
// The balance and the nonce have fixed length and therefore, encryption should not fail.
|
||||||
let ciphertext = Aes128GcmSiv::new(&key.0.into())
|
let ciphertext = Aes128GcmSiv::new(&key.0.into())
|
||||||
|
@ -86,7 +99,7 @@ impl AuthenticatedEncryption {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Zeroize)]
|
#[derive(Debug, Zeroize)]
|
||||||
pub struct AeKey([u8; 16]);
|
pub struct AeKey([u8; AE_KEY_LEN]);
|
||||||
impl AeKey {
|
impl AeKey {
|
||||||
/// Deterministically derives an authenticated encryption key from a Solana signer and a public
|
/// Deterministically derives an authenticated encryption key from a Solana signer and a public
|
||||||
/// seed.
|
/// seed.
|
||||||
|
@ -144,7 +157,7 @@ impl AeKey {
|
||||||
|
|
||||||
impl EncodableKey for AeKey {
|
impl EncodableKey for AeKey {
|
||||||
fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
|
fn read<R: Read>(reader: &mut R) -> Result<Self, Box<dyn error::Error>> {
|
||||||
let bytes: [u8; 16] = serde_json::from_reader(reader)?;
|
let bytes: [u8; AE_KEY_LEN] = serde_json::from_reader(reader)?;
|
||||||
Ok(Self(bytes))
|
Ok(Self(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +171,7 @@ impl EncodableKey for AeKey {
|
||||||
|
|
||||||
impl SeedDerivable for AeKey {
|
impl SeedDerivable for AeKey {
|
||||||
fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
|
fn from_seed(seed: &[u8]) -> Result<Self, Box<dyn error::Error>> {
|
||||||
const MINIMUM_SEED_LEN: usize = 16;
|
const MINIMUM_SEED_LEN: usize = AE_KEY_LEN;
|
||||||
|
|
||||||
if seed.len() < MINIMUM_SEED_LEN {
|
if seed.len() < MINIMUM_SEED_LEN {
|
||||||
return Err(AuthenticatedEncryptionError::SeedLengthTooShort.into());
|
return Err(AuthenticatedEncryptionError::SeedLengthTooShort.into());
|
||||||
|
@ -168,7 +181,7 @@ impl SeedDerivable for AeKey {
|
||||||
hasher.update(seed);
|
hasher.update(seed);
|
||||||
let result = hasher.finalize();
|
let result = hasher.finalize();
|
||||||
|
|
||||||
Ok(Self(result[..16].try_into()?))
|
Ok(Self(result[..AE_KEY_LEN].try_into()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_seed_and_derivation_path(
|
fn from_seed_and_derivation_path(
|
||||||
|
@ -191,8 +204,8 @@ impl SeedDerivable for AeKey {
|
||||||
|
|
||||||
/// For the purpose of encrypting balances for the spl token accounts, the nonce and ciphertext
|
/// For the purpose of encrypting balances for the spl token accounts, the nonce and ciphertext
|
||||||
/// sizes should always be fixed.
|
/// sizes should always be fixed.
|
||||||
type Nonce = [u8; 12];
|
type Nonce = [u8; NONCE_LEN];
|
||||||
type Ciphertext = [u8; 24];
|
type Ciphertext = [u8; CIPHERTEXT_LEN];
|
||||||
|
|
||||||
/// Authenticated encryption nonce and ciphertext
|
/// Authenticated encryption nonce and ciphertext
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
|
@ -205,20 +218,20 @@ impl AeCiphertext {
|
||||||
AuthenticatedEncryption::decrypt(key, self)
|
AuthenticatedEncryption::decrypt(key, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 36] {
|
pub fn to_bytes(&self) -> [u8; AE_CIPHERTEXT_LEN] {
|
||||||
let mut buf = [0_u8; 36];
|
let mut buf = [0_u8; AE_CIPHERTEXT_LEN];
|
||||||
buf[..12].copy_from_slice(&self.nonce);
|
buf[..NONCE_LEN].copy_from_slice(&self.nonce);
|
||||||
buf[12..].copy_from_slice(&self.ciphertext);
|
buf[NONCE_LEN..].copy_from_slice(&self.ciphertext);
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<AeCiphertext> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<AeCiphertext> {
|
||||||
if bytes.len() != 36 {
|
if bytes.len() != AE_CIPHERTEXT_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nonce = bytes[..32].try_into().ok()?;
|
let nonce = bytes[..NONCE_LEN].try_into().ok()?;
|
||||||
let ciphertext = bytes[32..].try_into().ok()?;
|
let ciphertext = bytes[NONCE_LEN..].try_into().ok()?;
|
||||||
|
|
||||||
Some(AeCiphertext { nonce, ciphertext })
|
Some(AeCiphertext { nonce, ciphertext })
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#![cfg(not(target_os = "solana"))]
|
#![cfg(not(target_os = "solana"))]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
crate::RISTRETTO_POINT_LEN,
|
||||||
curve25519_dalek::{
|
curve25519_dalek::{
|
||||||
constants::RISTRETTO_BASEPOINT_POINT as G,
|
constants::RISTRETTO_BASEPOINT_POINT as G,
|
||||||
ristretto::RistrettoPoint,
|
ristretto::RistrettoPoint,
|
||||||
|
@ -32,6 +33,9 @@ use {
|
||||||
const TWO16: u64 = 65536; // 2^16
|
const TWO16: u64 = 65536; // 2^16
|
||||||
const TWO17: u64 = 131072; // 2^17
|
const TWO17: u64 = 131072; // 2^17
|
||||||
|
|
||||||
|
/// Maximum number of threads permitted for discrete log computation
|
||||||
|
const MAX_THREAD: usize = 65536;
|
||||||
|
|
||||||
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum DiscreteLogError {
|
pub enum DiscreteLogError {
|
||||||
#[error("discrete log number of threads not power-of-two")]
|
#[error("discrete log number of threads not power-of-two")]
|
||||||
|
@ -61,7 +65,7 @@ pub struct DiscreteLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub struct DecodePrecomputation(HashMap<[u8; 32], u16>);
|
pub struct DecodePrecomputation(HashMap<[u8; RISTRETTO_POINT_LEN], u16>);
|
||||||
|
|
||||||
/// Builds a HashMap of 2^16 elements
|
/// Builds a HashMap of 2^16 elements
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -110,7 +114,7 @@ impl DiscreteLog {
|
||||||
/// Adjusts number of threads in a discrete log instance.
|
/// Adjusts number of threads in a discrete log instance.
|
||||||
pub fn num_threads(&mut self, num_threads: usize) -> Result<(), DiscreteLogError> {
|
pub fn num_threads(&mut self, num_threads: usize) -> Result<(), DiscreteLogError> {
|
||||||
// number of threads must be a positive power-of-two integer
|
// number of threads must be a positive power-of-two integer
|
||||||
if num_threads == 0 || (num_threads & (num_threads - 1)) != 0 || num_threads > 65536 {
|
if num_threads == 0 || (num_threads & (num_threads - 1)) != 0 || num_threads > MAX_THREAD {
|
||||||
return Err(DiscreteLogError::DiscreteLogThreads);
|
return Err(DiscreteLogError::DiscreteLogThreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,14 @@
|
||||||
//! discrete log to recover the originally encrypted value.
|
//! discrete log to recover the originally encrypted value.
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::encryption::{
|
crate::{
|
||||||
|
encryption::{
|
||||||
discrete_log::DiscreteLog,
|
discrete_log::DiscreteLog,
|
||||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening, G, H},
|
pedersen::{
|
||||||
|
Pedersen, PedersenCommitment, PedersenOpening, G, H, PEDERSEN_COMMITMENT_LEN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RISTRETTO_POINT_LEN, SCALAR_LEN,
|
||||||
},
|
},
|
||||||
base64::{prelude::BASE64_STANDARD, Engine},
|
base64::{prelude::BASE64_STANDARD, Engine},
|
||||||
core::ops::{Add, Mul, Sub},
|
core::ops::{Add, Mul, Sub},
|
||||||
|
@ -50,6 +55,21 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a decrypt handle
|
||||||
|
const DECRYPT_HANDLE_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal ciphertext
|
||||||
|
const ELGAMAL_CIPHERTEXT_LEN: usize = PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal public key
|
||||||
|
const ELGAMAL_PUBKEY_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal secret key
|
||||||
|
const ELGAMAL_SECRET_KEY_LEN: usize = SCALAR_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal keypair
|
||||||
|
const ELGAMAL_KEYPAIR_LEN: usize = ELGAMAL_PUBKEY_LEN + ELGAMAL_SECRET_KEY_LEN;
|
||||||
|
|
||||||
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
#[derive(Error, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum ElGamalError {
|
pub enum ElGamalError {
|
||||||
#[error("key derivation method not supported")]
|
#[error("key derivation method not supported")]
|
||||||
|
@ -209,21 +229,21 @@ impl ElGamalKeypair {
|
||||||
&self.secret
|
&self.secret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 64] {
|
pub fn to_bytes(&self) -> [u8; ELGAMAL_KEYPAIR_LEN] {
|
||||||
let mut bytes = [0u8; 64];
|
let mut bytes = [0u8; ELGAMAL_KEYPAIR_LEN];
|
||||||
bytes[..32].copy_from_slice(&self.public.to_bytes());
|
bytes[..ELGAMAL_PUBKEY_LEN].copy_from_slice(&self.public.to_bytes());
|
||||||
bytes[32..].copy_from_slice(self.secret.as_bytes());
|
bytes[ELGAMAL_PUBKEY_LEN..].copy_from_slice(self.secret.as_bytes());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
if bytes.len() != 64 {
|
if bytes.len() != ELGAMAL_KEYPAIR_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
public: ElGamalPubkey::from_bytes(&bytes[..32])?,
|
public: ElGamalPubkey::from_bytes(&bytes[..ELGAMAL_PUBKEY_LEN])?,
|
||||||
secret: ElGamalSecretKey::from_bytes(bytes[32..].try_into().ok()?)?,
|
secret: ElGamalSecretKey::from_bytes(bytes[ELGAMAL_PUBKEY_LEN..].try_into().ok()?)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,12 +337,12 @@ impl ElGamalPubkey {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; ELGAMAL_PUBKEY_LEN] {
|
||||||
self.0.compress().to_bytes()
|
self.0.compress().to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalPubkey> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalPubkey> {
|
||||||
if bytes.len() != 32 {
|
if bytes.len() != ELGAMAL_PUBKEY_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +448,7 @@ impl ElGamalSecretKey {
|
||||||
|
|
||||||
/// Derive an ElGamal secret key from an entropy seed.
|
/// Derive an ElGamal secret key from an entropy seed.
|
||||||
pub fn from_seed(seed: &[u8]) -> Result<Self, ElGamalError> {
|
pub fn from_seed(seed: &[u8]) -> Result<Self, ElGamalError> {
|
||||||
const MINIMUM_SEED_LEN: usize = 32;
|
const MINIMUM_SEED_LEN: usize = ELGAMAL_SECRET_KEY_LEN;
|
||||||
|
|
||||||
if seed.len() < MINIMUM_SEED_LEN {
|
if seed.len() < MINIMUM_SEED_LEN {
|
||||||
return Err(ElGamalError::SeedLengthTooShort);
|
return Err(ElGamalError::SeedLengthTooShort);
|
||||||
|
@ -453,11 +473,11 @@ impl ElGamalSecretKey {
|
||||||
ElGamal::decrypt_u32(self, ciphertext)
|
ElGamal::decrypt_u32(self, ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; ELGAMAL_SECRET_KEY_LEN] {
|
||||||
self.0.as_bytes()
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; ELGAMAL_SECRET_KEY_LEN] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,21 +574,21 @@ impl ElGamalCiphertext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 64] {
|
pub fn to_bytes(&self) -> [u8; ELGAMAL_CIPHERTEXT_LEN] {
|
||||||
let mut bytes = [0u8; 64];
|
let mut bytes = [0u8; ELGAMAL_CIPHERTEXT_LEN];
|
||||||
bytes[..32].copy_from_slice(&self.commitment.to_bytes());
|
bytes[..PEDERSEN_COMMITMENT_LEN].copy_from_slice(&self.commitment.to_bytes());
|
||||||
bytes[32..].copy_from_slice(&self.handle.to_bytes());
|
bytes[PEDERSEN_COMMITMENT_LEN..].copy_from_slice(&self.handle.to_bytes());
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalCiphertext> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalCiphertext> {
|
||||||
if bytes.len() != 64 {
|
if bytes.len() != ELGAMAL_CIPHERTEXT_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ElGamalCiphertext {
|
Some(ElGamalCiphertext {
|
||||||
commitment: PedersenCommitment::from_bytes(&bytes[..32])?,
|
commitment: PedersenCommitment::from_bytes(&bytes[..PEDERSEN_COMMITMENT_LEN])?,
|
||||||
handle: DecryptHandle::from_bytes(&bytes[32..])?,
|
handle: DecryptHandle::from_bytes(&bytes[PEDERSEN_COMMITMENT_LEN..])?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,12 +696,12 @@ impl DecryptHandle {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; DECRYPT_HANDLE_LEN] {
|
||||||
self.0.compress().to_bytes()
|
self.0.compress().to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<DecryptHandle> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<DecryptHandle> {
|
||||||
if bytes.len() != 32 {
|
if bytes.len() != DECRYPT_HANDLE_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,14 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::encryption::{
|
crate::{
|
||||||
|
encryption::{
|
||||||
discrete_log::DiscreteLog,
|
discrete_log::DiscreteLog,
|
||||||
elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey},
|
elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey},
|
||||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
|
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
|
||||||
},
|
},
|
||||||
|
RISTRETTO_POINT_LEN,
|
||||||
|
},
|
||||||
curve25519_dalek::scalar::Scalar,
|
curve25519_dalek::scalar::Scalar,
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
@ -163,7 +166,7 @@ impl<const N: usize> GroupedElGamalCiphertext<N> {
|
||||||
/// `(N+1) * 32`.
|
/// `(N+1) * 32`.
|
||||||
fn expected_byte_length() -> usize {
|
fn expected_byte_length() -> usize {
|
||||||
N.checked_add(1)
|
N.checked_add(1)
|
||||||
.and_then(|length| length.checked_mul(32))
|
.and_then(|length| length.checked_mul(RISTRETTO_POINT_LEN))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +184,7 @@ impl<const N: usize> GroupedElGamalCiphertext<N> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut iter = bytes.chunks(32);
|
let mut iter = bytes.chunks(RISTRETTO_POINT_LEN);
|
||||||
let commitment = PedersenCommitment::from_bytes(iter.next()?)?;
|
let commitment = PedersenCommitment::from_bytes(iter.next()?)?;
|
||||||
|
|
||||||
let mut handles = Vec::with_capacity(N);
|
let mut handles = Vec::with_capacity(N);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use {
|
use {
|
||||||
|
crate::{RISTRETTO_POINT_LEN, SCALAR_LEN},
|
||||||
core::ops::{Add, Mul, Sub},
|
core::ops::{Add, Mul, Sub},
|
||||||
curve25519_dalek::{
|
curve25519_dalek::{
|
||||||
constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
|
constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
|
||||||
|
@ -17,6 +18,12 @@ use {
|
||||||
zeroize::Zeroize,
|
zeroize::Zeroize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a Pedersen opening.
|
||||||
|
const PEDERSEN_OPENING_LEN: usize = SCALAR_LEN;
|
||||||
|
|
||||||
|
/// Byte length of a Pedersen commitment.
|
||||||
|
pub(crate) const PEDERSEN_COMMITMENT_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
/// Pedersen base point for encoding messages to be committed.
|
/// Pedersen base point for encoding messages to be committed.
|
||||||
pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
|
pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
|
||||||
|
@ -82,11 +89,11 @@ impl PedersenOpening {
|
||||||
PedersenOpening(Scalar::random(&mut OsRng))
|
PedersenOpening(Scalar::random(&mut OsRng))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; PEDERSEN_OPENING_LEN] {
|
||||||
self.0.as_bytes()
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; PEDERSEN_OPENING_LEN] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,12 +184,12 @@ impl PedersenCommitment {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; PEDERSEN_COMMITMENT_LEN] {
|
||||||
self.0.compress().to_bytes()
|
self.0.compress().to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
|
pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
|
||||||
if bytes.len() != 32 {
|
if bytes.len() != PEDERSEN_COMMITMENT_LEN {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,3 +38,10 @@ pub mod zk_token_elgamal;
|
||||||
pub mod zk_token_proof_instruction;
|
pub mod zk_token_proof_instruction;
|
||||||
pub mod zk_token_proof_program;
|
pub mod zk_token_proof_program;
|
||||||
pub mod zk_token_proof_state;
|
pub mod zk_token_proof_state;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
|
@ -11,7 +11,8 @@ use {
|
||||||
pedersen::{PedersenOpening, G, H},
|
pedersen::{PedersenOpening, G, H},
|
||||||
},
|
},
|
||||||
errors::ProofVerificationError,
|
errors::ProofVerificationError,
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
curve25519_dalek::traits::MultiscalarMul,
|
curve25519_dalek::traits::MultiscalarMul,
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
|
@ -27,6 +28,9 @@ use {
|
||||||
merlin::Transcript,
|
merlin::Transcript,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a ciphertext-ciphertext equality proof.
|
||||||
|
const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN: usize = UNIT_LEN * 7;
|
||||||
|
|
||||||
/// The ciphertext-ciphertext equality proof.
|
/// The ciphertext-ciphertext equality proof.
|
||||||
///
|
///
|
||||||
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
||||||
|
@ -221,30 +225,31 @@ impl CiphertextCiphertextEqualityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 224] {
|
pub fn to_bytes(&self) -> [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 224];
|
let mut buf = [0_u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
|
||||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||||
buf[96..128].copy_from_slice(self.Y_3.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||||
buf[128..160].copy_from_slice(self.z_s.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_2.as_bytes());
|
||||||
buf[160..192].copy_from_slice(self.z_x.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_3.as_bytes());
|
||||||
buf[192..224].copy_from_slice(self.z_r.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.z_s.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z_x.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z_r.as_bytes());
|
||||||
|
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
||||||
if bytes.len() != 224 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
let Y_0 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
let Y_1 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let Y_2 = CompressedRistretto::from_slice(&bytes[64..96]);
|
let Y_2 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let Y_3 = CompressedRistretto::from_slice(&bytes[96..128]);
|
let Y_3 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let z_s = canonical_scalar_from_slice(&bytes[128..160])?;
|
let z_s = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let z_x = canonical_scalar_from_slice(&bytes[160..192])?;
|
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let z_r = canonical_scalar_from_slice(&bytes[192..224])?;
|
let z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
|
|
||||||
Ok(CiphertextCiphertextEqualityProof {
|
Ok(CiphertextCiphertextEqualityProof {
|
||||||
Y_0,
|
Y_0,
|
||||||
|
|
|
@ -16,7 +16,8 @@ use {
|
||||||
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
||||||
},
|
},
|
||||||
errors::ProofVerificationError,
|
errors::ProofVerificationError,
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
curve25519_dalek::traits::MultiscalarMul,
|
curve25519_dalek::traits::MultiscalarMul,
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
|
@ -32,6 +33,9 @@ use {
|
||||||
merlin::Transcript,
|
merlin::Transcript,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a ciphertext-commitment equality proof.
|
||||||
|
const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN: usize = UNIT_LEN * 6;
|
||||||
|
|
||||||
/// Equality proof.
|
/// Equality proof.
|
||||||
///
|
///
|
||||||
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
||||||
|
@ -203,28 +207,26 @@ impl CiphertextCommitmentEqualityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 192] {
|
pub fn to_bytes(&self) -> [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 192];
|
let mut buf = [0_u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||||
buf[96..128].copy_from_slice(self.z_s.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_2.as_bytes());
|
||||||
buf[128..160].copy_from_slice(self.z_x.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.z_s.as_bytes());
|
||||||
buf[160..192].copy_from_slice(self.z_r.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.z_x.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z_r.as_bytes());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
||||||
if bytes.len() != 192 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
let Y_0 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
}
|
let Y_1 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
|
let Y_2 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
let z_s = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_2 = CompressedRistretto::from_slice(&bytes[64..96]);
|
let z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let z_s = canonical_scalar_from_slice(&bytes[96..128])?;
|
|
||||||
let z_x = canonical_scalar_from_slice(&bytes[128..160])?;
|
|
||||||
let z_r = canonical_scalar_from_slice(&bytes[160..192])?;
|
|
||||||
|
|
||||||
Ok(CiphertextCommitmentEqualityProof {
|
Ok(CiphertextCommitmentEqualityProof {
|
||||||
Y_0,
|
Y_0,
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
encryption::pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
encryption::pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
};
|
};
|
||||||
|
@ -32,6 +33,9 @@ use {
|
||||||
subtle::{Choice, ConditionallySelectable, ConstantTimeGreater},
|
subtle::{Choice, ConditionallySelectable, ConstantTimeGreater},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a fee sigma proof.
|
||||||
|
const FEE_SIGMA_PROOF_LEN: usize = UNIT_LEN * 8;
|
||||||
|
|
||||||
/// Fee sigma proof.
|
/// Fee sigma proof.
|
||||||
///
|
///
|
||||||
/// The proof consists of two main components: `fee_max_proof` and `fee_equality_proof`. If the fee
|
/// The proof consists of two main components: `fee_max_proof` and `fee_equality_proof`. If the fee
|
||||||
|
@ -387,33 +391,55 @@ impl FeeSigmaProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 256] {
|
pub fn to_bytes(&self) -> [u8; FEE_SIGMA_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 256];
|
let mut buf = [0_u8; FEE_SIGMA_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.fee_max_proof.Y_max_proof.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.fee_max_proof.z_max_proof.as_bytes());
|
chunks
|
||||||
buf[64..96].copy_from_slice(self.fee_max_proof.c_max_proof.as_bytes());
|
.next()
|
||||||
buf[96..128].copy_from_slice(self.fee_equality_proof.Y_delta.as_bytes());
|
.unwrap()
|
||||||
buf[128..160].copy_from_slice(self.fee_equality_proof.Y_claimed.as_bytes());
|
.copy_from_slice(self.fee_max_proof.Y_max_proof.as_bytes());
|
||||||
buf[160..192].copy_from_slice(self.fee_equality_proof.z_x.as_bytes());
|
chunks
|
||||||
buf[192..224].copy_from_slice(self.fee_equality_proof.z_delta.as_bytes());
|
.next()
|
||||||
buf[224..256].copy_from_slice(self.fee_equality_proof.z_claimed.as_bytes());
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_max_proof.z_max_proof.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_max_proof.c_max_proof.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_equality_proof.Y_delta.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_equality_proof.Y_claimed.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_equality_proof.z_x.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_equality_proof.z_delta.as_bytes());
|
||||||
|
chunks
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(self.fee_equality_proof.z_claimed.as_bytes());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, FeeSigmaProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, FeeSigmaProofError> {
|
||||||
if bytes.len() != 256 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
let Y_max_proof = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
}
|
let z_max_proof = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
|
let c_max_proof = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
|
|
||||||
let Y_max_proof = CompressedRistretto::from_slice(&bytes[..32]);
|
let Y_delta = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let z_max_proof = canonical_scalar_from_slice(&bytes[32..64])?;
|
let Y_claimed = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let c_max_proof = canonical_scalar_from_slice(&bytes[64..96])?;
|
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
|
let z_delta = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_delta = CompressedRistretto::from_slice(&bytes[96..128]);
|
let z_claimed = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_claimed = CompressedRistretto::from_slice(&bytes[128..160]);
|
|
||||||
let z_x = canonical_scalar_from_slice(&bytes[160..192])?;
|
|
||||||
let z_delta = canonical_scalar_from_slice(&bytes[192..224])?;
|
|
||||||
let z_claimed = canonical_scalar_from_slice(&bytes[224..256])?;
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
fee_max_proof: FeeMaxProof {
|
fee_max_proof: FeeMaxProof {
|
||||||
|
|
|
@ -16,7 +16,8 @@ use {
|
||||||
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
||||||
},
|
},
|
||||||
errors::ProofVerificationError,
|
errors::ProofVerificationError,
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
curve25519_dalek::traits::MultiscalarMul,
|
curve25519_dalek::traits::MultiscalarMul,
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
|
@ -32,6 +33,9 @@ use {
|
||||||
merlin::Transcript,
|
merlin::Transcript,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a grouped ciphertext validity proof for 2 handles
|
||||||
|
const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = UNIT_LEN * 5;
|
||||||
|
|
||||||
/// The grouped ciphertext validity proof for 2 handles.
|
/// The grouped ciphertext validity proof for 2 handles.
|
||||||
///
|
///
|
||||||
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
||||||
|
@ -194,26 +198,24 @@ impl GroupedCiphertext2HandlesValidityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 160] {
|
pub fn to_bytes(&self) -> [u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 160];
|
let mut buf = [0_u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||||
buf[96..128].copy_from_slice(self.z_r.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_2.as_bytes());
|
||||||
buf[128..160].copy_from_slice(self.z_x.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.z_r.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z_x.as_bytes());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ValidityProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ValidityProofError> {
|
||||||
if bytes.len() != 160 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
let Y_0 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
}
|
let Y_1 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
|
let Y_2 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
let z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_2 = CompressedRistretto::from_slice(&bytes[64..96]);
|
|
||||||
let z_r = canonical_scalar_from_slice(&bytes[96..128])?;
|
|
||||||
let z_x = canonical_scalar_from_slice(&bytes[128..160])?;
|
|
||||||
|
|
||||||
Ok(GroupedCiphertext2HandlesValidityProof {
|
Ok(GroupedCiphertext2HandlesValidityProof {
|
||||||
Y_0,
|
Y_0,
|
||||||
|
|
|
@ -15,19 +15,36 @@ pub mod pubkey_proof;
|
||||||
pub mod zero_balance_proof;
|
pub mod zero_balance_proof;
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use {crate::errors::ProofVerificationError, curve25519_dalek::scalar::Scalar};
|
use {
|
||||||
|
crate::{errors::ProofVerificationError, RISTRETTO_POINT_LEN, SCALAR_LEN},
|
||||||
|
curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Deserializes an optional slice of bytes to a compressed Ristretto point.
|
||||||
|
///
|
||||||
|
/// This is a helper function for deserializing byte encodings of sigma proofs. It is designed to
|
||||||
|
/// be used with `std::slice::Chunks`.
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
fn canonical_scalar_from_slice(bytes: &[u8]) -> Result<Scalar, ProofVerificationError> {
|
fn ristretto_point_from_optional_slice(
|
||||||
if bytes.len() != 32 {
|
optional_slice: Option<&[u8]>,
|
||||||
return Err(ProofVerificationError::Deserialization);
|
) -> Result<CompressedRistretto, ProofVerificationError> {
|
||||||
|
optional_slice
|
||||||
|
.and_then(|slice| (slice.len() == RISTRETTO_POINT_LEN).then_some(slice))
|
||||||
|
.map(CompressedRistretto::from_slice)
|
||||||
|
.ok_or(ProofVerificationError::Deserialization)
|
||||||
}
|
}
|
||||||
|
|
||||||
let scalar_bytes = bytes[..32]
|
/// Deserializes an optional slice of bytes to a scalar.
|
||||||
.try_into()
|
///
|
||||||
.map_err(|_| ProofVerificationError::Deserialization)?;
|
/// This is a helper function for deserializing byte encodings of sigma proofs. It is designed to
|
||||||
|
/// be used with `std::slice::Chunks`.
|
||||||
let scalar = Scalar::from_canonical_bytes(scalar_bytes)
|
#[cfg(not(target_os = "solana"))]
|
||||||
.ok_or(ProofVerificationError::Deserialization)?;
|
fn canonical_scalar_from_optional_slice(
|
||||||
Ok(scalar)
|
optional_slice: Option<&[u8]>,
|
||||||
|
) -> Result<Scalar, ProofVerificationError> {
|
||||||
|
optional_slice
|
||||||
|
.and_then(|slice| (slice.len() == SCALAR_LEN).then_some(slice)) // if chunk is the wrong length, convert to None
|
||||||
|
.and_then(|slice| slice.try_into().ok()) // convert to array
|
||||||
|
.and_then(Scalar::from_canonical_bytes)
|
||||||
|
.ok_or(ProofVerificationError::Deserialization)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@ use {
|
||||||
elgamal::{ElGamalKeypair, ElGamalPubkey},
|
elgamal::{ElGamalKeypair, ElGamalPubkey},
|
||||||
pedersen::H,
|
pedersen::H,
|
||||||
},
|
},
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
zeroize::Zeroize,
|
zeroize::Zeroize,
|
||||||
|
@ -28,6 +29,9 @@ use {
|
||||||
merlin::Transcript,
|
merlin::Transcript,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a public key validity proof.
|
||||||
|
const PUBKEY_VALIDITY_PROOF_LEN: usize = UNIT_LEN * 2;
|
||||||
|
|
||||||
/// Public-key proof.
|
/// Public-key proof.
|
||||||
///
|
///
|
||||||
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
||||||
|
@ -116,21 +120,18 @@ impl PubkeyValidityProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 64] {
|
pub fn to_bytes(&self) -> [u8; PUBKEY_VALIDITY_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 64];
|
let mut buf = [0_u8; PUBKEY_VALIDITY_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.Y.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.z.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z.as_bytes());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyValidityProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyValidityProofError> {
|
||||||
if bytes.len() != 64 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
let Y = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
}
|
let z = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
|
|
||||||
let Y = CompressedRistretto::from_slice(&bytes[..32]);
|
|
||||||
let z = canonical_scalar_from_slice(&bytes[32..64])?;
|
|
||||||
|
|
||||||
Ok(PubkeyValidityProof { Y, z })
|
Ok(PubkeyValidityProof { Y, z })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ use {
|
||||||
pedersen::H,
|
pedersen::H,
|
||||||
},
|
},
|
||||||
errors::ProofVerificationError,
|
errors::ProofVerificationError,
|
||||||
sigma_proofs::canonical_scalar_from_slice,
|
sigma_proofs::{canonical_scalar_from_optional_slice, ristretto_point_from_optional_slice},
|
||||||
|
UNIT_LEN,
|
||||||
},
|
},
|
||||||
curve25519_dalek::traits::MultiscalarMul,
|
curve25519_dalek::traits::MultiscalarMul,
|
||||||
rand::rngs::OsRng,
|
rand::rngs::OsRng,
|
||||||
|
@ -27,6 +28,9 @@ use {
|
||||||
merlin::Transcript,
|
merlin::Transcript,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of a zero-balance proof.
|
||||||
|
const ZERO_BALANCE_PROOF_LEN: usize = UNIT_LEN * 3;
|
||||||
|
|
||||||
/// Zero-balance proof.
|
/// Zero-balance proof.
|
||||||
///
|
///
|
||||||
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
/// Contains all the elliptic curve and scalar components that make up the sigma protocol.
|
||||||
|
@ -152,23 +156,20 @@ impl ZeroBalanceProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_bytes(&self) -> [u8; 96] {
|
pub fn to_bytes(&self) -> [u8; ZERO_BALANCE_PROOF_LEN] {
|
||||||
let mut buf = [0_u8; 96];
|
let mut buf = [0_u8; ZERO_BALANCE_PROOF_LEN];
|
||||||
buf[..32].copy_from_slice(self.Y_P.as_bytes());
|
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||||
buf[32..64].copy_from_slice(self.Y_D.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_P.as_bytes());
|
||||||
buf[64..96].copy_from_slice(self.z.as_bytes());
|
chunks.next().unwrap().copy_from_slice(self.Y_D.as_bytes());
|
||||||
|
chunks.next().unwrap().copy_from_slice(self.z.as_bytes());
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ZeroBalanceProofError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ZeroBalanceProofError> {
|
||||||
if bytes.len() != 96 {
|
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
let Y_P = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
}
|
let Y_D = ristretto_point_from_optional_slice(chunks.next())?;
|
||||||
|
let z = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||||
let Y_P = CompressedRistretto::from_slice(&bytes[..32]);
|
|
||||||
let Y_D = CompressedRistretto::from_slice(&bytes[32..64]);
|
|
||||||
let z = canonical_scalar_from_slice(&bytes[64..96])?;
|
|
||||||
|
|
||||||
Ok(ZeroBalanceProof { Y_P, Y_D, z })
|
Ok(ZeroBalanceProof { Y_P, Y_D, z })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ use {
|
||||||
std::fmt,
|
std::fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Byte length of an authenticated encryption ciphertext
|
||||||
|
const AE_CIPHERTEXT_LEN: usize = 36;
|
||||||
|
|
||||||
/// The `AeCiphertext` type as a `Pod`.
|
/// The `AeCiphertext` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct AeCiphertext(pub [u8; 36]);
|
pub struct AeCiphertext(pub [u8; AE_CIPHERTEXT_LEN]);
|
||||||
|
|
||||||
// `AeCiphertext` is a wrapper type for a byte array, which is both `Pod` and `Zeroable`. However,
|
// `AeCiphertext` 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
|
// the marker traits `bytemuck::Pod` and `bytemuck::Zeroable` can only be derived for power-of-two
|
||||||
|
|
|
@ -1,20 +1,32 @@
|
||||||
//! Plain Old Data types for the ElGamal encryption scheme.
|
//! Plain Old Data types for the ElGamal encryption scheme.
|
||||||
|
|
||||||
use {
|
|
||||||
crate::zk_token_elgamal::pod::{Pod, Zeroable},
|
|
||||||
base64::{prelude::BASE64_STANDARD, Engine},
|
|
||||||
std::fmt,
|
|
||||||
};
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use {
|
use {
|
||||||
crate::{encryption::elgamal as decoded, errors::ProofError},
|
crate::{encryption::elgamal as decoded, errors::ProofError},
|
||||||
curve25519_dalek::ristretto::CompressedRistretto,
|
curve25519_dalek::ristretto::CompressedRistretto,
|
||||||
};
|
};
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
zk_token_elgamal::pod::{pedersen::PEDERSEN_COMMITMENT_LEN, Pod, Zeroable},
|
||||||
|
RISTRETTO_POINT_LEN,
|
||||||
|
},
|
||||||
|
base64::{prelude::BASE64_STANDARD, Engine},
|
||||||
|
std::fmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal public key
|
||||||
|
const ELGAMAL_PUBKEY_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
|
/// Byte length of a decrypt handle
|
||||||
|
pub(crate) const DECRYPT_HANDLE_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an ElGamal ciphertext
|
||||||
|
const ELGAMAL_CIPHERTEXT_LEN: usize = PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN;
|
||||||
|
|
||||||
/// The `ElGamalCiphertext` type as a `Pod`.
|
/// The `ElGamalCiphertext` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ElGamalCiphertext(pub [u8; 64]);
|
pub struct ElGamalCiphertext(pub [u8; ELGAMAL_CIPHERTEXT_LEN]);
|
||||||
|
|
||||||
impl fmt::Debug for ElGamalCiphertext {
|
impl fmt::Debug for ElGamalCiphertext {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -53,7 +65,7 @@ impl TryFrom<ElGamalCiphertext> for decoded::ElGamalCiphertext {
|
||||||
/// The `ElGamalPubkey` type as a `Pod`.
|
/// The `ElGamalPubkey` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ElGamalPubkey(pub [u8; 32]);
|
pub struct ElGamalPubkey(pub [u8; ELGAMAL_PUBKEY_LEN]);
|
||||||
|
|
||||||
impl fmt::Debug for ElGamalPubkey {
|
impl fmt::Debug for ElGamalPubkey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -86,7 +98,7 @@ impl TryFrom<ElGamalPubkey> for decoded::ElGamalPubkey {
|
||||||
/// The `DecryptHandle` type as a `Pod`.
|
/// The `DecryptHandle` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct DecryptHandle(pub [u8; 32]);
|
pub struct DecryptHandle(pub [u8; DECRYPT_HANDLE_LEN]);
|
||||||
|
|
||||||
impl fmt::Debug for DecryptHandle {
|
impl fmt::Debug for DecryptHandle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
|
@ -3,14 +3,24 @@
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use crate::{encryption::grouped_elgamal::GroupedElGamalCiphertext, errors::ProofError};
|
use crate::{encryption::grouped_elgamal::GroupedElGamalCiphertext, errors::ProofError};
|
||||||
use {
|
use {
|
||||||
crate::zk_token_elgamal::pod::{Pod, Zeroable},
|
crate::zk_token_elgamal::pod::{
|
||||||
|
elgamal::DECRYPT_HANDLE_LEN, pedersen::PEDERSEN_COMMITMENT_LEN, Pod, Zeroable,
|
||||||
|
},
|
||||||
std::fmt,
|
std::fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 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`
|
/// The `GroupedElGamalCiphertext` type with two decryption handles as a `Pod`
|
||||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct GroupedElGamalCiphertext2Handles(pub [u8; 96]);
|
pub struct GroupedElGamalCiphertext2Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES]);
|
||||||
|
|
||||||
impl fmt::Debug for GroupedElGamalCiphertext2Handles {
|
impl fmt::Debug for GroupedElGamalCiphertext2Handles {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
@ -42,7 +52,7 @@ impl TryFrom<GroupedElGamalCiphertext2Handles> for GroupedElGamalCiphertext<2> {
|
||||||
/// The `GroupedElGamalCiphertext` type with three decryption handles as a `Pod`
|
/// The `GroupedElGamalCiphertext` type with three decryption handles as a `Pod`
|
||||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct GroupedElGamalCiphertext3Handles(pub [u8; 128]);
|
pub struct GroupedElGamalCiphertext3Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES]);
|
||||||
|
|
||||||
impl fmt::Debug for GroupedElGamalCiphertext3Handles {
|
impl fmt::Debug for GroupedElGamalCiphertext3Handles {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
//! Plain Old Data type for the Pedersen commitment scheme.
|
//! Plain Old Data type for the Pedersen commitment scheme.
|
||||||
|
|
||||||
use {
|
|
||||||
crate::zk_token_elgamal::pod::{Pod, Zeroable},
|
|
||||||
std::fmt,
|
|
||||||
};
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use {
|
use {
|
||||||
crate::{encryption::pedersen as decoded, errors::ProofError},
|
crate::{encryption::pedersen as decoded, errors::ProofError},
|
||||||
curve25519_dalek::ristretto::CompressedRistretto,
|
curve25519_dalek::ristretto::CompressedRistretto,
|
||||||
};
|
};
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
zk_token_elgamal::pod::{Pod, Zeroable},
|
||||||
|
RISTRETTO_POINT_LEN,
|
||||||
|
},
|
||||||
|
std::fmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Byte length of a Pedersen commitment
|
||||||
|
pub(crate) const PEDERSEN_COMMITMENT_LEN: usize = RISTRETTO_POINT_LEN;
|
||||||
|
|
||||||
/// The `PedersenCommitment` type as a `Pod`.
|
/// The `PedersenCommitment` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct PedersenCommitment(pub [u8; 32]);
|
pub struct PedersenCommitment(pub [u8; PEDERSEN_COMMITMENT_LEN]);
|
||||||
|
|
||||||
impl fmt::Debug for PedersenCommitment {
|
impl fmt::Debug for PedersenCommitment {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
|
@ -1,35 +1,58 @@
|
||||||
//! Plain Old Data types for range proofs.
|
//! Plain Old Data types for range proofs.
|
||||||
|
|
||||||
use crate::zk_token_elgamal::pod::{Pod, Zeroable};
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::ProofVerificationError,
|
errors::ProofVerificationError,
|
||||||
range_proof::{self as decoded, errors::RangeProofError},
|
range_proof::{self as decoded, errors::RangeProofError},
|
||||||
|
UNIT_LEN,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
zk_token_elgamal::pod::{Pod, Zeroable},
|
||||||
|
RISTRETTO_POINT_LEN, SCALAR_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Byte length of a range proof excluding the inner-product proof component
|
||||||
|
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
|
||||||
|
const INNER_PRODUCT_PROOF_U64_LEN: usize = 448;
|
||||||
|
|
||||||
|
/// Byte length of a range proof for an unsigned 64-bit number
|
||||||
|
const RANGE_PROOF_U64_LEN: usize =
|
||||||
|
INNER_PRODUCT_PROOF_U64_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an inner-product proof for a vector of length 128
|
||||||
|
const INNER_PRODUCT_PROOF_U128_LEN: usize = 512;
|
||||||
|
|
||||||
|
/// Byte length of a range proof for an unsigned 128-bit number
|
||||||
|
const RANGE_PROOF_U128_LEN: usize =
|
||||||
|
INNER_PRODUCT_PROOF_U128_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
|
||||||
|
|
||||||
|
/// Byte length of an inner-product proof for a vector of length 256
|
||||||
|
const INNER_PRODUCT_PROOF_U256_LEN: usize = 576;
|
||||||
|
|
||||||
|
/// Byte length of a range proof for an unsigned 256-bit number
|
||||||
|
const RANGE_PROOF_U256_LEN: usize =
|
||||||
|
INNER_PRODUCT_PROOF_U256_LEN + RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN;
|
||||||
|
|
||||||
/// The `RangeProof` type as a `Pod` restricted to proofs on 64-bit numbers.
|
/// The `RangeProof` type as a `Pod` restricted to proofs on 64-bit numbers.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct RangeProofU64(pub [u8; 672]);
|
pub struct RangeProofU64(pub [u8; RANGE_PROOF_U64_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl TryFrom<decoded::RangeProof> for RangeProofU64 {
|
impl TryFrom<decoded::RangeProof> for RangeProofU64 {
|
||||||
type Error = RangeProofError;
|
type Error = RangeProofError;
|
||||||
|
|
||||||
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
||||||
if decoded_proof.ipp_proof.serialized_size() != 448 {
|
if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U64_LEN {
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
return Err(ProofVerificationError::Deserialization.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = [0_u8; 672];
|
let mut buf = [0_u8; RANGE_PROOF_U64_LEN];
|
||||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U64_LEN]
|
||||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_bytes());
|
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||||
buf[96..128].copy_from_slice(decoded_proof.T_2.as_bytes());
|
|
||||||
buf[128..160].copy_from_slice(decoded_proof.t_x.as_bytes());
|
|
||||||
buf[160..192].copy_from_slice(decoded_proof.t_x_blinding.as_bytes());
|
|
||||||
buf[192..224].copy_from_slice(decoded_proof.e_blinding.as_bytes());
|
|
||||||
buf[224..672].copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
|
||||||
Ok(RangeProofU64(buf))
|
Ok(RangeProofU64(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,26 +69,21 @@ impl TryFrom<RangeProofU64> for decoded::RangeProof {
|
||||||
/// The `RangeProof` type as a `Pod` restricted to proofs on 128-bit numbers.
|
/// The `RangeProof` type as a `Pod` restricted to proofs on 128-bit numbers.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct RangeProofU128(pub [u8; 736]);
|
pub struct RangeProofU128(pub [u8; RANGE_PROOF_U128_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl TryFrom<decoded::RangeProof> for RangeProofU128 {
|
impl TryFrom<decoded::RangeProof> for RangeProofU128 {
|
||||||
type Error = RangeProofError;
|
type Error = RangeProofError;
|
||||||
|
|
||||||
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
||||||
if decoded_proof.ipp_proof.serialized_size() != 512 {
|
if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U128_LEN {
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
return Err(ProofVerificationError::Deserialization.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = [0_u8; 736];
|
let mut buf = [0_u8; RANGE_PROOF_U128_LEN];
|
||||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U128_LEN]
|
||||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_bytes());
|
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||||
buf[96..128].copy_from_slice(decoded_proof.T_2.as_bytes());
|
|
||||||
buf[128..160].copy_from_slice(decoded_proof.t_x.as_bytes());
|
|
||||||
buf[160..192].copy_from_slice(decoded_proof.t_x_blinding.as_bytes());
|
|
||||||
buf[192..224].copy_from_slice(decoded_proof.e_blinding.as_bytes());
|
|
||||||
buf[224..736].copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
|
||||||
Ok(RangeProofU128(buf))
|
Ok(RangeProofU128(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,26 +100,21 @@ impl TryFrom<RangeProofU128> for decoded::RangeProof {
|
||||||
/// The `RangeProof` type as a `Pod` restricted to proofs on 256-bit numbers.
|
/// The `RangeProof` type as a `Pod` restricted to proofs on 256-bit numbers.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct RangeProofU256(pub [u8; 800]);
|
pub struct RangeProofU256(pub [u8; RANGE_PROOF_U256_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl TryFrom<decoded::RangeProof> for RangeProofU256 {
|
impl TryFrom<decoded::RangeProof> for RangeProofU256 {
|
||||||
type Error = RangeProofError;
|
type Error = RangeProofError;
|
||||||
|
|
||||||
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
fn try_from(decoded_proof: decoded::RangeProof) -> Result<Self, Self::Error> {
|
||||||
if decoded_proof.ipp_proof.serialized_size() != 576 {
|
if decoded_proof.ipp_proof.serialized_size() != INNER_PRODUCT_PROOF_U256_LEN {
|
||||||
return Err(ProofVerificationError::Deserialization.into());
|
return Err(ProofVerificationError::Deserialization.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = [0_u8; 800];
|
let mut buf = [0_u8; RANGE_PROOF_U256_LEN];
|
||||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U256_LEN]
|
||||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_bytes());
|
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||||
buf[96..128].copy_from_slice(decoded_proof.T_2.as_bytes());
|
|
||||||
buf[128..160].copy_from_slice(decoded_proof.t_x.as_bytes());
|
|
||||||
buf[160..192].copy_from_slice(decoded_proof.t_x_blinding.as_bytes());
|
|
||||||
buf[192..224].copy_from_slice(decoded_proof.e_blinding.as_bytes());
|
|
||||||
buf[224..800].copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
|
||||||
Ok(RangeProofU256(buf))
|
Ok(RangeProofU256(buf))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,6 +128,24 @@ impl TryFrom<RangeProofU256> for decoded::RangeProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "solana"))]
|
||||||
|
fn copy_range_proof_modulo_inner_product_proof(proof: &decoded::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 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
|
// 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.
|
// length byte arrays. Directly implement these traits for the range proof pod types.
|
||||||
|
|
|
@ -12,10 +12,31 @@ use crate::sigma_proofs::{
|
||||||
};
|
};
|
||||||
use crate::zk_token_elgamal::pod::{Pod, Zeroable};
|
use crate::zk_token_elgamal::pod::{Pod, Zeroable};
|
||||||
|
|
||||||
|
/// Byte length of a ciphertext-commitment equality proof
|
||||||
|
const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN: usize = 192;
|
||||||
|
|
||||||
|
/// Byte length of a ciphertext-ciphertext equality proof
|
||||||
|
const CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN: usize = 224;
|
||||||
|
|
||||||
|
/// Byte length of a grouped ciphertext for 2 handles validity proof
|
||||||
|
const GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160;
|
||||||
|
|
||||||
|
/// Byte length of a batched grouped ciphertext for 2 handles validity proof
|
||||||
|
const BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN: usize = 160;
|
||||||
|
|
||||||
|
/// Byte length of a zero-balance proof
|
||||||
|
const ZERO_BALANCE_PROOF_LEN: usize = 96;
|
||||||
|
|
||||||
|
/// Byte length of a fee sigma proof
|
||||||
|
const FEE_SIGMA_PROOF_LEN: usize = 256;
|
||||||
|
|
||||||
|
/// Byte length of a public key validity proof
|
||||||
|
const PUBKEY_VALIDITY_PROOF_LEN: usize = 64;
|
||||||
|
|
||||||
/// The `CiphertextCommitmentEqualityProof` type as a `Pod`.
|
/// The `CiphertextCommitmentEqualityProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct CiphertextCommitmentEqualityProof(pub [u8; 192]);
|
pub struct CiphertextCommitmentEqualityProof(pub [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedCiphertextCommitmentEqualityProof> for CiphertextCommitmentEqualityProof {
|
impl From<DecodedCiphertextCommitmentEqualityProof> for CiphertextCommitmentEqualityProof {
|
||||||
|
@ -36,7 +57,7 @@ impl TryFrom<CiphertextCommitmentEqualityProof> for DecodedCiphertextCommitmentE
|
||||||
/// The `CiphertextCiphertextEqualityProof` type as a `Pod`.
|
/// The `CiphertextCiphertextEqualityProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct CiphertextCiphertextEqualityProof(pub [u8; 224]);
|
pub struct CiphertextCiphertextEqualityProof(pub [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedCiphertextCiphertextEqualityProof> for CiphertextCiphertextEqualityProof {
|
impl From<DecodedCiphertextCiphertextEqualityProof> for CiphertextCiphertextEqualityProof {
|
||||||
|
@ -57,7 +78,9 @@ impl TryFrom<CiphertextCiphertextEqualityProof> for DecodedCiphertextCiphertextE
|
||||||
/// The `GroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
/// The `GroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct GroupedCiphertext2HandlesValidityProof(pub [u8; 160]);
|
pub struct GroupedCiphertext2HandlesValidityProof(
|
||||||
|
pub [u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN],
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedGroupedCiphertext2HandlesValidityProof>
|
impl From<DecodedGroupedCiphertext2HandlesValidityProof>
|
||||||
|
@ -82,7 +105,9 @@ impl TryFrom<GroupedCiphertext2HandlesValidityProof>
|
||||||
/// The `BatchedGroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
/// The `BatchedGroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct BatchedGroupedCiphertext2HandlesValidityProof(pub [u8; 160]);
|
pub struct BatchedGroupedCiphertext2HandlesValidityProof(
|
||||||
|
pub [u8; BATCHED_GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN],
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedBatchedGroupedCiphertext2HandlesValidityProof>
|
impl From<DecodedBatchedGroupedCiphertext2HandlesValidityProof>
|
||||||
|
@ -109,7 +134,7 @@ impl TryFrom<BatchedGroupedCiphertext2HandlesValidityProof>
|
||||||
/// The `ZeroBalanceProof` type as a `Pod`.
|
/// The `ZeroBalanceProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ZeroBalanceProof(pub [u8; 96]);
|
pub struct ZeroBalanceProof(pub [u8; ZERO_BALANCE_PROOF_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedZeroBalanceProof> for ZeroBalanceProof {
|
impl From<DecodedZeroBalanceProof> for ZeroBalanceProof {
|
||||||
|
@ -130,7 +155,7 @@ impl TryFrom<ZeroBalanceProof> for DecodedZeroBalanceProof {
|
||||||
/// The `FeeSigmaProof` type as a `Pod`.
|
/// The `FeeSigmaProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct FeeSigmaProof(pub [u8; 256]);
|
pub struct FeeSigmaProof(pub [u8; FEE_SIGMA_PROOF_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedFeeSigmaProof> for FeeSigmaProof {
|
impl From<DecodedFeeSigmaProof> for FeeSigmaProof {
|
||||||
|
@ -151,7 +176,7 @@ impl TryFrom<FeeSigmaProof> for DecodedFeeSigmaProof {
|
||||||
/// The `PubkeyValidityProof` type as a `Pod`.
|
/// The `PubkeyValidityProof` type as a `Pod`.
|
||||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct PubkeyValidityProof(pub [u8; 64]);
|
pub struct PubkeyValidityProof(pub [u8; PUBKEY_VALIDITY_PROOF_LEN]);
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<DecodedPubkeyValidityProof> for PubkeyValidityProof {
|
impl From<DecodedPubkeyValidityProof> for PubkeyValidityProof {
|
||||||
|
|
Loading…
Reference in New Issue