Аккаунты
Все данные в сети Solana хранятся в аккаунтах. Вы можете представить сеть Solana как публичную базу данных с одной таблицей аккаунтов. Отношение между аккаунтом и его адресом похоже на пару ключ-значение, где ключ — это адрес, а значение — аккаунт.
Каждый аккаунт имеет одинаковую базовую структуру и может быть найден с помощью его адреса.
Диаграмма 3 аккаунтов и их адресов. Включает определение структуры аккаунта.
Адрес аккаунта
Адрес аккаунта — это уникальный 32-байтовый идентификатор, используемый для поиска аккаунта в блокчейне Solana. Адреса аккаунтов часто отображаются в виде строк, закодированных в base58. Большинство аккаунтов используют Ed25519 публичный ключ в качестве адреса, но это не обязательно, так как Solana также поддерживает программные производные адреса.
Аккаунт с адресом публичного ключа, закодированным в base58
Публичный ключ
Пример ниже демонстрирует, как использовать Solana SDK для создания keypair.
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Программный производный адрес
Программный производный адрес (PDA) — это адрес, который детерминированно создается с использованием ID программы и одного или нескольких опциональных входных данных (seed). Пример ниже демонстрирует, как использовать Solana SDK для создания программного производного адреса.
import { Address, getProgramDerivedAddress } from "@solana/kit";const programAddress = "11111111111111111111111111111111" as Address;const seeds = ["helloWorld"];const [pda, bump] = await getProgramDerivedAddress({programAddress,seeds});console.log(`PDA: ${pda}`);console.log(`Bump: ${bump}`);
Структура аккаунта
Каждый
Account
имеет максимальный размер
10MiB
и содержит следующую информацию:
lamports: Количество лампортов в аккаунтеdata: Данные аккаунтаowner: ID программы, которая владеет аккаунтомexecutable: Указывает, содержит ли аккаунт исполняемый бинарный файлrent_epoch: Устаревшее поле эпохи аренды
pub struct Account {/// lamports in the accountpub lamports: u64,/// data held in this account#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]pub data: Vec<u8>,/// the program that owns this account. If executable, the program that loads this account.pub owner: Pubkey,/// this account's data contains a loaded program (and is now read-only)pub executable: bool,/// the epoch at which this account will next owe rentpub rent_epoch: Epoch,}
Lamports
Баланс аккаунта в lamports.
Каждый аккаунт должен иметь минимальный баланс в lamports, называемый rent, который позволяет хранить его данные в блокчейне. Rent пропорционален размеру аккаунта.
Хотя этот баланс называется арендой, он работает скорее как депозит, так как весь баланс может быть возвращен, когда аккаунт закрывается. (Название "аренда" происходит от устаревшего поля rent epoch.)
(См. формулу минимального баланса и применимые константы.)
Данные
Это поле обычно называется "данные аккаунта". data в этом поле считается
произвольным, так как оно может содержать любую последовательность байтов.
Каждая программа определяет структуру данных, хранящихся в этом поле.
- Программные аккаунты: Это поле содержит либо исполняемый код программы, либо адрес аккаунта данных программы, который хранит исполняемый код программы.
 - Аккаунты данных: Это поле обычно хранит данные состояния, предназначенные для чтения.
 
Чтение данных из аккаунта Solana включает два шага:
- Получение аккаунта с использованием его адреса
 - Десериализация поля 
dataаккаунта из сырых байтов в соответствующую структуру данных, определенную программой, которой принадлежит аккаунт. 
Владелец
Это поле содержит ID программы, которой принадлежит аккаунт.
Каждый аккаунт Solana имеет программу, назначенную его
владельцем. Только программа-владелец может изменять data аккаунта или
вычитать lamports, как указано в инструкциях программы.
(В случае программного аккаунта владельцем является его загрузочная программа.)
Исполняемый
Это поле указывает, является ли аккаунт программным аккаунтом или аккаунтом данных
- Если 
true: Аккаунт является программным аккаунтом - Если 
false: Аккаунт является аккаунтом данных 
Эпоха аренды
Поле rent_epoch устарело.
Ранее это поле отслеживало, когда аккаунту нужно было платить аренду. Однако этот механизм сбора аренды был впоследствии упразднён.
Типы аккаунтов
Существует две основные категории аккаунтов:
- Программные аккаунты: Аккаунты, содержащие исполняемый код
 - Аккаунты данных: Аккаунты, не содержащие исполняемый код
 
Это разделение означает, что исполняемый код программы и её состояние хранятся в отдельных аккаунтах. (Аналогично операционным системам, которые обычно имеют отдельные файлы для программ и их данных.)
Программные аккаунты
Каждая программа принадлежит загрузочной программе, которая используется для развёртывания и управления аккаунтом. Когда новая программа развёртывается, создаётся аккаунт для хранения её исполняемого кода. Этот аккаунт называется программным аккаунтом. (Для упрощения можно считать программный аккаунт самой программой.)
На диаграмме ниже показано, как загрузочная программа используется для
развёртывания программного аккаунта. Поле data программного аккаунта содержит
исполняемый код программы.
Диаграмма программного аккаунта, его 4 компонентов и загрузочной программы.
Аккаунты данных программы
Программы, развёрнутые с использованием loader-v3, не содержат программный код в
своём поле data. Вместо этого их поле data указывает на отдельный аккаунт
данных программы, который содержит программный код. (См. диаграмму ниже.)
Программный аккаунт с данными. Данные указывают на отдельный аккаунт данных программы
Во время развертывания или обновления программы буферные аккаунты используются для временного размещения загрузки.
Пример ниже извлекает аккаунт Token Program. Обратите внимание, что поле
executable установлено в true, что указывает на то, что аккаунт является
программой.
import { Address, createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");const programId = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address;const accountInfo = await rpc.getAccountInfo(programId, { encoding: "base64" }).send();console.log(accountInfo);
Аккаунты данных
Аккаунты данных не содержат исполняемого кода. Вместо этого они хранят информацию.
Аккаунт состояния программы
Программы используют аккаунты данных для поддержания своего состояния. Для этого они должны сначала создать новый аккаунт данных. Процесс создания аккаунта состояния программы часто абстрагирован, но полезно понимать основной процесс.
Чтобы управлять своим состоянием, новая программа должна:
- Вызвать System Program для создания аккаунта. (System Program затем передает владение новой программе.)
 - Инициализировать данные аккаунта, как это определено в его инструкциях.
 
Диаграмма аккаунта данных, принадлежащего программному аккаунту
Пример ниже создает и извлекает аккаунт Token Mint, принадлежащий программе 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. Аккаунты, принадлежащие System Program, называются системными аккаунтами. Все аккаунты кошельков являются системными аккаунтами, что позволяет им оплачивать комиссии за транзакции.
Кошелек, принадлежащий System Program, содержащий 1,000,000 лампортов
Когда SOL отправляется на новый адрес впервые, на этом адресе создается аккаунт, принадлежащий System Program.
В приведённом ниже примере создаётся новая пара ключей (keypair) и пополняется
SOL. После выполнения кода вы можете увидеть адрес аккаунта owner, который
является 11111111111111111111111111111111
(Системная программа).
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 существуют по заранее определённым адресам и предоставляют доступ к данным состояния кластера. Они динамически обновляются с данными о сетевом кластере. Полный список можно найти в Аккаунты Sysvar.
Пример ниже извлекает и десериализует данные из аккаунта Sysvar Clock.
import { createSolanaRpc } from "@solana/kit";import { fetchSysvarClock, SYSVAR_CLOCK_ADDRESS } from "@solana/sysvars";const rpc = createSolanaRpc("https://api.mainnet-beta.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?