Confidential Transfer
Confidential Transfersとは何ですか?
Confidential transfersを使用すると、転送金額を明かさずにトークンアカウント間でトークンを転送できます。これはプライバシーを保護するトランザクションに役立ちます。転送金額とトークン残高のみがプライベートであり、トークンアカウントのアドレスは公開されたままです。
- プロトコル概要 - 基盤となる暗号プロトコルの詳細
- クイックスタートガイド - セットアップと基本的なCLIコマンド
- Confidential Balances クックブック - Confidential Transfer拡張機能の使用方法に関するコードスニペット
どのように機能しますか?
Confidential Transfer拡張機能は、転送金額を明かさずにアカウント間でトークンを転送できるようにする命令をToken Extensions Programに追加します。
機密トークン転送の基本的なフローは次のとおりです:
- Confidential transfer拡張機能を持つミントアカウントを作成します。
- 送信者と受信者のためのConfidential transfer拡張機能を持つトークンアカウントを作成します。
- 送信者アカウントにトークンをミントします。
- 送信者の公開残高を機密保留残高にデポジットします。
- 送信者の保留残高を機密利用可能残高に適用します。
- 送信者トークンアカウントから受信者トークンアカウントにトークンを機密に転送します。
- 受信者の保留残高を機密利用可能残高に適用します。
- 受信者の機密利用可能残高を公開残高に引き出します。
機密転送フローのステップの詳細については、対応するページを参照してください:
ミントアカウントの作成
Confidential Transfer拡張機能を持つミントアカウントを作成する方法
トークンアカウントの作成
Confidential Transfer拡張機能でトークンアカウントを設定する方法
トークンのデポジット
トークンを機密保留残高にデポジットする方法
保留残高の適用
保留残高を利用可能な機密残高に適用する方法
トークンの引き出し
機密利用可能残高からトークンを引き出す方法
トークンの転送
トークンアカウント間で機密にトークンを転送する方法
以下の図は、機密トークン転送の基本フローの詳細なシーケンスを示しています:
サンプルコード
以下のRustの例は、前述の図に対応する機密トークン転送のステップを示しています。わかりやすくするために、ステップはヘルパー関数に分けられています。
例を実行するには、以下のコマンドを使用して、メインネットからクローンしたToken Extension Programでローカルvalidatorを起動します。ローカルvalidatorを使用するには、Solana CLIがインストールされている必要があります。
$solana-test-validator --clone-upgradeable-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --url https://api.mainnet-beta.solana.com -r
執筆時点では、Confidential Transfersはデフォルトのローカル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(())}
Confidential Transfer命令
Confidential Transfer拡張機能の完全な命令リストは以下の通りです:
命令 | 説明 |
---|---|
InitializeMint | 機密転送用のmint accountを設定します。この命令は、*rsTokenInstruction::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 | *rsVerifyPubkeyValidity 証明の代わりにrsElGamalRegistry *アカウントを使用して、機密転送用のtoken accountを構成する代替方法です。 |
Is this page helpful?