[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,
|
||||
};
|
||||
|
||||
/// 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)]
|
||||
pub enum AuthenticatedEncryptionError {
|
||||
#[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`.
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
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
|
||||
|
@ -54,7 +67,7 @@ impl AuthenticatedEncryption {
|
|||
#[cfg(not(target_os = "solana"))]
|
||||
fn encrypt(key: &AeKey, balance: u64) -> AeCiphertext {
|
||||
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.
|
||||
let ciphertext = Aes128GcmSiv::new(&key.0.into())
|
||||
|
@ -86,7 +99,7 @@ impl AuthenticatedEncryption {
|
|||
}
|
||||
|
||||
#[derive(Debug, Zeroize)]
|
||||
pub struct AeKey([u8; 16]);
|
||||
pub struct AeKey([u8; AE_KEY_LEN]);
|
||||
impl AeKey {
|
||||
/// Deterministically derives an authenticated encryption key from a Solana signer and a public
|
||||
/// seed.
|
||||
|
@ -144,7 +157,7 @@ impl AeKey {
|
|||
|
||||
impl EncodableKey for AeKey {
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -158,7 +171,7 @@ impl EncodableKey for AeKey {
|
|||
|
||||
impl SeedDerivable for AeKey {
|
||||
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 {
|
||||
return Err(AuthenticatedEncryptionError::SeedLengthTooShort.into());
|
||||
|
@ -168,7 +181,7 @@ impl SeedDerivable for AeKey {
|
|||
hasher.update(seed);
|
||||
let result = hasher.finalize();
|
||||
|
||||
Ok(Self(result[..16].try_into()?))
|
||||
Ok(Self(result[..AE_KEY_LEN].try_into()?))
|
||||
}
|
||||
|
||||
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
|
||||
/// sizes should always be fixed.
|
||||
type Nonce = [u8; 12];
|
||||
type Ciphertext = [u8; 24];
|
||||
type Nonce = [u8; NONCE_LEN];
|
||||
type Ciphertext = [u8; CIPHERTEXT_LEN];
|
||||
|
||||
/// Authenticated encryption nonce and ciphertext
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
@ -205,20 +218,20 @@ impl AeCiphertext {
|
|||
AuthenticatedEncryption::decrypt(key, self)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 36] {
|
||||
let mut buf = [0_u8; 36];
|
||||
buf[..12].copy_from_slice(&self.nonce);
|
||||
buf[12..].copy_from_slice(&self.ciphertext);
|
||||
pub fn to_bytes(&self) -> [u8; AE_CIPHERTEXT_LEN] {
|
||||
let mut buf = [0_u8; AE_CIPHERTEXT_LEN];
|
||||
buf[..NONCE_LEN].copy_from_slice(&self.nonce);
|
||||
buf[NONCE_LEN..].copy_from_slice(&self.ciphertext);
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<AeCiphertext> {
|
||||
if bytes.len() != 36 {
|
||||
if bytes.len() != AE_CIPHERTEXT_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
let nonce = bytes[..32].try_into().ok()?;
|
||||
let ciphertext = bytes[32..].try_into().ok()?;
|
||||
let nonce = bytes[..NONCE_LEN].try_into().ok()?;
|
||||
let ciphertext = bytes[NONCE_LEN..].try_into().ok()?;
|
||||
|
||||
Some(AeCiphertext { nonce, ciphertext })
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#![cfg(not(target_os = "solana"))]
|
||||
|
||||
use {
|
||||
crate::RISTRETTO_POINT_LEN,
|
||||
curve25519_dalek::{
|
||||
constants::RISTRETTO_BASEPOINT_POINT as G,
|
||||
ristretto::RistrettoPoint,
|
||||
|
@ -32,6 +33,9 @@ use {
|
|||
const TWO16: u64 = 65536; // 2^16
|
||||
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)]
|
||||
pub enum DiscreteLogError {
|
||||
#[error("discrete log number of threads not power-of-two")]
|
||||
|
@ -61,7 +65,7 @@ pub struct DiscreteLog {
|
|||
}
|
||||
|
||||
#[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
|
||||
#[allow(dead_code)]
|
||||
|
@ -110,7 +114,7 @@ impl DiscreteLog {
|
|||
/// Adjusts number of threads in a discrete log instance.
|
||||
pub fn num_threads(&mut self, num_threads: usize) -> Result<(), DiscreteLogError> {
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,14 @@
|
|||
//! discrete log to recover the originally encrypted value.
|
||||
|
||||
use {
|
||||
crate::encryption::{
|
||||
discrete_log::DiscreteLog,
|
||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening, G, H},
|
||||
crate::{
|
||||
encryption::{
|
||||
discrete_log::DiscreteLog,
|
||||
pedersen::{
|
||||
Pedersen, PedersenCommitment, PedersenOpening, G, H, PEDERSEN_COMMITMENT_LEN,
|
||||
},
|
||||
},
|
||||
RISTRETTO_POINT_LEN, SCALAR_LEN,
|
||||
},
|
||||
base64::{prelude::BASE64_STANDARD, Engine},
|
||||
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)]
|
||||
pub enum ElGamalError {
|
||||
#[error("key derivation method not supported")]
|
||||
|
@ -209,21 +229,21 @@ impl ElGamalKeypair {
|
|||
&self.secret
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 64] {
|
||||
let mut bytes = [0u8; 64];
|
||||
bytes[..32].copy_from_slice(&self.public.to_bytes());
|
||||
bytes[32..].copy_from_slice(self.secret.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; ELGAMAL_KEYPAIR_LEN] {
|
||||
let mut bytes = [0u8; ELGAMAL_KEYPAIR_LEN];
|
||||
bytes[..ELGAMAL_PUBKEY_LEN].copy_from_slice(&self.public.to_bytes());
|
||||
bytes[ELGAMAL_PUBKEY_LEN..].copy_from_slice(self.secret.as_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||
if bytes.len() != 64 {
|
||||
if bytes.len() != ELGAMAL_KEYPAIR_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
public: ElGamalPubkey::from_bytes(&bytes[..32])?,
|
||||
secret: ElGamalSecretKey::from_bytes(bytes[32..].try_into().ok()?)?,
|
||||
public: ElGamalPubkey::from_bytes(&bytes[..ELGAMAL_PUBKEY_LEN])?,
|
||||
secret: ElGamalSecretKey::from_bytes(bytes[ELGAMAL_PUBKEY_LEN..].try_into().ok()?)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -317,12 +337,12 @@ impl ElGamalPubkey {
|
|||
&self.0
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; ELGAMAL_PUBKEY_LEN] {
|
||||
self.0.compress().to_bytes()
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalPubkey> {
|
||||
if bytes.len() != 32 {
|
||||
if bytes.len() != ELGAMAL_PUBKEY_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -428,7 +448,7 @@ impl ElGamalSecretKey {
|
|||
|
||||
/// Derive an ElGamal secret key from an entropy seed.
|
||||
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 {
|
||||
return Err(ElGamalError::SeedLengthTooShort);
|
||||
|
@ -453,11 +473,11 @@ impl ElGamalSecretKey {
|
|||
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()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; ELGAMAL_SECRET_KEY_LEN] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
|
@ -554,21 +574,21 @@ impl ElGamalCiphertext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 64] {
|
||||
let mut bytes = [0u8; 64];
|
||||
bytes[..32].copy_from_slice(&self.commitment.to_bytes());
|
||||
bytes[32..].copy_from_slice(&self.handle.to_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; ELGAMAL_CIPHERTEXT_LEN] {
|
||||
let mut bytes = [0u8; ELGAMAL_CIPHERTEXT_LEN];
|
||||
bytes[..PEDERSEN_COMMITMENT_LEN].copy_from_slice(&self.commitment.to_bytes());
|
||||
bytes[PEDERSEN_COMMITMENT_LEN..].copy_from_slice(&self.handle.to_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<ElGamalCiphertext> {
|
||||
if bytes.len() != 64 {
|
||||
if bytes.len() != ELGAMAL_CIPHERTEXT_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ElGamalCiphertext {
|
||||
commitment: PedersenCommitment::from_bytes(&bytes[..32])?,
|
||||
handle: DecryptHandle::from_bytes(&bytes[32..])?,
|
||||
commitment: PedersenCommitment::from_bytes(&bytes[..PEDERSEN_COMMITMENT_LEN])?,
|
||||
handle: DecryptHandle::from_bytes(&bytes[PEDERSEN_COMMITMENT_LEN..])?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -676,12 +696,12 @@ impl DecryptHandle {
|
|||
&self.0
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; DECRYPT_HANDLE_LEN] {
|
||||
self.0.compress().to_bytes()
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<DecryptHandle> {
|
||||
if bytes.len() != 32 {
|
||||
if bytes.len() != DECRYPT_HANDLE_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,13 @@
|
|||
//!
|
||||
|
||||
use {
|
||||
crate::encryption::{
|
||||
discrete_log::DiscreteLog,
|
||||
elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey},
|
||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
|
||||
crate::{
|
||||
encryption::{
|
||||
discrete_log::DiscreteLog,
|
||||
elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalPubkey, ElGamalSecretKey},
|
||||
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
|
||||
},
|
||||
RISTRETTO_POINT_LEN,
|
||||
},
|
||||
curve25519_dalek::scalar::Scalar,
|
||||
thiserror::Error,
|
||||
|
@ -163,7 +166,7 @@ impl<const N: usize> GroupedElGamalCiphertext<N> {
|
|||
/// `(N+1) * 32`.
|
||||
fn expected_byte_length() -> usize {
|
||||
N.checked_add(1)
|
||||
.and_then(|length| length.checked_mul(32))
|
||||
.and_then(|length| length.checked_mul(RISTRETTO_POINT_LEN))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
|
@ -181,7 +184,7 @@ impl<const N: usize> GroupedElGamalCiphertext<N> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let mut iter = bytes.chunks(32);
|
||||
let mut iter = bytes.chunks(RISTRETTO_POINT_LEN);
|
||||
let commitment = PedersenCommitment::from_bytes(iter.next()?)?;
|
||||
|
||||
let mut handles = Vec::with_capacity(N);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#[cfg(not(target_os = "solana"))]
|
||||
use rand::rngs::OsRng;
|
||||
use {
|
||||
crate::{RISTRETTO_POINT_LEN, SCALAR_LEN},
|
||||
core::ops::{Add, Mul, Sub},
|
||||
curve25519_dalek::{
|
||||
constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
|
||||
|
@ -17,6 +18,12 @@ use {
|
|||
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! {
|
||||
/// Pedersen base point for encoding messages to be committed.
|
||||
pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
|
||||
|
@ -82,11 +89,11 @@ impl PedersenOpening {
|
|||
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()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; PEDERSEN_OPENING_LEN] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
|
@ -177,12 +184,12 @@ impl PedersenCommitment {
|
|||
&self.0
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 32] {
|
||||
pub fn to_bytes(&self) -> [u8; PEDERSEN_COMMITMENT_LEN] {
|
||||
self.0.compress().to_bytes()
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
|
||||
if bytes.len() != 32 {
|
||||
if bytes.len() != PEDERSEN_COMMITMENT_LEN {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,3 +38,10 @@ pub mod zk_token_elgamal;
|
|||
pub mod zk_token_proof_instruction;
|
||||
pub mod zk_token_proof_program;
|
||||
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},
|
||||
},
|
||||
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,
|
||||
rand::rngs::OsRng,
|
||||
|
@ -27,6 +28,9 @@ use {
|
|||
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.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 224];
|
||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
||||
buf[96..128].copy_from_slice(self.Y_3.as_bytes());
|
||||
buf[128..160].copy_from_slice(self.z_s.as_bytes());
|
||||
buf[160..192].copy_from_slice(self.z_x.as_bytes());
|
||||
buf[192..224].copy_from_slice(self.z_r.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN] {
|
||||
let mut buf = [0_u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_2.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_3.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
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
||||
if bytes.len() != 224 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
|
||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
||||
let Y_2 = CompressedRistretto::from_slice(&bytes[64..96]);
|
||||
let Y_3 = CompressedRistretto::from_slice(&bytes[96..128]);
|
||||
let z_s = canonical_scalar_from_slice(&bytes[128..160])?;
|
||||
let z_x = canonical_scalar_from_slice(&bytes[160..192])?;
|
||||
let z_r = canonical_scalar_from_slice(&bytes[192..224])?;
|
||||
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_3 = ristretto_point_from_optional_slice(chunks.next())?;
|
||||
let z_s = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
|
||||
Ok(CiphertextCiphertextEqualityProof {
|
||||
Y_0,
|
||||
|
|
|
@ -16,7 +16,8 @@ use {
|
|||
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
||||
},
|
||||
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,
|
||||
rand::rngs::OsRng,
|
||||
|
@ -32,6 +33,9 @@ use {
|
|||
merlin::Transcript,
|
||||
};
|
||||
|
||||
/// Byte length of a ciphertext-commitment equality proof.
|
||||
const CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN: usize = UNIT_LEN * 6;
|
||||
|
||||
/// Equality proof.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 192];
|
||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
||||
buf[96..128].copy_from_slice(self.z_s.as_bytes());
|
||||
buf[128..160].copy_from_slice(self.z_x.as_bytes());
|
||||
buf[160..192].copy_from_slice(self.z_r.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN] {
|
||||
let mut buf = [0_u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_2.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
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, EqualityProofError> {
|
||||
if bytes.len() != 192 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
|
||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
||||
let Y_2 = CompressedRistretto::from_slice(&bytes[64..96]);
|
||||
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])?;
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
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 z_s = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
|
||||
Ok(CiphertextCommitmentEqualityProof {
|
||||
Y_0,
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
use {
|
||||
crate::{
|
||||
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,
|
||||
};
|
||||
|
@ -32,6 +33,9 @@ use {
|
|||
subtle::{Choice, ConditionallySelectable, ConstantTimeGreater},
|
||||
};
|
||||
|
||||
/// Byte length of a fee sigma proof.
|
||||
const FEE_SIGMA_PROOF_LEN: usize = UNIT_LEN * 8;
|
||||
|
||||
/// Fee sigma proof.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 256];
|
||||
buf[..32].copy_from_slice(self.fee_max_proof.Y_max_proof.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.fee_max_proof.z_max_proof.as_bytes());
|
||||
buf[64..96].copy_from_slice(self.fee_max_proof.c_max_proof.as_bytes());
|
||||
buf[96..128].copy_from_slice(self.fee_equality_proof.Y_delta.as_bytes());
|
||||
buf[128..160].copy_from_slice(self.fee_equality_proof.Y_claimed.as_bytes());
|
||||
buf[160..192].copy_from_slice(self.fee_equality_proof.z_x.as_bytes());
|
||||
buf[192..224].copy_from_slice(self.fee_equality_proof.z_delta.as_bytes());
|
||||
buf[224..256].copy_from_slice(self.fee_equality_proof.z_claimed.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; FEE_SIGMA_PROOF_LEN] {
|
||||
let mut buf = [0_u8; FEE_SIGMA_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
chunks
|
||||
.next()
|
||||
.unwrap()
|
||||
.copy_from_slice(self.fee_max_proof.Y_max_proof.as_bytes());
|
||||
chunks
|
||||
.next()
|
||||
.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
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, FeeSigmaProofError> {
|
||||
if bytes.len() != 256 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
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 z_max_proof = canonical_scalar_from_slice(&bytes[32..64])?;
|
||||
let c_max_proof = canonical_scalar_from_slice(&bytes[64..96])?;
|
||||
|
||||
let Y_delta = CompressedRistretto::from_slice(&bytes[96..128]);
|
||||
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])?;
|
||||
let Y_delta = ristretto_point_from_optional_slice(chunks.next())?;
|
||||
let Y_claimed = ristretto_point_from_optional_slice(chunks.next())?;
|
||||
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_delta = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_claimed = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
|
||||
Ok(Self {
|
||||
fee_max_proof: FeeMaxProof {
|
||||
|
|
|
@ -16,7 +16,8 @@ use {
|
|||
pedersen::{PedersenCommitment, PedersenOpening, G, H},
|
||||
},
|
||||
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,
|
||||
rand::rngs::OsRng,
|
||||
|
@ -32,6 +33,9 @@ use {
|
|||
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.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 160];
|
||||
buf[..32].copy_from_slice(self.Y_0.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.Y_1.as_bytes());
|
||||
buf[64..96].copy_from_slice(self.Y_2.as_bytes());
|
||||
buf[96..128].copy_from_slice(self.z_r.as_bytes());
|
||||
buf[128..160].copy_from_slice(self.z_x.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN] {
|
||||
let mut buf = [0_u8; GROUPED_CIPHERTEXT_2_HANDLES_VALIDITY_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_0.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_1.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_2.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
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ValidityProofError> {
|
||||
if bytes.len() != 160 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
|
||||
let Y_0 = CompressedRistretto::from_slice(&bytes[..32]);
|
||||
let Y_1 = CompressedRistretto::from_slice(&bytes[32..64]);
|
||||
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])?;
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
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 z_r = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
let z_x = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
|
||||
Ok(GroupedCiphertext2HandlesValidityProof {
|
||||
Y_0,
|
||||
|
|
|
@ -15,19 +15,36 @@ pub mod pubkey_proof;
|
|||
pub mod zero_balance_proof;
|
||||
|
||||
#[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"))]
|
||||
fn canonical_scalar_from_slice(bytes: &[u8]) -> Result<Scalar, ProofVerificationError> {
|
||||
if bytes.len() != 32 {
|
||||
return Err(ProofVerificationError::Deserialization);
|
||||
}
|
||||
|
||||
let scalar_bytes = bytes[..32]
|
||||
.try_into()
|
||||
.map_err(|_| ProofVerificationError::Deserialization)?;
|
||||
|
||||
let scalar = Scalar::from_canonical_bytes(scalar_bytes)
|
||||
.ok_or(ProofVerificationError::Deserialization)?;
|
||||
Ok(scalar)
|
||||
fn ristretto_point_from_optional_slice(
|
||||
optional_slice: Option<&[u8]>,
|
||||
) -> Result<CompressedRistretto, ProofVerificationError> {
|
||||
optional_slice
|
||||
.and_then(|slice| (slice.len() == RISTRETTO_POINT_LEN).then_some(slice))
|
||||
.map(CompressedRistretto::from_slice)
|
||||
.ok_or(ProofVerificationError::Deserialization)
|
||||
}
|
||||
|
||||
/// Deserializes an optional slice of bytes to a scalar.
|
||||
///
|
||||
/// 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"))]
|
||||
fn canonical_scalar_from_optional_slice(
|
||||
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},
|
||||
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,
|
||||
zeroize::Zeroize,
|
||||
|
@ -28,6 +29,9 @@ use {
|
|||
merlin::Transcript,
|
||||
};
|
||||
|
||||
/// Byte length of a public key validity proof.
|
||||
const PUBKEY_VALIDITY_PROOF_LEN: usize = UNIT_LEN * 2;
|
||||
|
||||
/// Public-key proof.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 64];
|
||||
buf[..32].copy_from_slice(self.Y.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.z.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; PUBKEY_VALIDITY_PROOF_LEN] {
|
||||
let mut buf = [0_u8; PUBKEY_VALIDITY_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
chunks.next().unwrap().copy_from_slice(self.Y.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.z.as_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PubkeyValidityProofError> {
|
||||
if bytes.len() != 64 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
|
||||
let Y = CompressedRistretto::from_slice(&bytes[..32]);
|
||||
let z = canonical_scalar_from_slice(&bytes[32..64])?;
|
||||
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
let Y = ristretto_point_from_optional_slice(chunks.next())?;
|
||||
let z = canonical_scalar_from_optional_slice(chunks.next())?;
|
||||
Ok(PubkeyValidityProof { Y, z })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ use {
|
|||
pedersen::H,
|
||||
},
|
||||
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,
|
||||
rand::rngs::OsRng,
|
||||
|
@ -27,6 +28,9 @@ use {
|
|||
merlin::Transcript,
|
||||
};
|
||||
|
||||
/// Byte length of a zero-balance proof.
|
||||
const ZERO_BALANCE_PROOF_LEN: usize = UNIT_LEN * 3;
|
||||
|
||||
/// Zero-balance proof.
|
||||
///
|
||||
/// 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] {
|
||||
let mut buf = [0_u8; 96];
|
||||
buf[..32].copy_from_slice(self.Y_P.as_bytes());
|
||||
buf[32..64].copy_from_slice(self.Y_D.as_bytes());
|
||||
buf[64..96].copy_from_slice(self.z.as_bytes());
|
||||
pub fn to_bytes(&self) -> [u8; ZERO_BALANCE_PROOF_LEN] {
|
||||
let mut buf = [0_u8; ZERO_BALANCE_PROOF_LEN];
|
||||
let mut chunks = buf.chunks_mut(UNIT_LEN);
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_P.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.Y_D.as_bytes());
|
||||
chunks.next().unwrap().copy_from_slice(self.z.as_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ZeroBalanceProofError> {
|
||||
if bytes.len() != 96 {
|
||||
return Err(ProofVerificationError::Deserialization.into());
|
||||
}
|
||||
|
||||
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])?;
|
||||
|
||||
let mut chunks = bytes.chunks(UNIT_LEN);
|
||||
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())?;
|
||||
Ok(ZeroBalanceProof { Y_P, Y_D, z })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ use {
|
|||
std::fmt,
|
||||
};
|
||||
|
||||
/// Byte length of an authenticated encryption ciphertext
|
||||
const AE_CIPHERTEXT_LEN: usize = 36;
|
||||
|
||||
/// The `AeCiphertext` type as a `Pod`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[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,
|
||||
// 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.
|
||||
|
||||
use {
|
||||
crate::zk_token_elgamal::pod::{Pod, Zeroable},
|
||||
base64::{prelude::BASE64_STANDARD, Engine},
|
||||
std::fmt,
|
||||
};
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
use {
|
||||
crate::{encryption::elgamal as decoded, errors::ProofError},
|
||||
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`.
|
||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct ElGamalCiphertext(pub [u8; 64]);
|
||||
pub struct ElGamalCiphertext(pub [u8; ELGAMAL_CIPHERTEXT_LEN]);
|
||||
|
||||
impl fmt::Debug for ElGamalCiphertext {
|
||||
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`.
|
||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct ElGamalPubkey(pub [u8; 32]);
|
||||
pub struct ElGamalPubkey(pub [u8; ELGAMAL_PUBKEY_LEN]);
|
||||
|
||||
impl fmt::Debug for ElGamalPubkey {
|
||||
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`.
|
||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct DecryptHandle(pub [u8; 32]);
|
||||
pub struct DecryptHandle(pub [u8; DECRYPT_HANDLE_LEN]);
|
||||
|
||||
impl fmt::Debug for DecryptHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -3,14 +3,24 @@
|
|||
#[cfg(not(target_os = "solana"))]
|
||||
use crate::{encryption::grouped_elgamal::GroupedElGamalCiphertext, errors::ProofError};
|
||||
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,
|
||||
};
|
||||
|
||||
/// Byte length of a grouped ElGamal ciphertext with 2 handles
|
||||
const GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES: usize =
|
||||
PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN;
|
||||
|
||||
/// Byte length of a grouped ElGamal ciphertext with 3 handles
|
||||
const GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES: usize =
|
||||
PEDERSEN_COMMITMENT_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN + DECRYPT_HANDLE_LEN;
|
||||
|
||||
/// The `GroupedElGamalCiphertext` type with two decryption handles as a `Pod`
|
||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct GroupedElGamalCiphertext2Handles(pub [u8; 96]);
|
||||
pub struct GroupedElGamalCiphertext2Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_2_HANDLES]);
|
||||
|
||||
impl fmt::Debug for GroupedElGamalCiphertext2Handles {
|
||||
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`
|
||||
#[derive(Clone, Copy, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct GroupedElGamalCiphertext3Handles(pub [u8; 128]);
|
||||
pub struct GroupedElGamalCiphertext3Handles(pub [u8; GROUPED_ELGAMAL_CIPHERTEXT_3_HANDLES]);
|
||||
|
||||
impl fmt::Debug for GroupedElGamalCiphertext3Handles {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
//! Plain Old Data type for the Pedersen commitment scheme.
|
||||
|
||||
use {
|
||||
crate::zk_token_elgamal::pod::{Pod, Zeroable},
|
||||
std::fmt,
|
||||
};
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
use {
|
||||
crate::{encryption::pedersen as decoded, errors::ProofError},
|
||||
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`.
|
||||
#[derive(Clone, Copy, Default, Pod, Zeroable, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PedersenCommitment(pub [u8; 32]);
|
||||
pub struct PedersenCommitment(pub [u8; PEDERSEN_COMMITMENT_LEN]);
|
||||
|
||||
impl fmt::Debug for PedersenCommitment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -1,35 +1,58 @@
|
|||
//! Plain Old Data types for range proofs.
|
||||
|
||||
use crate::zk_token_elgamal::pod::{Pod, Zeroable};
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
use crate::{
|
||||
errors::ProofVerificationError,
|
||||
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.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct RangeProofU64(pub [u8; 672]);
|
||||
pub struct RangeProofU64(pub [u8; RANGE_PROOF_U64_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl TryFrom<decoded::RangeProof> for RangeProofU64 {
|
||||
type Error = RangeProofError;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
let mut buf = [0_u8; 672];
|
||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_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());
|
||||
let mut buf = [0_u8; RANGE_PROOF_U64_LEN];
|
||||
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U64_LEN]
|
||||
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||
Ok(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.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct RangeProofU128(pub [u8; 736]);
|
||||
pub struct RangeProofU128(pub [u8; RANGE_PROOF_U128_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl TryFrom<decoded::RangeProof> for RangeProofU128 {
|
||||
type Error = RangeProofError;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
let mut buf = [0_u8; 736];
|
||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_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());
|
||||
let mut buf = [0_u8; RANGE_PROOF_U128_LEN];
|
||||
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U128_LEN]
|
||||
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||
Ok(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.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct RangeProofU256(pub [u8; 800]);
|
||||
pub struct RangeProofU256(pub [u8; RANGE_PROOF_U256_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl TryFrom<decoded::RangeProof> for RangeProofU256 {
|
||||
type Error = RangeProofError;
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
let mut buf = [0_u8; 800];
|
||||
buf[..32].copy_from_slice(decoded_proof.A.as_bytes());
|
||||
buf[32..64].copy_from_slice(decoded_proof.S.as_bytes());
|
||||
buf[64..96].copy_from_slice(decoded_proof.T_1.as_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());
|
||||
let mut buf = [0_u8; RANGE_PROOF_U256_LEN];
|
||||
copy_range_proof_modulo_inner_product_proof(&decoded_proof, &mut buf);
|
||||
buf[RANGE_PROOF_MODULO_INNER_PRODUCT_PROOF_LEN..RANGE_PROOF_U256_LEN]
|
||||
.copy_from_slice(&decoded_proof.ipp_proof.to_bytes());
|
||||
Ok(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 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.
|
||||
|
|
|
@ -12,10 +12,31 @@ use crate::sigma_proofs::{
|
|||
};
|
||||
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`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct CiphertextCommitmentEqualityProof(pub [u8; 192]);
|
||||
pub struct CiphertextCommitmentEqualityProof(pub [u8; CIPHERTEXT_COMMITMENT_EQUALITY_PROOF_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<DecodedCiphertextCommitmentEqualityProof> for CiphertextCommitmentEqualityProof {
|
||||
|
@ -36,7 +57,7 @@ impl TryFrom<CiphertextCommitmentEqualityProof> for DecodedCiphertextCommitmentE
|
|||
/// The `CiphertextCiphertextEqualityProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct CiphertextCiphertextEqualityProof(pub [u8; 224]);
|
||||
pub struct CiphertextCiphertextEqualityProof(pub [u8; CIPHERTEXT_CIPHERTEXT_EQUALITY_PROOF_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<DecodedCiphertextCiphertextEqualityProof> for CiphertextCiphertextEqualityProof {
|
||||
|
@ -57,7 +78,9 @@ impl TryFrom<CiphertextCiphertextEqualityProof> for DecodedCiphertextCiphertextE
|
|||
/// The `GroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[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"))]
|
||||
impl From<DecodedGroupedCiphertext2HandlesValidityProof>
|
||||
|
@ -82,7 +105,9 @@ impl TryFrom<GroupedCiphertext2HandlesValidityProof>
|
|||
/// The `BatchedGroupedCiphertext2HandlesValidityProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[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"))]
|
||||
impl From<DecodedBatchedGroupedCiphertext2HandlesValidityProof>
|
||||
|
@ -109,7 +134,7 @@ impl TryFrom<BatchedGroupedCiphertext2HandlesValidityProof>
|
|||
/// The `ZeroBalanceProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct ZeroBalanceProof(pub [u8; 96]);
|
||||
pub struct ZeroBalanceProof(pub [u8; ZERO_BALANCE_PROOF_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<DecodedZeroBalanceProof> for ZeroBalanceProof {
|
||||
|
@ -130,7 +155,7 @@ impl TryFrom<ZeroBalanceProof> for DecodedZeroBalanceProof {
|
|||
/// The `FeeSigmaProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(transparent)]
|
||||
pub struct FeeSigmaProof(pub [u8; 256]);
|
||||
pub struct FeeSigmaProof(pub [u8; FEE_SIGMA_PROOF_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<DecodedFeeSigmaProof> for FeeSigmaProof {
|
||||
|
@ -151,7 +176,7 @@ impl TryFrom<FeeSigmaProof> for DecodedFeeSigmaProof {
|
|||
/// The `PubkeyValidityProof` type as a `Pod`.
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(transparent)]
|
||||
pub struct PubkeyValidityProof(pub [u8; 64]);
|
||||
pub struct PubkeyValidityProof(pub [u8; PUBKEY_VALIDITY_PROOF_LEN]);
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<DecodedPubkeyValidityProof> for PubkeyValidityProof {
|
||||
|
|
Loading…
Reference in New Issue