Confidential Transfer uzantısıyla token account nasıl oluşturulur
Confidential Transfer uzantısı, token account'a ekstra durum bilgisi ekleyerek gizli token transferlerini mümkün kılar. Bu bölüm, bu uzantı etkinleştirilmiş bir token account'ın nasıl oluşturulacağını açıklar.
Aşağıdaki diyagram, Confidential Transfer uzantısıyla bir token account oluşturulurken izlenen adımları göstermektedir:
Confidential Transfer Token Account Durumu
Uzantı, token account'a ConfidentialTransferAccount durumunu ekler:
#[repr(C)]#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]pub struct ConfidentialTransferAccount {/// `true` if this account has been approved for use. All confidential/// transfer operations for the account will fail until approval is/// granted.pub approved: PodBool,/// The public key associated with ElGamal encryptionpub elgamal_pubkey: PodElGamalPubkey,/// The low 16 bits of the pending balance (encrypted by `elgamal_pubkey`)pub pending_balance_lo: EncryptedBalance,/// The high 48 bits of the pending balance (encrypted by `elgamal_pubkey`)pub pending_balance_hi: EncryptedBalance,/// The available balance (encrypted by `encryption_pubkey`)pub available_balance: EncryptedBalance,/// The decryptable available balancepub decryptable_available_balance: DecryptableBalance,/// If `false`, the extended account rejects any incoming confidential/// transferspub allow_confidential_credits: PodBool,/// If `false`, the base account rejects any incoming transferspub allow_non_confidential_credits: PodBool,/// The total number of `Deposit` and `Transfer` instructions that have/// credited `pending_balance`pub pending_balance_credit_counter: PodU64,/// The maximum number of `Deposit` and `Transfer` instructions that can/// credit `pending_balance` before the `ApplyPendingBalance`/// instruction is executedpub maximum_pending_balance_credit_counter: PodU64,/// The `expected_pending_balance_credit_counter` value that was included in/// the last `ApplyPendingBalance` instructionpub expected_pending_balance_credit_counter: PodU64,/// The actual `pending_balance_credit_counter` when the last/// `ApplyPendingBalance` instruction was executedpub actual_pending_balance_credit_counter: PodU64,}
ConfidentialTransferAccount, gizli transferleri yönetmek için çeşitli
alanlar içerir:
-
approved: Hesabın gizli transferler için onay durumu. Eğer mint account'ın
auto_approve_new_accountsyapılandırmasıtrueolarak ayarlanmışsa, tüm token account'lar gizli transferler için otomatik olarak onaylanır. -
elgamal_pubkey: Bakiyeleri ve transfer tutarlarını şifrelemek için kullanılan ElGamal pubkey'i.
-
pending_balance_lo: Bekleyen bakiyenin şifrelenmiş alt 16 biti. Bakiye, verimli şifre çözme için yüksek ve düşük parçalara bölünür.
-
pending_balance_hi: Bekleyen bakiyenin şifrelenmiş üst 48 biti. Bakiye, verimli şifre çözme için yüksek ve düşük parçalara bölünür.
-
available_balance: Transferler için kullanılabilir şifrelenmiş bakiye.
-
decryptable_available_balance: Hesap sahibi tarafından verimli şifre çözme için Gelişmiş Şifreleme Standardı (AES) anahtarıyla şifrelenmiş kullanılabilir bakiye.
-
allow_confidential_credits: True ise, gelen gizli transferlere izin verir.
-
allow_non_confidential_credits: True ise, gelen gizli olmayan transferlere izin verir.
-
pending_balance_credit_counter: Yatırma ve transfer talimatlarından gelen bekleyen bakiye kredilerini sayar.
-
maximum_pending_balance_credit_counter: Bekleyen bakiyeyi kullanılabilir bakiyeye dönüştürmek için
ApplyPendingBalancetalimatının gerekli kılınmasından önce izin verilen bekleyen kredi sayısı sınırı. -
expected_pending_balance_credit_counter:
ApplyPendingBalancetalimatının son işlendiği sırada istemci tarafından instruction data aracılığıyla sağlananpending_balance_credit_counterdeğeri. -
actual_pending_balance_credit_counter: token account üzerindeki
pending_balance_credit_counterdeğeri, sonApplyPendingBalancetalimatının işlendiği andaki değerdir.
Bekleyen ve Kullanılabilir Bakiye
Gizli bakiyeler, DoS saldırılarını önlemek amacıyla bekleyen ve kullanılabilir bakiyeler olarak ikiye ayrılır. Bu ayrım olmasaydı, bir saldırgan bir token account'a defalarca token göndererek token account sahibinin token transferi yapabilme yeteneğini engelleyebilirdi. Token account sahibi, işlem gönderildiği an ile işlendiği an arasında şifreli bakiye değişeceğinden token transferi yapamaz hale gelirdi ve bu durum işlemin başarısız olmasına yol açardı.
Tüm yatırma ve transfer tutarları başlangıçta bekleyen bakiyeye eklenir. Token
account sahipleri, bekleyen bakiyeyi kullanılabilir bakiyeye dönüştürmek için
ApplyPendingBalance talimatını kullanmalıdır. Gelen transferler veya
yatırma işlemleri, token account'ın kullanılabilir bakiyesini etkilemez.
Bekleyen Bakiye Yüksek/Düşük Bölünmesi
Gizli bekleyen bakiye, pending_balance_lo ve pending_balance_hi olarak ikiye
ayrılır; bunun nedeni ElGamal şifre çözme işleminin büyük sayılar için daha
fazla hesaplama gerektirmesidir. Şifreli metin aritmetik uygulamasını
burada
bulabilirsiniz; bu uygulama ApplyPendingBalance talimatında
burada
kullanılmaktadır.
Bekleyen Bakiye Kredi Sayaçları
Bekleyen bakiyeyi kullanılabilir bakiyeye dönüştürmek için
ApplyPendingBalance talimatı çağrıldığında:
-
İstemci, mevcut bekleyen ve kullanılabilir bakiyeleri sorgular, toplamı şifreler ve token account sahibinin AES anahtarı kullanılarak şifrelenmiş bir
decryptable_available_balancesağlar. -
Beklenen ve gerçek bekleyen kredi sayaçları,
ApplyPendingBalancetalimatının oluşturulduğu an ile işlendiği an arasındaki sayaç değeri değişikliklerini izler:expected_pending_balance_credit_counter: İstemcininApplyPendingBalancetalimatını oluşturduğu andakipending_balance_credit_counterdeğeriactual_pending_balance_credit_counter:ApplyPendingBalancetalimatının işlendiği andaki token account üzerindekipending_balance_credit_counterdeğeri
Beklenen/gerçek sayaçların eşleşmesi, decryptable_available_balance değerinin
available_balance ile uyuştuğunu gösterir.
Bir token account'ın durumu decryptable_available_balance değerini okumak için
getirildiğinde, beklenen/gerçek sayaç değerlerinin farklı olması durumunda
istemcinin doğru bakiyeyi hesaplamak için sayaç farkına karşılık gelen en son
para yatırma/transfer talimatlarını araması gerekir.
Bakiye Mutabakat Süreci
Beklenen ve gerçek bekleyen bakiye sayaçları farklı olduğunda,
decryptable_available_balance değerini mutabık kılmak için şu adımları
izleyin:
- token account'tan
decryptable_available_balanceile başlayın - Sayaç farkına (gerçek - beklenen) kadar para yatırma ve transfer
talimatlarını içeren en son işlemleri getirin:
- Para yatırma talimatlarından kamuya açık tutarları ekleyin
- Transfer talimatlarından hedef şifreli metin tutarlarını çözümleyip ekleyin
Gerekli Talimatlar
Gizli transferler için bir token account oluşturmak ve yapılandırmak, tek bir işleme sığan aşağıdaki talimatları kullanır:
-
Token Account'ı Oluşturun: Belirleyici adresinde token account'ı oluşturmak için Associated Token Program'ın
AssociatedTokenAccountInstruction::Createtalimatını çağırın. -
Hesap Alanını Yeniden Tahsis Edin:
ConfidentialTransferAccountdurumu için alan eklemek amacıyla Token Extension Program'ınTokenInstruction::Reallocatetalimatını çağırın. -
Pubkey Geçerlilik Kanıtını Doğrulayın: ZK ElGamal Proof programına ait bir hesap oluşturun, ardından kanıtı doğrulamak ve doğrulanmış sonucu o bağlam durum hesabında depolamak için
VerifyPubkeyValiditytalimatını çağırın. -
Gizli Transferleri Yapılandırın:
ConfidentialTransferAccountdurumunu başlatmak için kanıt bağlam durum hesabınıProofLocation::ContextStateAccountaracılığıyla referans alarak Token Extension Program'ın ConfidentialTransferInstruction::ConfigureAccount talimatını çağırın.
Yalnızca token hesabı sahibi, bir token hesabını gizli transferler için yapılandırabilir.
ConfigureAccount talimatı, yalnızca token account sahibi tarafından
oluşturulabilen şifreleme anahtarlarının ve bir kanıtın istemci tarafında
üretilmesini gerektirir.
Pubkey geçerlilik kanıtı, hesabın ElGamal açık anahtarının geçerli olduğunu
doğrular. build_pubkey_validity_proof_data ile oluşturulur, ZK ElGamal
Proof programı tarafından zincir üzerinde bir bağlam durum hesabına doğrulanır
ve ardından ProofLocation::ContextStateAccount aracılığıyla
ConfigureAccount içinden referans alınır; böylece token talimatının
kendisinde hiçbir kanıt baytı iletilmez. Uygulama ayrıntıları için bkz:
Örnek Kod
Aşağıdaki kod, bir associated token account oluşturur ve bunu mevcut bir gizli mint için gizli transferlere karşı yapılandırır.
Gizli transferler, mainnet ve devnet üzerinde etkinleştirilen ZK ElGamal Proof
programına bağlıdır. Standart bir solana-test-validator bunu etkinleştirmez;
ancak Surfpool gibi mainnet'i çatallayan yerel bir
validator etkinleştirir. Örneği, bu ortamlardan biri üzerinde (kod devnet
kullanır) fonlanmış bir ödeyiciyle çalıştırın ve mint yer tutucusunu
Mint Oluşturma
bölümüne göre oluşturulmuş bir mint ile değiştirin.
Rust
// The native ZK ElGamal Proof program verifies the proof on chain.const ZK_PROOF_PROGRAM_ID: Pubkey =solana_pubkey::pubkey!("ZkE1Gama1Proof11111111111111111111111111111");fn main() -> Result<()> {// Use a cluster whose ZK ElGamal Proof program is enabled (mainnet, devnet).let rpc_client = RpcClient::new_with_commitment(String::from("https://api.devnet.solana.com"),CommitmentConfig::confirmed(),);// The Solana CLI default keypair, used as fee payer, mint authority, and// token account owner.let payer = load_keypair()?;let decimals: u8 = 2;// Setup: create a confidential mint for the token account.let mint = create_confidential_mint(&rpc_client, &payer, decimals)?;let token_account = get_associated_token_address_with_program_id(&payer.pubkey(),&mint,&spl_token_2022::id(),);// 1. Create the associated token account.let create_ata_ix = create_associated_token_account(&payer.pubkey(), // funding account&payer.pubkey(), // token account owner&mint,&spl_token_2022::id(),);// 2. Add space for the ConfidentialTransferAccount extension.let realloc_ix = reallocate(&spl_token_2022::id(),&token_account,&payer.pubkey(), // payer&payer.pubkey(), // owner&[&payer.pubkey()],&[ExtensionType::ConfidentialTransferAccount],)?;// 3. Derive the owner's ElGamal keypair and AES key from a signature over// the token account address. The same signer and address always derive// the same keys, so the owner can recover them from their wallet.let (elgamal_keypair, aes_key) = derive_confidential_keys(&payer, &token_account.to_bytes()).map_err(|e| anyhow::anyhow!("derive confidential keys: {e}"))?;// Initial decryptable available balance of 0, encrypted with the AES key.let decryptable_balance: PodAeCiphertext = aes_key.encrypt(0).into();let maximum_pending_balance_credit_counter: u64 = 65_536;// 4. Generate the pubkey-validity proof, then pre-verify it into a context// state account owned by the ZK ElGamal Proof program. configure_account// references the verified proof by account, so no proof bytes travel in// the token instruction itself.let proof_data = build_pubkey_validity_proof_data(&elgamal_keypair).map_err(|e| anyhow::anyhow!("generate pubkey validity proof: {e}"))?;let proof_account = Keypair::new();let context_state_size = size_of::<ProofContextState<PubkeyValidityProofContext>>();let context_state_rent =rpc_client.get_minimum_balance_for_rent_exemption(context_state_size)?;let create_proof_account_ix = system_instruction::create_account(&payer.pubkey(),&proof_account.pubkey(),context_state_rent,context_state_size as u64,&ZK_PROOF_PROGRAM_ID,);let proof_account_address: Address = proof_account.pubkey().to_bytes().into();let owner_address: Address = payer.pubkey().to_bytes().into();let verify_proof_ix = ProofInstruction::VerifyPubkeyValidity.encode_verify_proof(Some(ContextStateInfo {context_state_account: &proof_account_address,context_state_authority: &owner_address,}),&proof_data,);// 5. Configure the account, pointing at the pre-verified proof account.let proof_location: ProofLocation<PubkeyValidityProofData> =ProofLocation::ContextStateAccount(&proof_account.pubkey());let configure_account_ixs = configure_account(&spl_token_2022::id(),&token_account,&mint,&decryptable_balance,maximum_pending_balance_credit_counter,&payer.pubkey(), // owner&[],proof_location,)?;// Everything fits in a single transaction.let mut instructions = vec![create_ata_ix,realloc_ix,create_proof_account_ix,verify_proof_ix,];instructions.extend(configure_account_ixs);let blockhash = rpc_client.get_latest_blockhash()?;let transaction = Transaction::new_signed_with_payer(&instructions,Some(&payer.pubkey()),&[&payer, &proof_account],blockhash,);let signature = rpc_client.send_and_confirm_transaction(&transaction)?;println!("Configured token account {token_account} for confidential transfers: {signature}");Ok(())}
Typescript
const client = await createClient().use(signerFromFile(join(homedir(), ".config/solana/id.json"))).use(solanaRpc({rpcUrl: "https://api.devnet.solana.com"}));// The Solana CLI default keypair, used as fee payer, mint authority, and// token account owner.const owner = client.payer;const decimals = 2;// Setup: create a confidential mint for the token account.const mint = await createConfidentialMint(client, owner, decimals);const [tokenAccount] = await findAssociatedTokenPda({owner: owner.address,tokenProgram: TOKEN_2022_PROGRAM_ADDRESS,mint});// Derive recoverable ElGamal and AES keys bound to (owner, mint). Re-deriving// from the same wallet always yields the same keys, so the owner can recover// them rather than having to back up a separate secret.const derivedElGamal = await deriveElGamalKeypairForOwnerMint({signer: owner,owner: owner.address,mint});const elgamalKeypair = ElGamalKeypair.fromSecretKey(ElGamalSecretKey.fromBytes(derivedElGamal.secretKey));const aesKey = AeKey.fromBytes(await deriveAeKeyForOwnerMint({ signer: owner, owner: owner.address, mint }));// Build the create-ATA + reallocate + verify-proof + configure plan, then send.// The helper returns an instruction plan because the steps may span more than// one transaction.const plan = await getCreateConfidentialTransferAccountInstructionPlan({rpc: client.rpc,payer: owner,owner,mint,elgamalKeypair,aesKey});const result = await client.sendTransaction(plan);console.log(`Configured token account ${tokenAccount} for confidential transfers: ${result.context.signature}`);
TypeScript yardımcı araçları @solana-program/token-2022/confidential alt
yolunda bulunur ve şifreleme ilkel öğeleri için @solana/zk-sdk üzerine inşa
edilir. owner ve client, @solana/kit kurulumunuzdan gelir; döndürülen
talimat planı, @solana/kit'nin talimat planı desteğiyle gönderilir ve
kanıtlar tek bir işlem için çok büyük olduğunda çalışmayı işlemler arasında
böler.
Is this page helpful?