Πώς να δημιουργήσετε ένα token account με την επέκταση Confidential Transfer
Η επέκταση Confidential Transfer επιτρέπει ιδιωτικές μεταφορές token προσθέτοντας επιπλέον κατάσταση στο token account. Αυτή η ενότητα εξηγεί πώς να δημιουργήσετε ένα token account με αυτήν την επέκταση ενεργοποιημένη.
Το παρακάτω διάγραμμα δείχνει τα βήματα που απαιτούνται για τη δημιουργία ενός token account με την επέκταση Confidential Transfer:
Κατάσταση Token Account Confidential Transfer
Η επέκταση προσθέτει την κατάσταση ConfidentialTransferAccount στο token account:
#[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 περιέχει αρκετά πεδία για τη διαχείριση
εμπιστευτικών μεταφορών:
-
approved: Η κατάσταση έγκρισης του λογαριασμού για εμπιστευτικές μεταφορές. Αν η διαμόρφωση
auto_approve_new_accountsτου mint account έχει οριστεί ωςtrue, όλα τα token accounts εγκρίνονται αυτόματα για εμπιστευτικές μεταφορές. -
elgamal_pubkey: Το δημόσιο κλειδί ElGamal που χρησιμοποιείται για την κρυπτογράφηση υπολοίπων και ποσών μεταφοράς.
-
pending_balance_lo: Τα κρυπτογραφημένα κατώτερα 16 bit του εκκρεμούς υπολοίπου. Το υπόλοιπο χωρίζεται σε υψηλό και χαμηλό τμήμα για αποδοτική αποκρυπτογράφηση.
-
pending_balance_hi: Τα κρυπτογραφημένα ανώτερα 48 bit του εκκρεμούς υπολοίπου. Το υπόλοιπο χωρίζεται σε υψηλό και χαμηλό τμήμα για αποδοτική αποκρυπτογράφηση.
-
available_balance: Το κρυπτογραφημένο υπόλοιπο που είναι διαθέσιμο για μεταφορές.
-
decryptable_available_balance: Το διαθέσιμο υπόλοιπο κρυπτογραφημένο με ένα κλειδί Advanced Encryption Standard (AES) για αποδοτική αποκρυπτογράφηση από τον ιδιοκτήτη του λογαριασμού.
-
allow_confidential_credits: Εάν είναι true, επιτρέπει εισερχόμενες εμπιστευτικές μεταφορές.
-
allow_non_confidential_credits: Εάν είναι true, επιτρέπει εισερχόμενες μη εμπιστευτικές μεταφορές.
-
pending_balance_credit_counter: Καταμετρά εισερχόμενες πιστώσεις εκκρεμούς υπολοίπου από εντολές κατάθεσης και μεταφοράς.
-
maximum_pending_balance_credit_counter: Το όριο καταμέτρησης εκκρεμών πιστώσεων πριν απαιτηθεί εντολή
ApplyPendingBalanceγια τη μετατροπή του εκκρεμούς υπολοίπου σε διαθέσιμο υπόλοιπο. -
expected_pending_balance_credit_counter: Η τιμή
pending_balance_credit_counterπου παρέχεται από τον πελάτη μέσω του instruction data την τελευταία φορά που η εντολήApplyPendingBalanceεπεξεργάστηκε. -
actual_pending_balance_credit_counter: Η τιμή
pending_balance_credit_counterστο token account τη στιγμή που επεξεργάστηκε η τελευταία εντολήApplyPendingBalance.
Εκκρεμές vs Διαθέσιμο Υπόλοιπο
Τα εμπιστευτικά υπόλοιπα χωρίζονται σε εκκρεμή και διαθέσιμα υπόλοιπα για να αποτρέπονται επιθέσεις DoS. Χωρίς αυτόν τον διαχωρισμό, ένας εισβολέας θα μπορούσε επανειλημμένα να στέλνει tokens σε ένα token account, εμποδίζοντας τον ιδιοκτήτη του token account να μεταφέρει tokens. Ο ιδιοκτήτης του token account δεν θα μπορούσε να μεταφέρει tokens επειδή το κρυπτογραφημένο υπόλοιπο θα άλλαζε μεταξύ της υποβολής και της επεξεργασίας της συναλλαγής, με αποτέλεσμα την αποτυχία της.
Όλες οι καταθέσεις και τα ποσά μεταφοράς προστίθενται αρχικά στο εκκρεμές
υπόλοιπο. Οι ιδιοκτήτες token account πρέπει να χρησιμοποιούν την εντολή
ApplyPendingBalance για να μετατρέψουν το εκκρεμές υπόλοιπο σε διαθέσιμο.
Οι εισερχόμενες μεταφορές ή καταθέσεις δεν επηρεάζουν το διαθέσιμο υπόλοιπο ενός
token account.
Διαχωρισμός Υψηλού/Χαμηλού Εκκρεμούς Υπολοίπου
Το εμπιστευτικό εκκρεμές υπόλοιπο χωρίζεται σε pending_balance_lo και
pending_balance_hi επειδή η αποκρυπτογράφηση ElGamal απαιτεί μεγαλύτερη
υπολογιστική ισχύ για μεγαλύτερους αριθμούς. Μπορείτε να βρείτε την υλοποίηση
της αριθμητικής κρυπτοκειμένου
εδώ,
η οποία χρησιμοποιείται στην εντολή ApplyPendingBalance
εδώ.
Μετρητές Πίστωσης Εκκρεμούς Υπολοίπου
Κατά την κλήση της εντολής ApplyPendingBalance για τη μετατροπή του
εκκρεμούς υπολοίπου σε διαθέσιμο:
-
Ο πελάτης αναζητά τα τρέχοντα εκκρεμή και διαθέσιμα υπόλοιπα, κρυπτογραφεί το άθροισμα και παρέχει ένα
decryptable_available_balanceκρυπτογραφημένο με το κλειδί AES του ιδιοκτήτη του token account. -
Οι αναμενόμενοι και πραγματικοί μετρητές εκκρεμούς πίστωσης παρακολουθούν τις αλλαγές στην τιμή του μετρητή μεταξύ της δημιουργίας και της επεξεργασίας της εντολής
ApplyPendingBalance:expected_pending_balance_credit_counter: Η τιμήpending_balance_credit_counterόταν ο πελάτης δημιουργεί την εντολήApplyPendingBalanceactual_pending_balance_credit_counter: Η τιμήpending_balance_credit_counterστο token account τη στιγμή που επεξεργάζεται η εντολήApplyPendingBalance
Οι αντίστοιχοι μετρητές αναμενόμενου/πραγματικού υποδεικνύουν ότι το
decryptable_available_balance ταιριάζει με το available_balance.
Κατά την ανάκτηση της κατάστασης ενός token account για την ανάγνωση του
decryptable_available_balance, διαφορετικές τιμές μετρητών
αναμενόμενου/πραγματικού απαιτούν από τον πελάτη να αναζητήσει πρόσφατες εντολές
κατάθεσης/μεταφοράς που αντιστοιχούν στη διαφορά μετρητών για τον υπολογισμό του
σωστού υπολοίπου.
Διαδικασία Συμφωνίας Υπολοίπου
Όταν οι μετρητές αναμενόμενου και πραγματικού εκκρεμούς υπολοίπου διαφέρουν,
ακολουθήστε τα παρακάτω βήματα για να συμφωνήσετε το
decryptable_available_balance:
- Ξεκινήστε με το
decryptable_available_balanceαπό το token account - Ανακτήστε τις πιο πρόσφατες συναλλαγές που περιλαμβάνουν εντολές κατάθεσης
και μεταφοράς έως τη διαφορά μετρητών (πραγματικός - αναμενόμενος):
- Προσθέστε δημόσια ποσά από εντολές κατάθεσης
- Αποκρυπτογραφήστε και προσθέστε ποσά κρυπτοκειμένου προορισμού από εντολές μεταφοράς
Απαιτούμενες Εντολές
Η δημιουργία και ρύθμιση ενός token account για εμπιστευτικές μεταφορές χρησιμοποιεί τις παρακάτω εντολές, οι οποίες χωράνε όλες σε μία μόνο συναλλαγή:
-
Δημιουργία του Token Account: Καλέστε την εντολή
AssociatedTokenAccountInstruction::Createτου Associated Token Program για τη δημιουργία του token account στη ντετερμινιστική του διεύθυνση. -
Εκ Νέου Κατανομή Χώρου Λογαριασμού: Καλέστε την εντολή
TokenInstruction::Reallocateτου Token Extensions Program για να προσθέσετε χώρο για την κατάστασηConfidentialTransferAccount. -
Επαλήθευση Απόδειξης Εγκυρότητας Pubkey: Δημιουργήστε έναν λογαριασμό που ανήκει στο πρόγραμμα ZK ElGamal Proof, και στη συνέχεια καλέστε την εντολή
VerifyPubkeyValidityτου για την επαλήθευση της απόδειξης και την αποθήκευση του επαληθευμένου αποτελέσματος σε αυτόν τον λογαριασμό κατάστασης πλαισίου. -
Ρύθμιση Εμπιστευτικών Μεταφορών: Καλέστε την εντολή ConfidentialTransferInstruction::ConfigureAccount του Token Extensions Program, αναφέροντας τον λογαριασμό κατάστασης πλαισίου απόδειξης μέσω
ProofLocation::ContextStateAccount, για να αρχικοποιήσετε την κατάστασηConfidentialTransferAccount.
Μόνο ο ιδιοκτήτης του token account μπορεί να διαμορφώσει ένα token account για εμπιστευτικές μεταφορές.
Η εντολή ConfigureAccount απαιτεί τη δημιουργία από την πλευρά του πελάτη
κλειδιών κρυπτογράφησης και μιας απόδειξης που μπορεί να δημιουργηθεί μόνο από
τον κάτοχο του token account.
Η απόδειξη εγκυρότητας pubkey επαληθεύει ότι το δημόσιο κλειδί ElGamal του
λογαριασμού είναι έγκυρο. Δημιουργείται με
build_pubkey_validity_proof_data, επαληθεύεται επί αλυσίδας από το
πρόγραμμα ZK ElGamal Proof σε έναν λογαριασμό κατάστασης πλαισίου, και στη
συνέχεια αναφέρεται από το ConfigureAccount μέσω
ProofLocation::ContextStateAccount, ώστε κανένα byte απόδειξης να μη
μεταφέρεται στην ίδια την εντολή token. Για λεπτομέρειες υλοποίησης, δείτε:
Παράδειγμα Κώδικα
Ο παρακάτω κώδικας δημιουργεί ένα associated token account και το διαμορφώνει για εμπιστευτικές μεταφορές έναντι ενός υπάρχοντος εμπιστευτικού mint.
Οι εμπιστευτικές μεταφορές εξαρτώνται από το πρόγραμμα ZK ElGamal Proof, το
οποίο είναι ενεργοποιημένο στο mainnet και στο devnet. Ένα τυπικό
solana-test-validator δεν το ενεργοποιεί, αλλά ένας topικός validator που
αντικατοπτρίζει το mainnet, όπως το Surfpool, το κάνει.
Εκτελέστε το παράδειγμα έναντι ενός από αυτά (ο κώδικας χρησιμοποιεί το devnet)
με έναν χρηματοδοτημένο payer, και αντικαταστήστε το placeholder του mint με ένα
mint που δημιουργήθηκε σύμφωνα με
Δημιουργία Mint.
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 βρίσκονται στο υποδιαδρομή
@solana-program/token-2022/confidential και βασίζονται στο @solana/zk-sdk
για τα κρυπτογραφικά primitives. Το owner και το client προέρχονται από τη
ρύθμιση του @solana/kit σας· το επιστρεφόμενο πλάνο εντολών αποστέλλεται με
την υποστήριξη πλάνου εντολών του @solana/kit, η οποία κατανέμει την εργασία
σε πολλαπλές συναλλαγές όταν οι αποδείξεις είναι πολύ μεγάλες για μία.
Is this page helpful?