Gizli Bakiyeler ile token account nasıl oluşturulur
Gizli Bakiyeler, token account'a ekstra durum bilgisi ekleyerek özel token transferlerini mümkün kılar. Bu bölümde, gerekli uzantı etkinleştirilmiş bir token account'un nasıl oluşturulacağı açıklanmaktadır.
Aşağıdaki diyagram, Gizli Bakiyeler etkinleştirilmiş bir token account oluşturmada yer alan adımları göstermektedir:
Gizli Bakiyeler 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 Bakiyeleri yöneten çeşitli alanlar içerir:
-
approved: Gizli Bakiyeler için hesabın onay durumu. Eğer mint account'un
auto_approve_new_accountsyapılandırmasıtrueolarak ayarlanmışsa, tüm token account'lar Gizli Bakiyeler 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 Bakiyeler 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 Bakiyeleri Yapılandır: Token Extensions Program'ın ConfidentialTransferInstruction::ConfigureAccount talimatını çağırın;
ConfidentialTransferAccountdurumunu başlatmak içinProofLocation::ContextStateAccountaracılığıyla kanıt bağlam durumu hesabına başvurun.
Yalnızca token account sahibi, bir token account üzerinde Gizli Bakiyeleri etkinleştirebilir.
ConfigureAccount talimatı, yalnızca token account sahibi tarafından oluşturulabilecek şifreleme anahtarlarının ve bir kanıtın istemci tarafında üretilmesini gerektirir.
Pubkey geçerlilik kanıtı, hesabın ElGamal pubkey'inin 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 durumu hesabına doğrulanır ve ardından ConfigureAccount tarafından ProofLocation::ContextStateAccount aracılığıyla 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, mevcut uyumlu bir mint ile Gizli Bakiyeleri kullanmak üzere yapılandırılmış bir associated token account oluşturur.
Gizli Bakiyeler, 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, fonlanmış bir ödeyici ile bu ortamlardan birine karşı çalıştırın (kod devnet kullanır) ve mint yer tutucusunu Mint Oluştur 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!("Enabled Confidential Balances on token account {token_account}: {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(`Enabled Confidential Balances on token account ${tokenAccount}: ${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?