forked from solana-labs/solana-program-library
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[confidential-transfer] Add transfer split proof generation (solana-l…
…abs#6915) * add `confidential-transfer-proofs` * add `encryption` module * add transfer proof generation and extraction * cargo fmt * allow `arithmetic_side_effects` on ciphertexts and openings * include `solana-zk-sdk` in patches * refactor into two separate crates * clean up tests * remove unnecessary `target_os = solana` * refactor tests into a separate crate
- Loading branch information
1 parent
d6a5ecf
commit 1808765
Showing
16 changed files
with
596 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "spl-token-confidential-transfer-proof-extraction" | ||
version = "0.1.0" | ||
description = "Solana Program Library Confidential Transfer Proof Extraction" | ||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"] | ||
repository = "https://github.com/solana-labs/solana-program-library" | ||
license = "Apache-2.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
solana-zk-sdk = "2.0.0" | ||
thiserror = "1.0.61" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] |
5 changes: 5 additions & 0 deletions
5
token/confidential-transfer/proof-extraction/src/encryption.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
use solana_zk_sdk::encryption::pod::grouped_elgamal::PodGroupedElGamalCiphertext3Handles; | ||
|
||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
#[repr(C)] | ||
pub struct PodTransferAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext3Handles); |
11 changes: 11 additions & 0 deletions
11
token/confidential-transfer/proof-extraction/src/errors.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use thiserror::Error; | ||
|
||
#[derive(Error, Clone, Debug, Eq, PartialEq)] | ||
pub enum TokenProofExtractionError { | ||
#[error("ElGamal pubkey mismatch")] | ||
ElGamalPubkeyMismatch, | ||
#[error("Pedersen commitment mismatch")] | ||
PedersenCommitmentMismatch, | ||
#[error("Range proof length mismatch")] | ||
RangeProofLengthMismatch, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod encryption; | ||
pub mod errors; | ||
pub mod transfer; |
137 changes: 137 additions & 0 deletions
137
token/confidential-transfer/proof-extraction/src/transfer.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
use { | ||
crate::{encryption::PodTransferAmountCiphertext, errors::TokenProofExtractionError}, | ||
solana_zk_sdk::{ | ||
encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, | ||
zk_elgamal_proof_program::proof_data::{ | ||
BatchedGroupedCiphertext3HandlesValidityProofContext, BatchedRangeProofContext, | ||
CiphertextCommitmentEqualityProofContext, | ||
}, | ||
}, | ||
}; | ||
|
||
/// The transfer public keys associated with a transfer. | ||
pub struct TransferPubkeys { | ||
/// Source ElGamal public key | ||
pub source: PodElGamalPubkey, | ||
/// Destination ElGamal public key | ||
pub destination: PodElGamalPubkey, | ||
/// Auditor ElGamal public key | ||
pub auditor: PodElGamalPubkey, | ||
} | ||
|
||
/// The proof context information needed to process a [Transfer] instruction. | ||
pub struct TransferProofContext { | ||
/// Ciphertext containing the low 16 bits of the transafer amount | ||
pub ciphertext_lo: PodTransferAmountCiphertext, | ||
/// Ciphertext containing the high 32 bits of the transafer amount | ||
pub ciphertext_hi: PodTransferAmountCiphertext, | ||
/// The transfer public keys associated with a transfer | ||
pub transfer_pubkeys: TransferPubkeys, | ||
/// The new source available balance ciphertext | ||
pub new_source_ciphertext: PodElGamalCiphertext, | ||
} | ||
|
||
impl TransferProofContext { | ||
pub fn verify_and_extract( | ||
equality_proof_context: &CiphertextCommitmentEqualityProofContext, | ||
ciphertext_validity_proof_context: &BatchedGroupedCiphertext3HandlesValidityProofContext, | ||
range_proof_context: &BatchedRangeProofContext, | ||
) -> Result<Self, TokenProofExtractionError> { | ||
// The equality proof context consists of the source ElGamal public key, the new | ||
// source available balance ciphertext, and the new source available | ||
// commitment. The public key and ciphertext should be returned as parts | ||
// of `TransferProofContextInfo` and the commitment should be checked | ||
// with range proof for consistency. | ||
let CiphertextCommitmentEqualityProofContext { | ||
pubkey: source_pubkey_from_equality_proof, | ||
ciphertext: new_source_ciphertext, | ||
commitment: new_source_commitment, | ||
} = equality_proof_context; | ||
|
||
// The ciphertext validity proof context consists of the destination ElGamal | ||
// public key, auditor ElGamal public key, and the transfer amount | ||
// ciphertexts. All of these fields should be returned as part of | ||
// `TransferProofContextInfo`. In addition, the commitments pertaining | ||
// to the transfer amount ciphertexts should be checked with range proof for | ||
// consistency. | ||
let BatchedGroupedCiphertext3HandlesValidityProofContext { | ||
first_pubkey: source_pubkey_from_validity_proof, | ||
second_pubkey: destination_pubkey, | ||
third_pubkey: auditor_pubkey, | ||
grouped_ciphertext_lo: transfer_amount_ciphertext_lo, | ||
grouped_ciphertext_hi: transfer_amount_ciphertext_hi, | ||
} = ciphertext_validity_proof_context; | ||
|
||
// The range proof context consists of the Pedersen commitments and bit-lengths | ||
// for which the range proof is proved. The commitments must consist of | ||
// three commitments pertaining to the new source available balance, the | ||
// low bits of the transfer amount, and high bits of the transfer | ||
// amount. These commitments must be checked for bit lengths `64`, `16`, | ||
// and `32`. | ||
let BatchedRangeProofContext { | ||
commitments: range_proof_commitments, | ||
bit_lengths: range_proof_bit_lengths, | ||
} = range_proof_context; | ||
|
||
// check that the source pubkey is consistent between equality and ciphertext | ||
// validity proofs | ||
if source_pubkey_from_equality_proof != source_pubkey_from_validity_proof { | ||
return Err(TokenProofExtractionError::ElGamalPubkeyMismatch); | ||
} | ||
|
||
// check that the range proof was created for the correct set of Pedersen | ||
// commitments | ||
let transfer_amount_commitment_lo = transfer_amount_ciphertext_lo.extract_commitment(); | ||
let transfer_amount_commitment_hi = transfer_amount_ciphertext_hi.extract_commitment(); | ||
|
||
let expected_commitments = [ | ||
*new_source_commitment, | ||
transfer_amount_commitment_lo, | ||
transfer_amount_commitment_hi, | ||
]; | ||
|
||
if !range_proof_commitments | ||
.iter() | ||
.zip(expected_commitments.iter()) | ||
.all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) | ||
{ | ||
return Err(TokenProofExtractionError::PedersenCommitmentMismatch); | ||
} | ||
|
||
// check that the range proof was created for the correct number of bits | ||
const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; | ||
const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16; | ||
const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32; | ||
const PADDING_BIT_LENGTH: u8 = 16; | ||
let expected_bit_lengths = [ | ||
REMAINING_BALANCE_BIT_LENGTH, | ||
TRANSFER_AMOUNT_LO_BIT_LENGTH, | ||
TRANSFER_AMOUNT_HI_BIT_LENGTH, | ||
PADDING_BIT_LENGTH, | ||
] | ||
.iter(); | ||
|
||
if !range_proof_bit_lengths | ||
.iter() | ||
.zip(expected_bit_lengths) | ||
.all(|(proof_len, expected_len)| proof_len == expected_len) | ||
{ | ||
return Err(TokenProofExtractionError::RangeProofLengthMismatch); | ||
} | ||
|
||
let transfer_pubkeys = TransferPubkeys { | ||
source: *source_pubkey_from_equality_proof, | ||
destination: *destination_pubkey, | ||
auditor: *auditor_pubkey, | ||
}; | ||
|
||
let context_info = TransferProofContext { | ||
ciphertext_lo: PodTransferAmountCiphertext(*transfer_amount_ciphertext_lo), | ||
ciphertext_hi: PodTransferAmountCiphertext(*transfer_amount_ciphertext_hi), | ||
transfer_pubkeys, | ||
new_source_ciphertext: *new_source_ciphertext, | ||
}; | ||
|
||
Ok(context_info) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "spl-token-confidential-transfer-proof-generation" | ||
version = "0.1.0" | ||
description = "Solana Program Library Confidential Transfer Proof Generation" | ||
authors = ["Solana Labs Maintainers <maintainers@solanalabs.com>"] | ||
repository = "https://github.com/solana-labs/solana-program-library" | ||
license = "Apache-2.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
curve25519-dalek = "3.2.1" | ||
solana-zk-sdk = "2.0.0" | ||
thiserror = "1.0.61" | ||
|
||
[dev-dependencies] | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] |
49 changes: 49 additions & 0 deletions
49
token/confidential-transfer/proof-generation/src/encryption.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
use solana_zk_sdk::encryption::{ | ||
elgamal::{DecryptHandle, ElGamalPubkey}, | ||
grouped_elgamal::{GroupedElGamal, GroupedElGamalCiphertext}, | ||
pedersen::{PedersenCommitment, PedersenOpening}, | ||
}; | ||
|
||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
#[repr(C)] | ||
pub struct TransferAmountCiphertext(pub(crate) GroupedElGamalCiphertext<3>); | ||
|
||
impl TransferAmountCiphertext { | ||
pub fn new( | ||
amount: u64, | ||
source_pubkey: &ElGamalPubkey, | ||
destination_pubkey: &ElGamalPubkey, | ||
auditor_pubkey: &ElGamalPubkey, | ||
) -> (Self, PedersenOpening) { | ||
let opening = PedersenOpening::new_rand(); | ||
let grouped_ciphertext = GroupedElGamal::<3>::encrypt_with( | ||
[source_pubkey, destination_pubkey, auditor_pubkey], | ||
amount, | ||
&opening, | ||
); | ||
|
||
(Self(grouped_ciphertext), opening) | ||
} | ||
|
||
pub fn get_commitment(&self) -> &PedersenCommitment { | ||
&self.0.commitment | ||
} | ||
|
||
pub fn get_source_handle(&self) -> &DecryptHandle { | ||
// `TransferAmountCiphertext` is a wrapper for `GroupedElGamalCiphertext<3>`, | ||
// which holds exactly three decryption handles. | ||
self.0.handles.first().unwrap() | ||
} | ||
|
||
pub fn get_destination_handle(&self) -> &DecryptHandle { | ||
// `TransferAmountCiphertext` is a wrapper for `GroupedElGamalCiphertext<3>`, | ||
// which holds exactly three decryption handles. | ||
self.0.handles.get(1).unwrap() | ||
} | ||
|
||
pub fn get_auditor_handle(&self) -> &DecryptHandle { | ||
// `TransferAmountCiphertext` is a wrapper for `GroupedElGamalCiphertext<3>`, | ||
// which holds exactly three decryption handles. | ||
self.0.handles.get(2).unwrap() | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
token/confidential-transfer/proof-generation/src/errors.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use {solana_zk_sdk::zk_elgamal_proof_program::errors::ProofGenerationError, thiserror::Error}; | ||
|
||
#[derive(Error, Clone, Debug, Eq, PartialEq)] | ||
pub enum TokenProofGenerationError { | ||
#[error("inner proof generation failed")] | ||
ProofGeneration(#[from] ProofGenerationError), | ||
#[error("not enough funds in account")] | ||
NotEnoughFunds, | ||
#[error("illegal amount bit length")] | ||
IllegalAmountBitLength, | ||
} |
Oops, something went wrong.