[zk-token-sdk] Refactor transfer instruction (#31848)

* refactor `instruction::transfer` and `instruction::transfer_with_fee` into separate submodule

* rename `transfer.rs` to `transfer_without_fee.rs` to satisfy clippy

* refactor transfer encryptions into separate submodule

* refactor `FeeParameters` to parent transfer module

* refactor `Role` and ciphertext arithmetic specific to transfer instruction into transfer submodule

* fix visibility

* cargo fmt

* change `transfer_with_fee` and `transfer_without_fee` to `with_fee` and `without_fee`

* fix rebase errors
This commit is contained in:
samkim-crypto 2023-06-01 06:55:23 +09:00 committed by GitHub
parent 944310b751
commit 9216ff8c3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 219 additions and 194 deletions

View File

@ -4,22 +4,12 @@ pub mod ciphertext_commitment_equality;
pub mod pubkey_validity;
pub mod range_proof;
pub mod transfer;
pub mod transfer_with_fee;
pub mod withdraw;
pub mod zero_balance;
use num_derive::{FromPrimitive, ToPrimitive};
#[cfg(not(target_os = "solana"))]
use {
crate::{
encryption::{
elgamal::ElGamalCiphertext,
pedersen::{PedersenCommitment, PedersenOpening},
},
errors::ProofError,
},
curve25519_dalek::scalar::Scalar,
};
use crate::errors::ProofError;
use num_derive::{FromPrimitive, ToPrimitive};
pub use {
batched_range_proof::{
batched_range_proof_u128::BatchedRangeProofU128Data,
@ -35,8 +25,10 @@ pub use {
},
pubkey_validity::{PubkeyValidityData, PubkeyValidityProofContext},
range_proof::{RangeProofContext, RangeProofU64Data},
transfer::{TransferData, TransferProofContext},
transfer_with_fee::{FeeParameters, TransferWithFeeData, TransferWithFeeProofContext},
transfer::{
FeeParameters, TransferData, TransferProofContext, TransferWithFeeData,
TransferWithFeeProofContext,
},
withdraw::{WithdrawData, WithdrawProofContext},
zero_balance::{ZeroBalanceProofContext, ZeroBalanceProofData},
};
@ -67,65 +59,3 @@ pub trait ZkProofData<T: Pod> {
#[cfg(not(target_os = "solana"))]
fn verify_proof(&self) -> Result<(), ProofError>;
}
#[cfg(not(target_os = "solana"))]
#[derive(Debug, Copy, Clone)]
pub enum Role {
Source,
Destination,
Auditor,
WithdrawWithheldAuthority,
}
/// Takes in a 64-bit number `amount` and a bit length `bit_length`. It returns:
/// - the `bit_length` low bits of `amount` interpretted as u64
/// - the (64 - `bit_length`) high bits of `amount` interpretted as u64
#[cfg(not(target_os = "solana"))]
pub fn split_u64(amount: u64, bit_length: usize) -> (u64, u64) {
if bit_length == 64 {
(amount, 0)
} else {
let lo = amount << (64 - bit_length) >> (64 - bit_length);
let hi = amount >> bit_length;
(lo, hi)
}
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_u64(amount_lo: u64, amount_hi: u64, bit_length: usize) -> u64 {
if bit_length == 64 {
amount_lo
} else {
amount_lo + (amount_hi << bit_length)
}
}
#[cfg(not(target_os = "solana"))]
fn combine_lo_hi_ciphertexts(
ciphertext_lo: &ElGamalCiphertext,
ciphertext_hi: &ElGamalCiphertext,
bit_length: usize,
) -> ElGamalCiphertext {
let two_power = (1_u64) << bit_length;
ciphertext_lo + &(ciphertext_hi * &Scalar::from(two_power))
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_commitments(
comm_lo: &PedersenCommitment,
comm_hi: &PedersenCommitment,
bit_length: usize,
) -> PedersenCommitment {
let two_power = (1_u64) << bit_length;
comm_lo + comm_hi * &Scalar::from(two_power)
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_openings(
opening_lo: &PedersenOpening,
opening_hi: &PedersenOpening,
bit_length: usize,
) -> PedersenOpening {
let two_power = (1_u64) << bit_length;
opening_lo + opening_hi * &Scalar::from(two_power)
}

View File

@ -0,0 +1,85 @@
#[cfg(not(target_os = "solana"))]
use crate::{
encryption::{
elgamal::{DecryptHandle, ElGamalPubkey},
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
},
zk_token_elgamal::pod,
};
// TransferAmountEncryption
#[derive(Clone)]
#[repr(C)]
#[cfg(not(target_os = "solana"))]
pub struct TransferAmountEncryption {
pub commitment: PedersenCommitment,
pub source_handle: DecryptHandle,
pub destination_handle: DecryptHandle,
pub auditor_handle: DecryptHandle,
}
#[cfg(not(target_os = "solana"))]
impl TransferAmountEncryption {
pub fn new(
amount: u64,
source_pubkey: &ElGamalPubkey,
destination_pubkey: &ElGamalPubkey,
auditor_pubkey: &ElGamalPubkey,
) -> (Self, PedersenOpening) {
let (commitment, opening) = Pedersen::new(amount);
let transfer_amount_encryption = Self {
commitment,
source_handle: source_pubkey.decrypt_handle(&opening),
destination_handle: destination_pubkey.decrypt_handle(&opening),
auditor_handle: auditor_pubkey.decrypt_handle(&opening),
};
(transfer_amount_encryption, opening)
}
pub fn to_pod(&self) -> pod::TransferAmountEncryption {
pod::TransferAmountEncryption {
commitment: self.commitment.into(),
source_handle: self.source_handle.into(),
destination_handle: self.destination_handle.into(),
auditor_handle: self.auditor_handle.into(),
}
}
}
// FeeEncryption
#[derive(Clone)]
#[repr(C)]
#[cfg(not(target_os = "solana"))]
pub struct FeeEncryption {
pub commitment: PedersenCommitment,
pub destination_handle: DecryptHandle,
pub withdraw_withheld_authority_handle: DecryptHandle,
}
#[cfg(not(target_os = "solana"))]
impl FeeEncryption {
pub fn new(
amount: u64,
destination_pubkey: &ElGamalPubkey,
withdraw_withheld_authority_pubkey: &ElGamalPubkey,
) -> (Self, PedersenOpening) {
let (commitment, opening) = Pedersen::new(amount);
let fee_encryption = Self {
commitment,
destination_handle: destination_pubkey.decrypt_handle(&opening),
withdraw_withheld_authority_handle: withdraw_withheld_authority_pubkey
.decrypt_handle(&opening),
};
(fee_encryption, opening)
}
pub fn to_pod(&self) -> pod::FeeEncryption {
pod::FeeEncryption {
commitment: self.commitment.into(),
destination_handle: self.destination_handle.into(),
withdraw_withheld_authority_handle: self.withdraw_withheld_authority_handle.into(),
}
}
}

View File

@ -0,0 +1,115 @@
mod encryption;
mod with_fee;
mod without_fee;
#[cfg(not(target_os = "solana"))]
use {
crate::encryption::{
elgamal::ElGamalCiphertext,
pedersen::{PedersenCommitment, PedersenOpening},
},
arrayref::{array_ref, array_refs},
curve25519_dalek::scalar::Scalar,
};
#[cfg(not(target_os = "solana"))]
pub use {
encryption::{FeeEncryption, TransferAmountEncryption},
with_fee::TransferWithFeePubkeys,
without_fee::TransferPubkeys,
};
pub use {
with_fee::{TransferWithFeeData, TransferWithFeeProofContext},
without_fee::{TransferData, TransferProofContext},
};
#[cfg(not(target_os = "solana"))]
#[derive(Debug, Copy, Clone)]
pub enum Role {
Source,
Destination,
Auditor,
WithdrawWithheldAuthority,
}
/// Takes in a 64-bit number `amount` and a bit length `bit_length`. It returns:
/// - the `bit_length` low bits of `amount` interpretted as u64
/// - the (64 - `bit_length`) high bits of `amount` interpretted as u64
#[cfg(not(target_os = "solana"))]
pub fn split_u64(amount: u64, bit_length: usize) -> (u64, u64) {
if bit_length == 64 {
(amount, 0)
} else {
let lo = amount << (64 - bit_length) >> (64 - bit_length);
let hi = amount >> bit_length;
(lo, hi)
}
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_u64(amount_lo: u64, amount_hi: u64, bit_length: usize) -> u64 {
if bit_length == 64 {
amount_lo
} else {
amount_lo + (amount_hi << bit_length)
}
}
#[cfg(not(target_os = "solana"))]
fn combine_lo_hi_ciphertexts(
ciphertext_lo: &ElGamalCiphertext,
ciphertext_hi: &ElGamalCiphertext,
bit_length: usize,
) -> ElGamalCiphertext {
let two_power = (1_u64) << bit_length;
ciphertext_lo + &(ciphertext_hi * &Scalar::from(two_power))
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_commitments(
comm_lo: &PedersenCommitment,
comm_hi: &PedersenCommitment,
bit_length: usize,
) -> PedersenCommitment {
let two_power = (1_u64) << bit_length;
comm_lo + comm_hi * &Scalar::from(two_power)
}
#[cfg(not(target_os = "solana"))]
pub fn combine_lo_hi_openings(
opening_lo: &PedersenOpening,
opening_hi: &PedersenOpening,
bit_length: usize,
) -> PedersenOpening {
let two_power = (1_u64) << bit_length;
opening_lo + opening_hi * &Scalar::from(two_power)
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct FeeParameters {
/// Fee rate expressed as basis points of the transfer amount, i.e. increments of 0.01%
pub fee_rate_basis_points: u16,
/// Maximum fee assessed on transfers, expressed as an amount of tokens
pub maximum_fee: u64,
}
#[cfg(not(target_os = "solana"))]
impl FeeParameters {
pub fn to_bytes(&self) -> [u8; 10] {
let mut bytes = [0u8; 10];
bytes[..2].copy_from_slice(&self.fee_rate_basis_points.to_le_bytes());
bytes[2..10].copy_from_slice(&self.maximum_fee.to_le_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Self {
let bytes = array_ref![bytes, 0, 10];
let (fee_rate_basis_points, maximum_fee) = array_refs![bytes, 2, 8];
Self {
fee_rate_basis_points: u16::from_le_bytes(*fee_rate_basis_points),
maximum_fee: u64::from_le_bytes(*maximum_fee),
}
}
}

View File

@ -2,15 +2,15 @@
use {
crate::{
encryption::{
elgamal::{
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey},
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
},
errors::ProofError,
instruction::{
instruction::transfer::{
combine_lo_hi_ciphertexts, combine_lo_hi_commitments, combine_lo_hi_openings,
combine_lo_hi_u64, split_u64, transfer::TransferAmountEncryption, Role,
combine_lo_hi_u64,
encryption::{FeeEncryption, TransferAmountEncryption},
split_u64, FeeParameters, Role,
},
range_proof::RangeProof,
sigma_proofs::{
@ -721,72 +721,6 @@ impl TransferWithFeePubkeys {
}
}
#[derive(Clone)]
#[repr(C)]
#[cfg(not(target_os = "solana"))]
pub struct FeeEncryption {
pub commitment: PedersenCommitment,
pub destination_handle: DecryptHandle,
pub withdraw_withheld_authority_handle: DecryptHandle,
}
#[cfg(not(target_os = "solana"))]
impl FeeEncryption {
pub fn new(
amount: u64,
destination_pubkey: &ElGamalPubkey,
withdraw_withheld_authority_pubkey: &ElGamalPubkey,
) -> (Self, PedersenOpening) {
let (commitment, opening) = Pedersen::new(amount);
let fee_encryption = Self {
commitment,
destination_handle: destination_pubkey.decrypt_handle(&opening),
withdraw_withheld_authority_handle: withdraw_withheld_authority_pubkey
.decrypt_handle(&opening),
};
(fee_encryption, opening)
}
pub fn to_pod(&self) -> pod::FeeEncryption {
pod::FeeEncryption {
commitment: self.commitment.into(),
destination_handle: self.destination_handle.into(),
withdraw_withheld_authority_handle: self.withdraw_withheld_authority_handle.into(),
}
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct FeeParameters {
/// Fee rate expressed as basis points of the transfer amount, i.e. increments of 0.01%
pub fee_rate_basis_points: u16,
/// Maximum fee assessed on transfers, expressed as an amount of tokens
pub maximum_fee: u64,
}
#[cfg(not(target_os = "solana"))]
impl FeeParameters {
pub fn to_bytes(&self) -> [u8; 10] {
let mut bytes = [0u8; 10];
bytes[..2].copy_from_slice(&self.fee_rate_basis_points.to_le_bytes());
bytes[2..10].copy_from_slice(&self.maximum_fee.to_le_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Self {
let bytes = array_ref![bytes, 0, 10];
let (fee_rate_basis_points, maximum_fee) = array_refs![bytes, 2, 8];
Self {
fee_rate_basis_points: u16::from_le_bytes(*fee_rate_basis_points),
maximum_fee: u64::from_le_bytes(*maximum_fee),
}
}
}
#[cfg(not(target_os = "solana"))]
fn calculate_fee(transfer_amount: u64, fee_rate_basis_points: u16) -> Option<(u64, u64)> {
let numerator = (transfer_amount as u128).checked_mul(fee_rate_basis_points as u128)?;

View File

@ -2,13 +2,13 @@
use {
crate::{
encryption::{
elgamal::{
DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey,
},
elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey},
pedersen::{Pedersen, PedersenCommitment, PedersenOpening},
},
errors::ProofError,
instruction::{combine_lo_hi_ciphertexts, split_u64, Role},
instruction::transfer::{
combine_lo_hi_ciphertexts, encryption::TransferAmountEncryption, split_u64, Role,
},
range_proof::RangeProof,
sigma_proofs::{
ciphertext_commitment_equality_proof::CiphertextCommitmentEqualityProof,
@ -469,45 +469,6 @@ impl TransferPubkeys {
}
}
#[derive(Clone)]
#[repr(C)]
#[cfg(not(target_os = "solana"))]
pub struct TransferAmountEncryption {
pub commitment: PedersenCommitment,
pub source_handle: DecryptHandle,
pub destination_handle: DecryptHandle,
pub auditor_handle: DecryptHandle,
}
#[cfg(not(target_os = "solana"))]
impl TransferAmountEncryption {
pub fn new(
amount: u64,
source_pubkey: &ElGamalPubkey,
destination_pubkey: &ElGamalPubkey,
auditor_pubkey: &ElGamalPubkey,
) -> (Self, PedersenOpening) {
let (commitment, opening) = Pedersen::new(amount);
let transfer_amount_encryption = Self {
commitment,
source_handle: source_pubkey.decrypt_handle(&opening),
destination_handle: destination_pubkey.decrypt_handle(&opening),
auditor_handle: auditor_pubkey.decrypt_handle(&opening),
};
(transfer_amount_encryption, opening)
}
pub fn to_pod(&self) -> pod::TransferAmountEncryption {
pod::TransferAmountEncryption {
commitment: self.commitment.into(),
source_handle: self.source_handle.into(),
destination_handle: self.destination_handle.into(),
auditor_handle: self.auditor_handle.into(),
}
}
}
#[cfg(test)]
mod test {
use {super::*, crate::encryption::elgamal::ElGamalKeypair};

View File

@ -53,9 +53,9 @@ mod target_arch {
crate::{
curve25519::scalar::PodScalar,
errors::ProofError,
instruction::{
transfer::{TransferAmountEncryption, TransferPubkeys},
transfer_with_fee::{FeeEncryption, FeeParameters, TransferWithFeePubkeys},
instruction::transfer::{
FeeEncryption, FeeParameters, TransferAmountEncryption, TransferPubkeys,
TransferWithFeePubkeys,
},
},
curve25519_dalek::{ristretto::CompressedRistretto, scalar::Scalar},

View File

@ -134,7 +134,7 @@ mod tests {
elgamal::{ElGamalCiphertext, ElGamalKeypair},
pedersen::{Pedersen, PedersenOpening},
},
instruction::split_u64,
instruction::transfer::split_u64,
zk_token_elgamal::{ops, pod},
},
bytemuck::Zeroable,