保密转账
什么是保密转账?
保密转账允许您在代币账户之间转移代币,而无需透露转账金额。这对于保护隐私的交易非常有用。只有转账金额和代币余额是保密的,代币账户地址仍然是公开的。
它是如何工作的?
保密转账扩展为 Token Extension 程序添加了 指令,允许您在账户之间转移代币而不透露转账金额。
Confidential Transfer Basic Overview
保密代币转账的基本流程如下:
- 创建一个带有保密转账扩展的 mint 账户。
- 为发送方和接收方创建带有保密转账扩展的代币账户。
- 将代币 mint 到发送方账户。
- 存入发送方的公开余额到保密待处理余额。
- 应用发送方的待处理余额到保密可用余额。
- 保密地将代币从发送方代币账户转移到接收方代币账户。
- 应用接收方的待处理余额到保密可用余额。
- 提取接收方的保密可用余额到公开余额。
有关保密转账流程中各步骤的更多详细信息,请参阅相应页面:
创建 Mint 账户
如何创建带有保密转账扩展的 mint 账户
创建代币账户
如何配置带有保密转账扩展的代币账户
存入代币
如何将代币存入保密待处理余额
应用待处理余额
如何将待处理余额应用到可用保密余额
提取代币
如何从保密可用余额中提取代币
转移代币
如何在代币账户之间保密地转移代币
下图展示了保密代币转账的基本流程的详细顺序:
Confidential Transfer Detailed Overview
示例代码
下面的 Rust 示例展示了与前面图表对应的保密代币转账步骤。这些步骤被分解为辅助函数以便于理解。
要运行该示例,请使用以下命令启动一个从主网克隆的包含 Token Extension Program 的本地 validator。您必须安装 Solana CLI 才能使用本地 validator。
Terminal
$solana-test-validator --clone-upgradeable-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --url https://api.mainnet-beta.solana.com -r
在撰写本文时,默认的本地 validator 尚未启用保密转账功能。您必须克隆主网的 Token Extension Program 才能运行示例代码。
use anyhow::{Context, Result};use solana_client::nonblocking::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,signature::{Keypair, Signer},transaction::Transaction,pubkey::Pubkey};use spl_associated_token_account::{get_associated_token_address_with_program_id, instruction::create_associated_token_account,};use spl_token_client::{client::{ProgramRpcClient, ProgramRpcClientSendTransaction},spl_token_2022::{extension::{confidential_transfer::{instruction::{configure_account, PubkeyValidityProofData},account_info::{WithdrawAccountInfo, TransferAccountInfo},ConfidentialTransferAccount,},BaseStateWithExtensions, ExtensionType,},id as token_2022_program_id,instruction::reallocate,solana_zk_sdk::encryption::{auth_encryption::*, elgamal::*},},token::{ExtensionInitializationParams, Token},};use spl_token_confidential_transfer_proof_extraction::instruction::{ProofData, ProofLocation};use spl_token_confidential_transfer_proof_generation::withdraw::WithdrawProofData;use std::sync::Arc;/// Define a type alias for the Token client to simplify type signaturestype TokenClient = Token<ProgramRpcClientSendTransaction>;/// Demonstrates the complete lifecycle of confidential tokens:/// - Creating a mint with confidential transfer extension/// - Creating token accounts configured for confidential transfer/// - Minting tokens/// - Depositing tokens from public balance to confidential pending balance/// - Applying pending balance to available balance/// - Transferring tokens confidentially between token accounts/// - Withdrawing tokens from confidential available balance to public balance#[tokio::main]async fn main() -> Result<()> {// Create connection to local test validator with confirmed commitment level// This ensures transactions are confirmed before proceedinglet rpc_client = Arc::new(RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),));// Load the default Solana CLI keypair to use as the fee payerlet payer = Arc::new(load_keypair()?);println!("Payer: {}", payer.pubkey());// Generate a new keypair for the mint// This pubkey will be the address of the token mintlet mint = Keypair::new();println!("Mint keypair generated: {}", mint.pubkey());// Number of decimals for the mint// This determines the smallest unit of the token (e.g., 2 decimals means 0.01 is the smallest unit)let decimals = 2;// Create a program clientlet program_client = ProgramRpcClient::new(rpc_client.clone(), ProgramRpcClientSendTransaction);// Create a token client// This client provides high-level methods to interact with the token programlet token = Token::new(Arc::new(program_client),&token_2022_program_id(),&mint.pubkey(),Some(decimals),payer.clone(),);// Create a new mint with confidential transfer extension// This initializes the token mint with confidential transfer enabledcreate_mint(&token, &mint, payer.clone()).await?;// Create first token account (sender)// This account will be used to send tokens confidentiallylet (sender_pubkey, sender_elgamal_keypair, sender_aes_key) = create_token_account(rpc_client.clone(),payer.clone(),&mint,payer.clone(),&token_2022_program_id(),).await?;// Create second token account (recipient) with a new owner// This account will receive tokens confidentiallylet recipient_owner = Arc::new(Keypair::new());println!("Recipient owner: {}", recipient_owner.pubkey());// Fund the recipient owner account with SOL// This ensures the recipient can pay for transaction feesfund_account(rpc_client.clone(),payer.clone(),&recipient_owner.pubkey(),10_000_000, // 0.01 SOL in lamports).await?;// Create the recipient token account// This account will receive tokens from the senderlet (recipient_pubkey, recipient_elgamal_keypair, recipient_aes_key) = create_token_account(rpc_client.clone(),recipient_owner.clone(),&mint,recipient_owner.clone(),&token_2022_program_id(),).await?;// Mint tokens to sender account// This creates new tokens and increases the sender's token account public balancelet amount = 100 * 10u64.pow(decimals as u32); // 100 tokensmint_tokens(&token, &sender_pubkey, &payer, amount).await?;// Deposit tokens to confidential pending balance// This converts public balance to confidential pending balancedeposit_tokens(&token, &sender_pubkey, &payer, amount, decimals).await?;// Apply pending balance to make funds available// This converts confidential pending balance to confidential available balanceapply_pending_balance(&token,&sender_pubkey,&payer,&sender_elgamal_keypair,&sender_aes_key,).await?;// Transfer tokens from sender to recipient// This sends tokens confidentially from sender token account to recipient token accountlet transfer_amount = 50 * 10u64.pow(decimals as u32); // 50 tokenstransfer_tokens(&token,&sender_pubkey,&payer,&sender_elgamal_keypair,&sender_aes_key,&recipient_pubkey,recipient_elgamal_keypair.pubkey(),transfer_amount,payer.clone(),).await?;// Apply pending balance on recipient token account// This converts confidential pending balance to confidential available balanceapply_pending_balance(&token,&recipient_pubkey,&recipient_owner,&recipient_elgamal_keypair,&recipient_aes_key,).await?;// Withdraw half of the tokens from recipient's confidential available balance// This converts confidential available balance to public balancelet withdraw_amount = 25 * 10u64.pow(decimals as u32); // half of the 50 tokens receivedwithdraw_tokens(&token,&recipient_pubkey,&recipient_owner,&recipient_elgamal_keypair,&recipient_aes_key,withdraw_amount,recipient_owner.clone(),decimals,).await?;println!("\nAll operations completed successfully!");println!("Sender Account: {}", sender_pubkey);println!("Recipient Account: {}", recipient_pubkey);Ok(())}/// Load the keypair from the default Solana CLI keypair path////// Returns:/// - Result<Keypair>: The loaded keypair or an error if loading failsfn load_keypair() -> Result<Keypair> {// Get the default keypair path from the home directorylet keypair_path = dirs::home_dir().context("Could not find home directory")?.join(".config/solana/id.json");// Read the keypair filelet file = std::fs::File::open(&keypair_path)?;let keypair_bytes: Vec<u8> = serde_json::from_reader(file)?;// Create keypairlet keypair = Keypair::from_bytes(&keypair_bytes)?;Ok(keypair)}/// Create a new token mint with confidential transfer extension////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - mint: Reference to the keypair that will be used as the mint account's address/// - payer: Arc-wrapped keypair that will pay for the transaction fees and serve as the mint authority////// Returns:/// - Result<()>: Success or errorasync fn create_mint(token: &TokenClient, mint: &Keypair, payer: Arc<Keypair>) -> Result<()> {println!("Creating token mint with confidential transfer extension...");// Create extension initialization parameters for the mint// This configures the confidential transfer extension on the mintlet extension_initialization_params =vec![ExtensionInitializationParams::ConfidentialTransferMint {authority: Some(payer.pubkey()), // Authority that can modify confidential transfer settingsauto_approve_new_accounts: true, // Automatically approve new accounts for confidential transfersauditor_elgamal_pubkey: None, // No auditor for this example}];// Create and initialize the mint with the ConfidentialTransferMint extensionlet transaction_signature = token.create_mint(&payer.pubkey(), // Mint authoritySome(&payer.pubkey()), // Freeze authority (optional)extension_initialization_params, // Extension parameters&[mint], // Signers needed for the transaction).await?;println!("Mint Address: {}", mint.pubkey());println!("Mint Creation Transaction Signature: {}",transaction_signature);Ok(())}/// Fund an account with SOL////// Arguments:/// - rpc_client: Arc-wrapped RPC client for communicating with the Solana cluster/// - payer: Arc-wrapped keypair that will send the SOL/// - recipient: Public key of the account that will receive the SOL/// - amount: Amount of lamports (1 SOL = 1,000,000,000 lamports) to send////// Returns:/// - Result<()>: Success or errorasync fn fund_account(rpc_client: Arc<RpcClient>,payer: Arc<Keypair>,recipient: &Pubkey,amount: u64,) -> Result<()> {println!("Funding account {} with {} lamports...", recipient, amount);// Create and send a transaction to transfer SOL from payer to recipientlet fund_signature = rpc_client.send_and_confirm_transaction(&Transaction::new_signed_with_payer(&[solana_sdk::system_instruction::transfer(&payer.pubkey(), // Fromrecipient, // Toamount, // Amount in lamports)],Some(&payer.pubkey()), // Fee payer&[&payer], // Signersrpc_client.get_latest_blockhash().await?, // Recent blockhash)).await?;println!("Fund Transaction Signature: {}", fund_signature);Ok(())}/// Create and configure a token account for confidential transfers////// Arguments:/// - rpc_client: Arc-wrapped RPC client for communicating with the Solana cluster/// - payer: Arc-wrapped keypair that will pay for the transaction fees/// - mint: Reference to the keypair of the token mint/// - owner: Arc-wrapped keypair that will own the token account/// - token_program_id: Public key of the token program (Token-2022)////// Returns:/// - Result<(Pubkey, ElGamalKeypair, AeKey)>: The token account address, ElGamal keypair, and AES key for encryptionasync fn create_token_account(rpc_client: Arc<RpcClient>,payer: Arc<Keypair>,mint: &Keypair,owner: Arc<Keypair>,token_program_id: &Pubkey,) -> Result<(Pubkey, ElGamalKeypair, AeKey)> {println!("Creating token account for owner: {}", owner.pubkey());// Get the associated token account address for the owner// This is a deterministic address based on the owner and mintlet token_account_pubkey = get_associated_token_address_with_program_id(&owner.pubkey(),&mint.pubkey(),token_program_id,);println!("Token Account Address: {}", token_account_pubkey);// Step 1: Create the associated token account// This creates a token account at the deterministic addresslet create_associated_token_account_instruction = create_associated_token_account(&payer.pubkey(), // Funding account&owner.pubkey(), // Wallet address&mint.pubkey(), // Mint addresstoken_program_id, // Token program ID);// Step 2: Reallocate the token account to include space for the ConfidentialTransferAccount extension// This increases the account size to accommodate the confidential transfer datalet reallocate_instruction = reallocate(token_program_id, // Token program ID&token_account_pubkey, // Token account to reallocate&payer.pubkey(), // Payer for the reallocation&owner.pubkey(), // Owner of the token account&[&owner.pubkey()], // Signers required for the reallocation&[ExtensionType::ConfidentialTransferAccount], // Extension to allocate space for)?;// Step 3: Generate the ElGamal keypair and AES key for token account// These are used for encrypting and decrypting confidential token amountslet elgamal_keypair = ElGamalKeypair::new_from_signer(&owner, &token_account_pubkey.to_bytes()).expect("Failed to create ElGamal keypair");let aes_key = AeKey::new_from_signer(&owner, &token_account_pubkey.to_bytes()).expect("Failed to create AES key");// The maximum number of Deposit and Transfer instructions that can// credit pending_balance before the ApplyPendingBalance instruction must be executedlet maximum_pending_balance_credit_counter = 65536;// Initial token balance is 0// This is encrypted using the AES keylet decryptable_balance = aes_key.encrypt(0);// Generate the proof data client-side// This proves that the ElGamal public key is validlet proof_data = PubkeyValidityProofData::new(&elgamal_keypair).map_err(|_| anyhow::anyhow!("Failed to generate proof data"))?;// Indicate that proof is included as instruction data// This specifies where to find the proof data in the transactionlet proof_location =ProofLocation::InstructionOffset(1.try_into()?, ProofData::InstructionData(&proof_data));// Step 4: Create instructions to configure the account for confidential transfers// This initializes the confidential transfer extension on the token accountlet configure_account_instructions = configure_account(token_program_id, // Token program ID&token_account_pubkey, // Token account to configure&mint.pubkey(), // Mint of the token&decryptable_balance.into(), // Initial encrypted balancemaximum_pending_balance_credit_counter, // Maximum pending balance credit counter&owner.pubkey(), // Owner of the token account&[], // Additional signers (none needed)proof_location, // Location of the proof data)?;// Combine all instructionslet mut instructions = vec![create_associated_token_account_instruction,reallocate_instruction,];instructions.extend(configure_account_instructions);// Create and send the transactionlet recent_blockhash = rpc_client.get_latest_blockhash().await?;let transaction = Transaction::new_signed_with_payer(&instructions, // Instructions to executeSome(&payer.pubkey()), // Fee payer&[&payer], // Signersrecent_blockhash, // Recent blockhash);let transaction_signature = rpc_client.send_and_confirm_transaction(&transaction).await?;println!("Create Token Account Transaction Signature: {}",transaction_signature);Ok((token_account_pubkey, elgamal_keypair, aes_key))}/// Mint tokens to a token account////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account that will receive the tokens/// - mint_authority: Arc-wrapped keypair that has authority to mint tokens/// - amount: Amount of tokens to mint (in the smallest units)////// Returns:/// - Result<()>: Success or errorasync fn mint_tokens(token: &TokenClient,token_account: &Pubkey,mint_authority: &Arc<Keypair>,amount: u64,) -> Result<()> {println!("Minting {} tokens to account: {}", amount, token_account);// Mint tokens to the specified token accountlet mint_signature = token.mint_to(token_account, // Destination token account&mint_authority.pubkey(), // Mint authorityamount, // Amount to mint&[&mint_authority], // Signers).await?;println!("Token Minting Transaction Signature: {}", mint_signature);Ok(())}/// Deposit tokens to confidential state////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account with public balance/// - owner: Arc-wrapped keypair that owns the token account/// - amount: Amount of tokens to deposit into confidential pending balance/// - decimals: Number of decimal for the mint////// Returns:/// - Result<()>: Success or errorasync fn deposit_tokens(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,amount: u64,decimals: u8,) -> Result<()> {println!("Depositing {} tokens to confidential pending balance...", amount);// Deposit tokens to confidential pending balancelet deposit_signature = token.confidential_transfer_deposit(token_account, // Token account&owner.pubkey(), // Owner of the token accountamount, // Amount to depositdecimals, // Decimal for the mint&[&owner], // Signers).await?;println!("Confidential Transfer Deposit Signature: {}",deposit_signature);Ok(())}/// Apply pending balance to make funds available////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account/// - owner: Arc-wrapped keypair that owns the token account/// - elgamal_keypair: ElGamal keypair for decrypting confidential amounts/// - aes_key: AES key for decrypting confidential amounts////// Returns:/// - Result<()>: Success or errorasync fn apply_pending_balance(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,elgamal_keypair: &ElGamalKeypair,aes_key: &AeKey,) -> Result<()> {println!("Applying pending balance for account: {}", token_account);// Apply pending balance to convert confidential pending balance to confidential available balancelet apply_signature = token.confidential_transfer_apply_pending_balance(token_account, // Token account&owner.pubkey(), // Owner of the token accountNone, // Optional auditor public key (none in this example)elgamal_keypair.secret(), // ElGamal secret key for decryptionaes_key, // AES key for decryption&[&owner], // Signers).await?;println!("Apply Pending Balance Signature: {}", apply_signature);Ok(())}/// Withdraw tokens from confidential state////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - token_account: Public key of the token account/// - owner: Arc-wrapped keypair that owns the token account/// - elgamal_keypair: ElGamal keypair for decrypting confidential amounts/// - aes_key: AES key for decrypting confidential amounts/// - amount: Amount of tokens to withdraw from confidential available balance/// - payer: Arc-wrapped keypair that will pay for the transaction fees/// - decimals: Number of decimal for the mint////// Returns:/// - Result<()>: Success or error#[allow(clippy::too_many_arguments)]async fn withdraw_tokens(token: &TokenClient,token_account: &Pubkey,owner: &Arc<Keypair>,elgamal_keypair: &ElGamalKeypair,aes_key: &AeKey,amount: u64,payer: Arc<Keypair>,decimals: u8,) -> Result<()> {println!("Withdrawing {} tokens from confidential available balance to public balance...", amount);// Get the token account data to access the confidential transfer extension statelet token_account_data = token.get_account_info(token_account).await?;// Unpack the ConfidentialTransferAccount extension portion of the token account datalet extension_data = token_account_data.get_extension::<ConfidentialTransferAccount>()?;// Confidential Transfer extension state from token account needed to construct a `Withdraw` instructionlet withdraw_account_info =WithdrawAccountInfo::new(extension_data,);// Create keypairs for the proof context state accounts// These accounts will store the context state from the equality and range proofs required for the withdrawallet equality_proof_context_state_keypair = Keypair::new();let equality_proof_context_state_pubkey = equality_proof_context_state_keypair.pubkey();let range_proof_context_state_keypair = Keypair::new();let range_proof_context_state_pubkey = range_proof_context_state_keypair.pubkey();// Create a withdraw proof data// This generates the zero-knowledge proofs needed for the withdrawallet WithdrawProofData {equality_proof_data,range_proof_data,} = withdraw_account_info.generate_proof_data(amount, elgamal_keypair, aes_key)?;// Generate the equality proof context state account// This creates an account to store the equality proof context stateprintln!("Creating equality proof context state account...");let equality_proof_signature = token.confidential_transfer_create_context_state_account(&equality_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&equality_proof_data, // Proof data to verifyfalse, // Not a range proof&[&equality_proof_context_state_keypair], // Signers).await?;println!("Create Equality Proof Context State Account: {}",equality_proof_signature);// Generate the range proof context state account// This creates an account to store the range proof context stateprintln!("Creating range proof context state account...");let range_proof_signature = token.confidential_transfer_create_context_state_account(&range_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&range_proof_data, // Proof data to verifytrue, // Is a range proof&[&range_proof_context_state_keypair], // Signers).await?;println!("Create Range Proof Context State Account: {}",range_proof_signature);// Perform the withdrawal// This converts confidential available balance back to public balanceprintln!("Executing withdrawal transaction...");let withdraw_signature = token.confidential_transfer_withdraw(token_account, // Token account&owner.pubkey(), // Owner of the token accountSome(&spl_token_client::token::ProofAccount::ContextAccount(equality_proof_context_state_pubkey, // Equality proof account)),Some(&spl_token_client::token::ProofAccount::ContextAccount(range_proof_context_state_pubkey, // Range proof account)),amount, // Amount to withdrawdecimals, // Decimal for the mintSome(withdraw_account_info), // Data from token account required for withdrawalelgamal_keypair, // ElGamal keypair for decryptionaes_key, // AES key for decryption&[&owner], // Signers).await?;println!("Withdraw Transaction Signature: {}", withdraw_signature);// Close the context state accounts to recover rent// This reclaims the SOL used to rent space for the proof accountsprintln!("Closing proof context state accounts...");let close_equality_signature = token.confidential_transfer_close_context_state_account(&equality_proof_context_state_pubkey, // Account to closetoken_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Equality Proof Context State Account: {}",close_equality_signature);let close_range_signature = token.confidential_transfer_close_context_state_account(&range_proof_context_state_pubkey, // Account to closetoken_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Range Proof Context State Account: {}",close_range_signature);Ok(())}/// Transfer tokens from one confidential account to another////// Arguments:/// - token: Reference to the token client for interacting with the Token-2022 program/// - sender_token_account: Public key of the sender's token account/// - sender_owner: Arc-wrapped keypair that owns the sender's token account/// - sender_elgamal_keypair: ElGamal keypair for the sender's account/// - sender_aes_key: AES key for encrypting sender's confidential amounts/// - recipient_token_account: Public key of the recipient's token account/// - recipient_elgamal_pubkey: ElGamal public key of the recipient/// - amount: Amount of tokens to transfer/// - payer: Arc-wrapped keypair that will pay for the transaction fees////// Returns:/// - Result<()>: Success or error#[allow(clippy::too_many_arguments)]async fn transfer_tokens(token: &TokenClient,sender_token_account: &Pubkey,sender_owner: &Arc<Keypair>,sender_elgamal_keypair: &ElGamalKeypair,sender_aes_key: &AeKey,recipient_token_account: &Pubkey,recipient_elgamal_pubkey: &ElGamalPubkey,amount: u64,payer: Arc<Keypair>,) -> Result<()> {println!("Performing confidential transfer of {} tokens...", amount);// Get the token account data to access the confidential transfer extensionlet token_account_data = token.get_account_info(sender_token_account).await?;let extension_data = token_account_data.get_extension::<ConfidentialTransferAccount>()?;// Confidential Transfer extension state from token account needed to construct a `Transfer` instructionlet transfer_account_info =TransferAccountInfo::new(extension_data,);// Generate the proof data for the transfer// This creates the zero-knowledge proofs needed for the transferlet transfer_proof_data = transfer_account_info.generate_split_transfer_proof_data(amount, // Amount to transfersender_elgamal_keypair, // Sender's ElGamal keypairsender_aes_key, // Sender's AES keyrecipient_elgamal_pubkey, // Recipient's ElGamal public keyNone, // Auditor ElGamal public key (none in this example))?;// Create proof context state accounts// These accounts will store the context state from the equality, ciphertext validity, and range proofs required for the transferlet equality_proof_context_state_keypair = Keypair::new();let equality_proof_context_state_pubkey = equality_proof_context_state_keypair.pubkey();let ciphertext_validity_proof_context_state_keypair = Keypair::new();let ciphertext_validity_proof_context_state_pubkey =ciphertext_validity_proof_context_state_keypair.pubkey();let range_proof_context_state_keypair = Keypair::new();let range_proof_context_state_pubkey = range_proof_context_state_keypair.pubkey();// Equality proof - proves that two ciphertexts encrypt the same amountprintln!("Creating equality proof context state account...");let equality_proof_signature = token.confidential_transfer_create_context_state_account(&equality_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.equality_proof_data, // Proof data to verifyfalse, // Not a range proof&[&equality_proof_context_state_keypair], // Signers).await?;println!("Create Equality Proof Context State Account: {}",equality_proof_signature);// Ciphertext validity proof - proves that the ciphertext is properly formedprintln!("Creating ciphertext validity proof context state account...");let ciphertext_proof_signature = token.confidential_transfer_create_context_state_account(&ciphertext_validity_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.proof_data, // Proof data to verifyfalse, // Not a range proof&[&ciphertext_validity_proof_context_state_keypair], // Signers).await?;println!("Create Ciphertext Validity Proof Context State Account: {}",ciphertext_proof_signature);// Range proof - proves that the encrypted amount is within a valid range and non-negativeprintln!("Creating range proof context state account...");let range_proof_signature = token.confidential_transfer_create_context_state_account(&range_proof_context_state_pubkey, // Account to create&payer.pubkey(), // Payer for the account creation&transfer_proof_data.range_proof_data, // Proof to verifytrue, // Is a range proof&[&range_proof_context_state_keypair], // Signers).await?;println!("Create Range Proof Context State Account: {}", range_proof_signature);// Execute the confidential transfer// This transfers tokens confidentially from sender token account to recipient token accountprintln!("Executing confidential transfer transaction...");// Create a ProofAccountWithCiphertext for the ciphertext validity proof// This combines the proof account with the ciphertext datalet ciphertext_validity_proof_account_with_ciphertext =spl_token_client::token::ProofAccountWithCiphertext {proof_account: spl_token_client::token::ProofAccount::ContextAccount(ciphertext_validity_proof_context_state_pubkey, // Proof account),ciphertext_lo: transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.ciphertext_lo, // Low 128 bits of ciphertextciphertext_hi: transfer_proof_data.ciphertext_validity_proof_data_with_ciphertext.ciphertext_hi, // High 128 bits of ciphertext};// Perform the confidential transferlet transfer_signature = token.confidential_transfer_transfer(sender_token_account, // Source token accountrecipient_token_account, // Destination token account&sender_owner.pubkey(), // Owner of the source accountSome(&spl_token_client::token::ProofAccount::ContextAccount(equality_proof_context_state_pubkey, // Equality proof context state account)),Some(&ciphertext_validity_proof_account_with_ciphertext), // Ciphertext validity proofSome(&spl_token_client::token::ProofAccount::ContextAccount(range_proof_context_state_pubkey, // Range proof account)),amount, // Amount to transferNone, // Optional auditor info (none in this case)sender_elgamal_keypair, // Sender's ElGamal keypairsender_aes_key, // Sender's AES keyrecipient_elgamal_pubkey, // Recipient's ElGamal public keyNone, // Optional auditor ElGamal public key&[&sender_owner], // Signers).await?;println!("Confidential Transfer Signature: {}", transfer_signature);// Close the context state accounts to recover rent// This reclaims the SOL used to rent space for the proof accountsprintln!("Closing proof context state accounts...");// Close the equality proof accountlet close_equality_signature = token.confidential_transfer_close_context_state_account(&equality_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Equality Proof Context State Account: {}",close_equality_signature);// Close the ciphertext validity proof accountlet close_ciphertext_signature = token.confidential_transfer_close_context_state_account(&ciphertext_validity_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Ciphertext Validity Proof Context State Account: {}",close_ciphertext_signature);// Close the range proof accountlet close_range_signature = token.confidential_transfer_close_context_state_account(&range_proof_context_state_pubkey, // Account to closesender_token_account, // Destination for the rent&payer.pubkey(), // Authority to close the account&[&payer], // Signers).await?;println!("Close Range Proof Context State Account: {}",close_range_signature);Ok(())}
ConsolePowered by Mirror
Click to execute the code.
保密转账指令
完整的保密转账扩展 指令 如下:
指令 | 描述 |
---|---|
InitializeMint | 设置用于保密转账的 mint account。此指令必须与 TokenInstruction::InitializeMint 指令在同一事务中包含。 |
UpdateMint | 更新 mint 的保密转账设置。 |
ConfigureAccount | 设置用于保密转账的 token account。 |
ApproveAccount | 如果 mint 要求新 token account 获得批准,则批准该 token account 进行保密转账。 |
EmptyAccount | 清空待处理和可用的保密余额以关闭 token account。 |
Deposit | 将公共代币余额转换为待处理的保密余额。 |
Withdraw | 将可用的保密余额转换回公共余额。 |
Transfer | 在 token account 之间进行保密代币转账。 |
ApplyPendingBalance | 在存款或转账后将待处理余额转换为可用余额。 |
EnableConfidentialCredits | 允许 token account 接收保密代币转账。 |
DisableConfidentialCredits | 阻止传入的保密转账,同时仍允许公共转账。 |
EnableNonConfidentialCredits | 允许 token account 接收公共代币转账。 |
DisableNonConfidentialCredits | 阻止常规转账,使账户仅接收保密转账。 |
TransferWithFee | 在 token account 之间进行保密代币转账并收取费用。 |
ConfigureAccountWithRegistry | 使用 ElGamalRegistry 账户而不是 VerifyPubkeyValidity 证明来配置 token account 进行保密转账的替代方法。 |
Is this page helpful?