Luo token account

Kuinka luoda token account Confidential Transfer -laajennuksella

Confidential Transfer -laajennus mahdollistaa yksityiset tokensiirrot lisäämällä extra-tilan token account -tilille. Tässä osiossa selitetään, kuinka luoda token account tämä laajennus aktivoituna.

Seuraava kaavio näyttää vaiheet, jotka liittyvät token account -tilin luomiseen Confidential Transfer -laajennuksella:

Create Token Account with Confidential Transfer Extension

Confidential Transfer Token Account -tila

Laajennus lisää ConfidentialTransferAccount -tilan token account -tilille:

Confidential Token Account State
#[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 encryption
pub 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 balance
pub decryptable_available_balance: DecryptableBalance,
/// If `false`, the extended account rejects any incoming confidential
/// transfers
pub allow_confidential_credits: PodBool,
/// If `false`, the base account rejects any incoming transfers
pub 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 executed
pub maximum_pending_balance_credit_counter: PodU64,
/// The `expected_pending_balance_credit_counter` value that was included in
/// the last `ApplyPendingBalance` instruction
pub expected_pending_balance_credit_counter: PodU64,
/// The actual `pending_balance_credit_counter` when the last
/// `ApplyPendingBalance` instruction was executed
pub actual_pending_balance_credit_counter: PodU64,
}

ConfidentialTransferAccount sisältää useita kenttiä luottamuksellisten siirtojen hallintaan:

  • approved: Tilin hyväksyntätila luottamuksellisia siirtoja varten. Jos mint account -tilin auto_approve_new_accounts -asetus on määritetty arvoksi true, kaikki token account -tilit hyväksytään automaattisesti luottamuksellisia siirtoja varten.

  • elgamal_pubkey: ElGamal-julkinen avain, jota käytetään saldojen ja siirtosummien salaamiseen.

  • pending_balance_lo: Salattu odottavan saldon alemmat 16 bittiä. Saldo on jaettu ylä- ja alaosiin tehokasta salauksen purkua varten.

  • pending_balance_hi: Salattu odottavan saldon ylemmät 48 bittiä. Saldo on jaettu ylä- ja alaosiin tehokasta salauksen purkua varten.

  • available_balance: Salattu saldo, joka on käytettävissä siirtoihin.

  • decryptable_available_balance: Käytettävissä oleva saldo salattuna Advanced Encryption Standard (AES) -avaimella tilin omistajan tehokasta salauksen purkua varten.

  • allow_confidential_credits: Jos tosi, sallii saapuvat luottamukselliset siirrot.

  • allow_non_confidential_credits: Jos tosi, sallii saapuvat ei-luottamukselliset siirrot.

  • pending_balance_credit_counter: Laskee saapuvat odottavat saldohyvitykset talletus- ja siirto-ohjeista.

  • maximum_pending_balance_credit_counter: Odottavien hyvitysten enimmäismäärä ennen kuin vaaditaan ApplyPendingBalance -ohje odottavan saldon muuntamiseksi käytettävissä olevaksi saldoksi.

  • expected_pending_balance_credit_counter: Asiakkaan instruction data -kentässä viimeksi toimittama pending_balance_credit_counter -arvo, kun ApplyPendingBalance -ohje viimeksi käsiteltiin.

  • actual_pending_balance_credit_counter: pending_balance_credit_counter-arvo token account -tilillä sillä hetkellä, kun viimeisin ApplyPendingBalance-instruktio käsiteltiin.

Odottava vs. käytettävissä oleva saldo

Luottamukselliset saldot on jaettu odottaviin ja käytettävissä oleviin saldoihin DoS-hyökkäysten estämiseksi. Ilman tätä erottelua hyökkääjä voisi toistuvasti lähettää tokeneita token account -tilille, estäen token account -tilin omistajan mahdollisuuden siirtää tokeneita. Token account -tilin omistaja ei pystyisi siirtämään tokeneita, koska salattu saldo muuttuisi siinä välillä, kun tapahtuma lähetetään ja kun se käsitellään, mikä johtaisi epäonnistuneeseen tapahtumaan.

Kaikki talletukset ja siirtosummat lisätään aluksi odottavaan saldoon. Token account -tilin omistajien on käytettävä ApplyPendingBalance-instruktiota muuntaakseen odottavan saldon käytettävissä olevaksi saldoksi. Saapuvat siirrot tai talletukset eivät vaikuta token account -tilin käytettävissä olevaan saldoon.

Odottavan saldon korkea/matala jako

Luottamuksellinen odottava saldo on jaettu pending_balance_lo- ja pending_balance_hi-osiin, koska ElGamal-salauksen purku vaatii enemmän laskentaa suuremmille luvuille. Salauslaskutoimituksen toteutuksen löydät täältä, jota käytetään ApplyPendingBalance-instruktiossa täällä.

Odottavan saldon krediittilaskurit

Kutsuttaessa ApplyPendingBalance-instruktiota odottavan saldon muuntamiseksi käytettävissä olevaksi saldoksi:

  1. Asiakas hakee nykyiset odottavat ja käytettävissä olevat saldot, salaa summan ja toimittaa decryptable_available_balance-arvon salattuna token account -tilin omistajan AES-avaimella.

  2. Odotetut ja toteutuneet odottavat krediittilaskurit seuraavat laskuriarvon muutoksia siinä välillä, kun ApplyPendingBalance-instruktio luodaan ja käsitellään:

    • expected_pending_balance_credit_counter: pending_balance_credit_counter-arvo sillä hetkellä, kun asiakas luo ApplyPendingBalance-instruktion
    • actual_pending_balance_credit_counter: pending_balance_credit_counter-arvo token account -tilillä sillä hetkellä, kun ApplyPendingBalance-instruktio käsitellään

Yhteensopivat odotettu/todellinen-laskurit osoittavat, että decryptable_available_balance vastaa available_balance.

Kun haetaan token account -tilan decryptable_available_balance-arvoa, odotettu/todellinen-laskureiden eri arvot edellyttävät, että asiakasohjelma hakee viimeisimmät talletus- ja siirtoinstruktiot, jotka vastaavat laskurieroa oikean saldon laskemiseksi.

Saldontäsmäytysprosessi

Kun odotettu ja todellinen odottavan saldon laskuri eroavat toisistaan, seuraa näitä vaiheita decryptable_available_balance-arvon täsmäyttämiseksi:

  1. Aloita decryptable_available_balance:sta token account -tililtä
  2. Hae viimeisimmät transaktiot, mukaan lukien talletus- ja siirtoinstruktiot laskurieron (todellinen - odotettu) verran:
    • Lisää julkiset summat talletusinstruktioista
    • Pura salaus ja lisää kohdesalakielitekstin summat siirtoinstruktioista

Vaaditut instruktiot

Token account -tilin luominen ja konfigurointi luottamuksellisia siirtoja varten käyttää seuraavia instruktioita, jotka mahtuvat kaikki yhteen transaktioon:

  1. Luo token account: Kutsu Associated Token Program -ohjelman AssociatedTokenAccountInstruction::Create-instruktioita luodaksesi token account -tilin sen deterministiseen osoitteeseen.

  2. Uudelleenallokoi tilatila: Kutsu Token Extension Program -ohjelman TokenInstruction::Reallocate-instruktioita lisätäksesi tilaa ConfidentialTransferAccount-tilalle.

  3. Tarkista pubkey-voimassaolotodiste: Luo ZK ElGamal Proof -ohjelman omistama tili, ja kutsu sitten sen VerifyPubkeyValidity-instruktioita todisteen vahvistamiseksi ja vahvistetun tuloksen tallentamiseksi kyseiselle kontekstitilatilille.

  4. Konfiguroi luottamukselliset siirrot: Kutsu Token Extension Program -ohjelman ConfidentialTransferInstruction::ConfigureAccount -instruktioita, viitaten todistekontekstitilatiliin ProofLocation::ContextStateAccount:n kautta, alustaaksesi ConfidentialTransferAccount-tilan.

Vain token accountin omistaja voi määrittää token accountin luottamuksellisia siirtoja varten.

ConfigureAccount-instruktio vaatii asiakaspuolella salausavainten ja todisteen luomisen, jonka voi luoda ainoastaan token account -tilin omistaja.

Pubkey-voimassaolotodiste vahvistaa, että tilin ElGamal-julkinen avain on kelvollinen. Se luodaan build_pubkey_validity_proof_data-funktiolla, vahvistetaan ketjussa ZK ElGamal Proof -ohjelman toimesta kontekstitilatilille, ja siihen viitataan sen jälkeen ConfigureAccount-instruktion kautta ProofLocation::ContextStateAccount:n avulla, joten todistebittejä ei kulje itse token-instruktion sisällä. Toteutuksen yksityiskohtia varten, katso:

Esimerkkikoodi

Seuraava koodi luo associated token account -tilin ja määrittää sen luottamuksellisia siirtoja varten olemassa olevaa luottamuksellista mint-tilaa vastaan.

Luottamukselliset siirrot perustuvat ZK ElGamal Proof -ohjelmaan, joka on käytössä mainnetissä ja devnetissä. Tavallinen solana-test-validator ei ota sitä käyttöön, mutta mainnetiä haaruttava paikallinen validator, kuten Surfpool, tekee sen. Aja esimerkki jompaakumpaa vastaan (koodi käyttää devnetiä) rahoitetulla maksajalla, ja korvaa mint-paikkamerkki per Create a Mint luodulla mintillä.

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-apufunktiot sijaitsevat @solana-program/token-2022/confidential-alipolussa ja rakentuvat @solana/zk-sdk:n salausprimitiivien päälle. owner ja client tulevat @solana/kit-asetuksestasi; palautettu ohjesuunnitelma lähetetään @solana/kit:n ohjesuunnitelmatuen avulla, joka jakaa työn useisiin transaktioihin, kun todisteet ovat liian suuria yhdelle.

Is this page helpful?

Sisällysluettelo

Muokkaa sivua
© 2026 Solana Foundation. Kaikki oikeudet pidätetään.