Чтобы взаимодействовать с сетью Solana, необходимо отправить транзакцию. Вы можете представить транзакцию как конверт, содержащий несколько форм. Каждая форма — это инструкция, которая сообщает сети, что нужно сделать. Отправка транзакции подобна отправке конверта по почте, чтобы формы могли быть обработаны.
Пример ниже показывает упрощённую версию двух транзакций. Когда первая транзакция будет обработана, она выполнит одну инструкцию. Когда вторая транзакция будет обработана, она выполнит три инструкции в последовательном порядке: сначала инструкцию 1, затем инструкцию 2, затем инструкцию 3.
Транзакции являются атомарными: если одна инструкция не выполнится, вся транзакция завершится неудачей, и изменения не произойдут.
Упрощённая диаграмма, показывающая две транзакции
Transaction
содержит следующую информацию:
signatures: Массив подписейmessage: Информация о транзакции, включая список инструкций для обработки
pub struct Transaction {#[wasm_bindgen(skip)]#[serde(with = "short_vec")]pub signatures: Vec<Signature>,#[wasm_bindgen(skip)]pub message: Message,}
Диаграмма, показывающая две части транзакции
Размер транзакции ограничен
1232
байт. Это ограничение включает как массив signatures, так и
структуру message.
Этот лимит предназначен для предотвращения фрагментации пакетов в типичной интернет-инфраструктуре. Хотя IPv6 поддерживает MTU больше 9000 байт, большинство интернет-маршрутизаторов используют стандартное значение MTU в 1500 байт (стандарт Ethernet). Чтобы гарантировать, что транзакции помещаются в один пакет без фрагментации, Solana использует 1280 байт (минимальное значение MTU, требуемое для IPv6) минус 48 байт для сетевых заголовков (40 байт для IPv6 + 8 байт для заголовка фрагментации/UDP), что в итоге дает ограничение размера транзакции в 1232 байта.
Диаграмма, показывающая формат транзакции и ограничения по размеру
Подписи
Массив signatures транзакции содержит структуры Signature. Каждая
Signature
занимает 64 байта и создаётся путём подписи Message транзакции приватным
ключом аккаунта. Для каждого аккаунта-подписанта,
участвующего в любой из инструкций транзакции, должна быть предоставлена
подпись.
Первая подпись принадлежит аккаунту, который оплатит базовую комиссию транзакции, и является подписью транзакции. Подпись транзакции можно использовать для поиска деталей транзакции в сети.
Сообщение
Структура message транзакции — это
Message,
которая содержит следующую информацию:
header: Заголовок header сообщенияaccount_keys: Массив адресов аккаунтов, необходимых для инструкций транзакцииrecent_blockhash: blockhash, который выступает в роли временной метки для транзакцииinstructions: Массив инструкций
Чтобы сэкономить место, транзакция не хранит права доступа для каждого
аккаунта отдельно. Вместо этого права определяются с помощью header и
account_keys.
pub struct Message {/// The message header, identifying signed and read-only `account_keys`.pub header: MessageHeader,/// All the account keys used by this transaction.#[serde(with = "short_vec")]pub account_keys: Vec<Pubkey>,/// The id of a recent ledger entry.pub recent_blockhash: Hash,/// Programs that will be executed in sequence and committed in/// one atomic transaction if all succeed.#[serde(with = "short_vec")]pub instructions: Vec<CompiledInstruction>,}
Заголовок
Заголовок сообщения header — это
MessageHeader
структура. Она содержит следующую информацию:
num_required_signatures: Общее количество подписей, необходимых для транзакцииnum_readonly_signed_accounts: Общее количество аккаунтов только для чтения, для которых требуется подписьnum_readonly_unsigned_accounts: Общее количество аккаунтов только для чтения, для которых подпись не требуется
pub struct MessageHeader {/// The number of signatures required for this message to be considered/// valid. The signers of those signatures must match the first/// `num_required_signatures` of [`Message::account_keys`].pub num_required_signatures: u8,/// The last `num_readonly_signed_accounts` of the signed keys are read-only/// accounts.pub num_readonly_signed_accounts: u8,/// The last `num_readonly_unsigned_accounts` of the unsigned keys are/// read-only accounts.pub num_readonly_unsigned_accounts: u8,}
Диаграмма, показывающая три части заголовка сообщения
Адреса аккаунтов
Поле сообщения
account_keys
— это массив адресов аккаунтов, передаваемый в
компактном формате массива.
Префикс массива указывает его длину. Каждый элемент массива — это публичный
ключ, указывающий на аккаунт, используемый в инструкциях. Массив accounts_keys
должен быть полным и строго упорядоченным следующим образом:
- Подписант + Доступ на запись
- Подписант + Только для чтения
- Не подписант + Доступ на запись
- Не подписант + Только для чтения
Строгий порядок позволяет массиву account_keys быть объединённым с
информацией из поля сообщения header для определения прав доступа
для каждого аккаунта.
Диаграмма, показывающая порядок массива адресов аккаунтов
Недавний blockhash
Поле recent_blockhash сообщения — это хеш-значение, которое выступает в роли
метки времени транзакции и предотвращает дублирование транзакций. Blockhash
истекает через
150 блоков.
(Это эквивалентно одной минуте — при условии, что каждый блок занимает 400 мс.)
После истечения срока действия блока транзакция становится недействительной и не
может быть обработана.
Метод RPC getLatestBlockhash позволяет
получить текущий blockhash и последний номер блока, на котором blockhash будет
действителен.
Инструкции
Поле сообщения
instructions
— это массив всех инструкций для обработки, передаваемый в
компактном формате массива.
Префикс массива указывает его длину. Каждый элемент массива — это структура
CompiledInstruction
и содержит следующую информацию:
program_id_index: Индекс, указывающий на адрес в массивеaccount_keys. Это значение определяет адрес программы, которая обрабатывает инструкцию.accounts: Массив индексов, указывающих на адреса в массивеaccount_keys. Каждый индекс указывает на адрес аккаунта, необходимого для этой инструкции.data: Массив байтов, определяющий, какую инструкцию вызвать в программе. Также включает любые дополнительные данные, необходимые для инструкции (например, аргументы функции).
pub struct CompiledInstruction {/// Index into the transaction keys array indicating the program account that executes this instruction.pub program_id_index: u8,/// Ordered indices into the transaction keys array indicating which accounts to pass to the program.#[serde(with = "short_vec")]pub accounts: Vec<u8>,/// The program input data.#[serde(with = "short_vec")]pub data: Vec<u8>,}
Компактный массив инструкций
Пример структуры транзакции
Следующий пример показывает структуру транзакции, содержащей одну инструкцию перевода SOL.
import {createSolanaRpc,generateKeyPairSigner,lamports,createTransactionMessage,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,appendTransactionMessageInstructions,pipe,signTransactionMessageWithSigners,getCompiledTransactionMessageDecoder} from "@solana/kit";import { getTransferSolInstruction } from "@solana-program/system";const rpc = createSolanaRpc("http://localhost:8899");const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// Generate sender and recipient keypairsconst sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();// Define the amount to transferconst LAMPORTS_PER_SOL = 1_000_000_000n;const transferAmount = lamports(LAMPORTS_PER_SOL / 100n); // 0.01 SOL// Create a transfer instruction for transferring SOL from sender to recipientconst transferInstruction = getTransferSolInstruction({source: sender,destination: recipient.address,amount: transferAmount});// Create transaction messageconst transactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(sender, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions([transferInstruction], tx));const signedTransaction =await signTransactionMessageWithSigners(transactionMessage);// Decode the messageBytesconst compiledTransactionMessage =getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Код ниже показывает вывод из предыдущих фрагментов кода. Формат отличается между SDK, но обратите внимание, что каждая инструкция содержит одну и ту же необходимую информацию.
{"version": 0,"header": {"numSignerAccounts": 1,"numReadonlySignerAccounts": 0,"numReadonlyNonSignerAccounts": 1},"staticAccounts": ["HoCy8p5xxDDYTYWEbQZasEjVNM5rxvidx8AfyqA4ywBa","5T388jBjovy7d8mQ3emHxMDTbUF8b7nWvAnSiP3EAdFL","11111111111111111111111111111111"],"lifetimeToken": "EGCWPUEXhqHJWYBfDirq3mHZb4qDpATmYqBZMBy9TBC1","instructions": [{"programAddressIndex": 2,"accountIndices": [0, 1],"data": {"0": 2,"1": 0,"2": 0,"3": 0,"4": 128,"5": 150,"6": 152,"7": 0,"8": 0,"9": 0,"10": 0,"11": 0}}]}
После отправки транзакции вы можете получить её детали, используя подпись транзакции и метод RPC getTransaction. Ответ будет иметь структуру, похожую на следующий фрагмент.
Вы также можете найти транзакцию с помощью Solana Explorer.
{"blockTime": 1745196488,"meta": {"computeUnitsConsumed": 150,"err": null,"fee": 5000,"innerInstructions": [],"loadedAddresses": {"readonly": [],"writable": []},"logMessages": ["Program 11111111111111111111111111111111 invoke [1]","Program 11111111111111111111111111111111 success"],"postBalances": [989995000, 10000000, 1],"postTokenBalances": [],"preBalances": [1000000000, 0, 1],"preTokenBalances": [],"rewards": [],"status": {"Ok": null}},"slot": 13049,"transaction": {"message": {"header": {"numReadonlySignedAccounts": 0,"numReadonlyUnsignedAccounts": 1,"numRequiredSignatures": 1},"accountKeys": ["8PLdpLxkuv9Nt8w3XcGXvNa663LXDjSrSNon4EK7QSjQ","7GLg7bqgLBv1HVWXKgWAm6YoPf1LoWnyWGABbgk487Ma","11111111111111111111111111111111"],"recentBlockhash": "7ZCxc2SDhzV2bYgEQqdxTpweYJkpwshVSDtXuY7uPtjf","instructions": [{"accounts": [0, 1],"data": "3Bxs4NN8M2Yn4TLb","programIdIndex": 2,"stackHeight": null}],"indexToProgramIds": {}},"signatures": ["3jUKrQp1UGq5ih6FTDUUt2kkqUfoG2o4kY5T1DoVHK2tXXDLdxJSXzuJGY4JPoRivgbi45U2bc7LZfMa6C4R3szX"]},"version": "legacy"}
Is this page helpful?