Аккаунты
Все данные в сети Solana хранятся в аккаунтах. Вы можете представить сеть Solana как публичную базу данных с одной таблицей аккаунтов. Отношение между аккаунтом и его адресом похоже на пару ключ-значение, где ключ — это адрес, а значение — аккаунт.
Каждый аккаунт имеет одинаковую базовую структуру и может быть найден с помощью его адреса.
Диаграмма 3 аккаунтов и их адресов. Включает определение структуры аккаунта.
Адрес аккаунта
Адрес аккаунта — это уникальный 32-байтовый идентификатор, используемый для поиска аккаунта в блокчейне Solana. Адреса аккаунтов часто отображаются в виде строк, закодированных в base58. Большинство аккаунтов используют Ed25519 публичный ключ в качестве адреса, но это не обязательно, так как Solana также поддерживает программные производные адреса.
Аккаунт с адресом публичного ключа, закодированным в base58
Публичный ключ
Пример ниже демонстрирует, как использовать Solana SDK для создания keypair. A
Keypair включает:
- Публичный ключ, который служит адресом аккаунта
- Приватный ключ, который используется для подписания транзакций
import { generateKeyPairSigner } from "@solana/kit";// Kit does not enable extractable private keysconst keypairSigner = await generateKeyPairSigner();console.log(keypairSigner);
Program derived address
Program derived address (PDA) — это адрес, который детерминированно создается с использованием ID программы и одного или нескольких дополнительных входных данных (seeds). Пример ниже демонстрирует, как использовать Solana SDK для создания program derived address.
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: Количество lamports в аккаунтеdata: Данные аккаунтаowner: ID программы, которой принадлежит аккаунтexecutable: Указывает, содержит ли аккаунт исполняемый бинарный файлrent_epoch: Устаревшее поле 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, он работает скорее как залог, так как весь баланс можно вернуть, когда аккаунт закрывается. (Название "rent" происходит от устаревшего поля rent epoch.)
(См. формулу минимального баланса и применимые константы.)
Данные
Это поле обычно называют "данные аккаунта". data в этом поле считаются
произвольными, так как они могут содержать любую последовательность байтов.
Каждая программа определяет структуру данных, хранящихся в этом поле.
- Аккаунты программ: Это поле содержит либо исполняемый код программы, либо адрес аккаунта данных программы, который хранит исполняемый код программы.
- Аккаунты данных: Это поле обычно хранит данные состояния, предназначенные для чтения.
Чтение данных из аккаунта Solana включает два шага:
- Получение аккаунта с использованием его адреса
- Десериализация поля
dataаккаунта из сырых байтов в соответствующую структуру данных, определённую программой, которой принадлежит аккаунт.
Владелец
Это поле содержит ID программы, которая является владельцем аккаунта.
Каждый аккаунт Solana имеет программу, назначенную его
владельцем. Только программа-владелец может изменять data аккаунта или
списывать лампорты, как указано в инструкциях программы.
(В случае аккаунта программы владельцем является её загрузочная программа.)
Исполняемый
Это поле указывает, является ли аккаунт аккаунтом программы или аккаунтом данных.
- Если
true: Аккаунт является аккаунтом программы - Если
false: Аккаунт является аккаунтом данных
Эпоха аренды
Поле rent_epoch устарело.
Ранее это поле отслеживало, когда аккаунту нужно будет платить аренду. Однако этот механизм сбора аренды был впоследствии упразднён.
Типы аккаунтов
Существует две основные категории аккаунтов:
- Программные аккаунты: Аккаунты, содержащие исполняемый код
- Данные аккаунтов: Аккаунты, не содержащие исполняемый код
Разделение кода программы и её состояния является ключевой особенностью модели аккаунтов Solana. (Это похоже на операционные системы, которые обычно имеют отдельные файлы для программ и их данных.)
Программные аккаунты
Каждая программа принадлежит загрузочной программе, которая используется для развертывания и управления аккаунтом. Когда новая программа развертывается, создается аккаунт для хранения её исполняемого кода. Этот аккаунт называется программным аккаунтом. (Для простоты можно считать программный аккаунт самой программой.)
На диаграмме ниже показано, как загрузочная программа используется для
развертывания программного аккаунта. Поле 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.
В примере ниже создается новая пара ключей и пополняется 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 существуют по предопределённым адресам и предоставляют доступ к данным состояния кластера. Они динамически обновляются с данными о сетевом кластере. См. полный список аккаунтов 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?