[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:
parent
944310b751
commit
9216ff8c3e
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)?;
|
|
@ -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};
|
|
@ -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},
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue