Πώς να εφαρμόσετε το εκκρεμές υπόλοιπο στο διαθέσιμο υπόλοιπο
Πριν μεταφερθούν τα token εμπιστευτικά, το δημόσιο υπόλοιπο token πρέπει να μετατραπεί σε εμπιστευτικό υπόλοιπο. Αυτή η μετατροπή γίνεται σε δύο στάδια:
- Εμπιστευτικό Εκκρεμές Υπόλοιπο: Αρχικά, τα token «κατατίθενται» από το δημόσιο υπόλοιπο σε ένα «εκκρεμές» εμπιστευτικό υπόλοιπο.
- Εμπιστευτικό Διαθέσιμο Υπόλοιπο: Το εκκρεμές υπόλοιπο στη συνέχεια «εφαρμόζεται» στο διαθέσιμο υπόλοιπο, καθιστώντας τα token διαθέσιμα για εμπιστευτικές μεταφορές.
Αυτή η ενότητα εξηγεί το δεύτερο στάδιο: την εφαρμογή του εκκρεμούς υπολοίπου στο διαθέσιμο υπόλοιπο.
Όταν τα token «κατατίθενται» από το δημόσιο υπόλοιπο ή όταν μεταφέρονται εμπιστευτικά από ένα token account σε άλλο, τα token προστίθενται αρχικά στο εμπιστευτικό εκκρεμές υπόλοιπο. Πριν χρησιμοποιηθούν τα token για εμπιστευτικές μεταφορές, το εκκρεμές υπόλοιπο πρέπει να «εφαρμοστεί» στο διαθέσιμο υπόλοιπο.
Το παρακάτω διάγραμμα δείχνει τα βήματα που απαιτούνται για την εφαρμογή του εκκρεμούς υπολοίπου στο διαθέσιμο υπόλοιπο:
Απαιτούμενη Εντολή
Για να μετατρέψετε ένα εκκρεμές υπόλοιπο σε διαθέσιμο υπόλοιπο, χρησιμοποιήστε την εντολή ConfidentialTransferInstruction::ApplyPendingBalance.
Το crate spl_token_client παρέχει μια μέθοδο
confidential_transfer_apply_pending_balance που δημιουργεί και αποστέλλει μια
συναλλαγή με την εντολή ApplyPendingBalance, όπως φαίνεται στο παρακάτω
παράδειγμα.
Παράδειγμα Κώδικα
Το παρακάτω παράδειγμα δείχνει πώς να εφαρμόσετε το εμπιστευτικό εκκρεμές υπόλοιπο στο εμπιστευτικό διαθέσιμο υπόλοιπο.
Οι εμπιστευτικές μεταφορές βασίζονται στο πρόγραμμα ZK ElGamal Proof, το οποίο
είναι ενεργοποιημένο στο mainnet και στο devnet. Ένα τυπικό
solana-test-validator δεν το ενεργοποιεί, αλλά ένας topικός validator που
κάνει fork του mainnet, όπως το Surfpool, το κάνει.
Εκτελέστε το παράδειγμα με έναν από αυτούς (ο κώδικας χρησιμοποιεί devnet) με
έναν χρηματοδοτημένο payer, και αντικαταστήστε τις placeholder διευθύνσεις mint
και account με τις δικές σας.
Rust
const ZK_PROOF_PROGRAM_ID: Pubkey =solana_pubkey::pubkey!("ZkE1Gama1Proof11111111111111111111111111111");fn main() -> Result<()> {let rpc_client = RpcClient::new_with_commitment(String::from("https://api.devnet.solana.com"),CommitmentConfig::confirmed(),);let owner = load_keypair()?;let amount: u64 = 100;let decimals: u8 = 2;// Setup: create a confidential account with a pending balance.let (_mint, token_account) = setup_pending_balance_account(&rpc_client, &owner, amount, decimals)?;// Derive the owner's keys to decrypt the current balances.let (elgamal_keypair, aes_key) = derive_confidential_keys(&owner, &token_account.to_bytes()).map_err(|e| anyhow::anyhow!("derive confidential keys: {e}"))?;let account_data = rpc_client.get_account(&token_account)?;let account = StateWithExtensions::<TokenAccount>::unpack(&account_data.data)?;let ct_extension = account.get_extension::<ConfidentialTransferAccount>()?;// The pending balance is split into low (16 bits) and high parts, each small// enough to recover with ElGamal's bounded decrypt_u32.let pending_lo: ElGamalCiphertext = ct_extension.pending_balance_lo.try_into().map_err(|e| anyhow::anyhow!("pending_balance_lo: {e:?}"))?;let pending_hi: ElGamalCiphertext = ct_extension.pending_balance_hi.try_into().map_err(|e| anyhow::anyhow!("pending_balance_hi: {e:?}"))?;let pending_lo_amount = pending_lo.decrypt_u32(elgamal_keypair.secret()).context("decrypt pending_balance_lo")? as u64;let pending_hi_amount = pending_hi.decrypt_u32(elgamal_keypair.secret()).context("decrypt pending_balance_hi")? as u64;let pending_total = pending_lo_amount + (pending_hi_amount << 16);// Read the current available balance from the AES-encrypted decryptable// balance. ElGamal's decrypt_u32 only recovers values up to 2^32 raw units,// so it fails for realistic balances; the AES field has no such limit.let decryptable_balance: AeCiphertext = ct_extension.decryptable_available_balance.try_into().map_err(|e| anyhow::anyhow!("decryptable_available_balance: {e:?}"))?;let current_available = decryptable_balance.decrypt(&aes_key).context("decrypt available balance")?;let new_available = current_available + pending_total;// Re-encrypt the new available balance with the AES key for fast reads.let new_decryptable: PodAeCiphertext = aes_key.encrypt(new_available).into();// The expected counter guards against pending credits that arrive between// building and processing this instruction.let expected_counter: u64 = ct_extension.pending_balance_credit_counter.into();let apply_ix = apply_pending_balance(&spl_token_2022::id(),&token_account,expected_counter,&new_decryptable,&owner.pubkey(),&[&owner.pubkey()],)?;let blockhash = rpc_client.get_latest_blockhash()?;let transaction =Transaction::new_signed_with_payer(&[apply_ix], Some(&owner.pubkey()), &[&owner], blockhash);let signature = rpc_client.send_and_confirm_transaction(&transaction)?;println!("Applied pending balance. New available: {new_available}. Tx: {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 amount = 100n;const decimals = 2;// Setup: create a confidential account with a pending balance.const mint = await createConfidentialMint(client, owner, decimals);const token = await createConfidentialTokenAccount(client, owner, mint);await mintPublicTokens(client, owner, mint, token, amount);await depositTokens(client, owner, mint, token, amount, decimals);// Derive the owner's keys to decrypt the current balances.const { elgamalSecretKey, aesKey } = await deriveConfidentialKeys(owner, mint);// The helper decrypts the pending and available balances, re-encrypts the new// available balance, and builds the ApplyPendingBalance instruction. No proof// is required.const tokenAccount = await fetchToken(client.rpc, token);const applyInstruction = getApplyConfidentialPendingBalanceInstructionFromToken({token,tokenAccount: tokenAccount.data,authority: owner,elgamalSecretKey,aesKey});const result = await client.sendTransaction([applyInstruction]);console.log(`Applied pending balance. New available: ${amount}. Tx: ${result.context.signature}`);
Is this page helpful?