Кратко
Аккаунты программ содержат исполняемый sBPF-код. Аккаунты данных хранят состояние и принадлежат программам. Системные аккаунты принадлежат System Program. Sysvars предоставляют состояние кластера, доступное по предопределённым адресам.
Поле executable определяет категорию
аккаунта:
- Аккаунты программ:
executable=true. Содержит исполняемый код. - Аккаунты данных:
executable=false. Хранит состояние или пользовательские данные.
Такое разделение кода и изменяемого состояния позволяет развернуть программу один раз и управлять любым количеством аккаунтов данных.
Аккаунты программ
Аккаунт программы хранит исполняемый код. Каждый аккаунт программы принадлежит загрузчику программ. Когда программа развёртывается, среда выполнения создаёт аккаунт программы для хранения её байткода.
Диаграмма аккаунта программы, его 4 компонентов и загрузчика.
Аккаунты данных программ
Программы, развёрнутые с помощью loader-v3 (см.
Загрузчики программ), не
хранят исполняемый байткод в собственном поле data. Вместо этого их data
указывает на отдельный аккаунт данных программы, который содержит код
программы. (См. диаграмму ниже.)
Аккаунт программы с данными. Данные указывают на отдельный аккаунт данных программы
Во время развёртывания или обновления программы используются буферные аккаунты для временной загрузки данных.
В следующем примере происходит получение аккаунта Token Program. Поле
executable имеет значение true, что подтверждает: это аккаунт программы.
import { Address, createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc("https://api.mainnet.solana.com");const programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;const accountInfo = await rpc.getAccountInfo(programId, { encoding: "base64" }).send();console.log(accountInfo);
Аккаунты данных
Аккаунты данных не содержат исполняемого кода. Они хранят состояние, определяемое программой.
Аккаунт состояния программы
Программы хранят своё состояние в аккаунтах данных. Создание аккаунта состояния программы включает два шага:
- Вызвать System Program, чтобы создать аккаунт. System Program передаёт право собственности указанной программе.
- Владеющая программа инициализирует поле аккаунта
dataсогласно своим инструкциям.
Диаграмма аккаунта данных, принадлежащего program account
В следующем примере создаётся и запрашивается mint account токена, принадлежащий программе Token 2022.
import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners} from "@solana/kit";import { getCreateAccountInstruction } from "@solana-program/system";import {getInitializeMintInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS,fetchMint} from "@solana-program/token-2022";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate keypairs for fee payerconst feePayer = await generateKeyPairSigner();// Fund fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: feePayer.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});// Generate keypair to use as address of mintconst mint = await generateKeyPairSigner();// Get default mint account size (in bytes), no extensions enabledconst space = BigInt(getMintSize());// Get minimum balance for rent exemptionconst rent = await rpc.getMinimumBalanceForRentExemption(space).send();// Instruction to create new account for mint (token 2022 program)// Invokes the system programconst createAccountInstruction = getCreateAccountInstruction({payer: feePayer,newAccount: mint,lamports: rent,space,programAddress: TOKEN_2022_PROGRAM_ADDRESS});// Instruction to initialize mint account data// Invokes the token 2022 programconst initializeMintInstruction = getInitializeMintInstruction({mint: mint.address,decimals: 9,mintAuthority: feePayer.address});const instructions = [createAccountInstruction, initializeMintInstruction];// Get latest blockhash to include in transactionconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }), // Create transaction message(tx) => setTransactionMessageFeePayerSigner(feePayer, tx), // Set fee payer(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), // Set transaction blockhash(tx) => appendTransactionMessageInstructions(instructions, tx) // Append instructions);// Sign transaction message with required signers (fee payer and mint keypair)const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Send and confirm transactionawait sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTransaction,{ commitment: "confirmed" });// Get transaction signatureconst transactionSignature = getSignatureFromTransaction(signedTransaction);console.log("Mint Address:", mint.address);console.log("Transaction Signature:", transactionSignature);const accountInfo = await rpc.getAccountInfo(mint.address).send();console.log(accountInfo);const mintAccount = await fetchMint(rpc, mint.address);console.log(mintAccount);
Системные аккаунты
Аккаунты, которые после создания остаются во владении System Program, называются системными аккаунтами. Отправка SOL на новый адрес впервые создаёт новый аккаунт по этому адресу, принадлежащий System Program.
Все кошельки являются системными аккаунтами. Плательщик комиссии в транзакции должен быть системным аккаунтом, так как только аккаунты, принадлежащие System Program, могут оплачивать комиссии за транзакции.
Кошелёк, принадлежащий System Program, содержащий 1 000 000 лампортов
В следующем примере создаётся новая keypair, пополняется SOL и запрашивается
аккаунт. Поле owner равно 11111111111111111111111111111111
(System Program).
import {airdropFactory,createSolanaRpc,createSolanaRpcSubscriptions,generateKeyPairSigner,lamports} from "@solana/kit";// Create a connection to Solana clusterconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate a new keypairconst keypair = await generateKeyPairSigner();console.log(`Public Key: ${keypair.address}`);// Funding an address with SOL automatically creates an accountconst signature = await airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: keypair.address,lamports: lamports(1_000_000_000n),commitment: "confirmed"});const accountInfo = await rpc.getAccountInfo(keypair.address).send();console.log(accountInfo);
Системные аккаунты (Sysvar)
Системные аккаунты (Sysvar) — это специальные аккаунты по предопределённым адресам, которые предоставляют только для чтения доступ к данным состояния кластера. Они обновляются динамически каждый slot.
| Sysvar | Адрес | Назначение |
|---|---|---|
| Clock | SysvarC1ock11111111111111111111111111111111 | Текущий slot, epoch и Unix-временная метка |
| EpochSchedule | SysvarEpochSchedu1e111111111111111111111111 | Константы расписания epoch, заданные в genesis |
| EpochRewards | SysvarEpochRewards1111111111111111111111111 | Статус и прогресс распределения наград за epoch |
| Rent | SysvarRent111111111111111111111111111111111 | Ставка rent и порог освобождения |
| SlotHashes | SysvarS1otHashes111111111111111111111111111 | Последние хэши родительских банков slot |
| StakeHistory | SysvarStakeHistory1111111111111111111111111 | Активация и деактивация стейка по epoch |
| LastRestartSlot | SysvarLastRestartS1ot1111111111111111111111 | Последний slot перезапуска кластера |
| Instructions | Sysvar1nstructions1111111111111111111111111 | Сериализованные инструкции текущей транзакции |
| SlotHistory | SysvarS1otHistory11111111111111111111111111 | Запись о том, какие slot были произведены за последний epoch |
В следующем примере выполняется получение и десериализация системного аккаунта Clock.
import { createSolanaRpc } from "@solana/kit";import { fetchSysvarClock, SYSVAR_CLOCK_ADDRESS } from "@solana/sysvars";const rpc = createSolanaRpc("https://api.mainnet.solana.com");const accountInfo = await rpc.getAccountInfo(SYSVAR_CLOCK_ADDRESS, { encoding: "base64" }).send();console.log(accountInfo);// Automatically fetch and deserialize the account dataconst clock = await fetchSysvarClock(rpc);console.log(clock);
Is this page helpful?