Транзакции

Чтобы взаимодействовать с сетью Solana, необходимо отправить транзакцию. Вы можете представить транзакцию как конверт, содержащий несколько форм. Каждая форма — это инструкция, которая сообщает сети, что нужно сделать. Отправка транзакции подобна отправке конверта по почте, чтобы формы могли быть обработаны.

Пример ниже показывает упрощённую версию двух транзакций. Когда первая транзакция будет обработана, она выполнит одну инструкцию. Когда вторая транзакция будет обработана, она выполнит три инструкции в последовательном порядке: сначала инструкцию 1, затем инструкцию 2, затем инструкцию 3.

Транзакции являются атомарными: если одна инструкция не выполнится, вся транзакция завершится неудачей, и изменения не произойдут.

Упрощённая диаграмма, показывающая две транзакцииУпрощённая диаграмма, показывающая две транзакции

Transaction состоит из следующей информации:

  • signatures: Массив подписей
  • message: Информация о транзакции, включая список инструкций для обработки
Transaction
pub struct Transaction {
#[wasm_bindgen(skip)]
#[serde(with = "short_vec")]
pub signatures: Vec<Signature>,
#[wasm_bindgen(skip)]
pub message: Message,
}

Диаграмма, показывающая две части транзакцииДиаграмма, показывающая две части транзакции

Транзакции имеют общий лимит размера в 1232 байт. Этот лимит включает как массив signatures, так и структуру message.

Этот лимит обусловлен размером максимального блока передачи данных (MTU) IPv6 в 1280 байт, за вычетом 48 байт для сетевых заголовков (40 байт IPv6 + 8 байт заголовка).

Диаграмма, показывающая формат транзакции и ограничения по размеруДиаграмма, показывающая формат транзакции и ограничения по размеру

Подписи

Массив signatures транзакции содержит структуры Signature. Каждая Signature имеет размер 64 байта и создаётся путём подписания Message транзакции с использованием приватного ключа аккаунта. Подпись должна быть предоставлена для каждого аккаунта-подписанта, включённого в любую из инструкций транзакции.

Первая подпись принадлежит аккаунту, который оплатит базовую комиссию транзакции, и является подписью транзакции. Подпись транзакции можно использовать для поиска деталей транзакции в сети.

Сообщение

Сообщение транзакции message представляет собой структуру Message, которая содержит следующую информацию:

Чтобы сэкономить место, транзакция не хранит разрешения для каждого аккаунта отдельно. Вместо этого разрешения аккаунтов определяются с использованием header и account_keys.

Message
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: Общее количество только для чтения аккаунтов, которые не требуют подписей
MessageHeader
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 должен быть полным и строго упорядоченным следующим образом:

  1. Подписант + Доступ на запись
  2. Подписант + Только для чтения
  3. Не подписант + Доступ на запись
  4. Не подписант + Только для чтения

Строгая упорядоченность позволяет массиву account_keys быть объединённым с информацией в заголовке сообщения header для определения разрешений для каждого аккаунта.

Диаграмма, показывающая порядок массива адресов аккаунтовДиаграмма, показывающая порядок массива адресов аккаунтов

Недавний blockhash

recent_blockhash сообщения — это хэш-значение, которое действует как временная метка транзакции и предотвращает дублирование транзакций. Blockhash истекает через 150 блоков. (Эквивалентно одной минуте — при условии, что каждый блок занимает 400 мс.) После истечения срока действия блока транзакция становится недействительной и не может быть обработана.

Метод RPC getLatestBlockhash позволяет получить текущий blockhash и последний номер блока, на котором blockhash будет действителен.

Инструкции

instructions сообщения — это массив всех инструкций, которые должны быть обработаны, отправленных в компактном формате массива. Префикс массива указывает его длину. Каждый элемент массива представляет собой структуру CompiledInstruction и включает следующую информацию:

  1. program_id_index: Индекс, указывающий на адрес в массиве account_keys. Это значение указывает адрес программы, которая обрабатывает инструкцию.
  2. accounts: Массив индексов, указывающих на адреса в массиве account_keys. Каждый индекс указывает на адрес аккаунта, необходимого для этой инструкции.
  3. data: Массив байтов, указывающий, какую инструкцию вызвать в программе. Он также включает любые дополнительные данные, необходимые для инструкции (например, аргументы функции).
CompiledInstruction
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 keypairs
const sender = await generateKeyPairSigner();
const recipient = await generateKeyPairSigner();
// Define the amount to transfer
const 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 recipient
const transferInstruction = getTransferSolInstruction({
source: sender,
destination: recipient.address,
amount: transferAmount
});
// Create transaction message
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(sender, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction =
await signTransactionMessageWithSigners(transactionMessage);
// Decode the messageBytes
const compiledTransactionMessage =
getCompiledTransactionMessageDecoder().decode(signedTransaction.messageBytes);
console.log(JSON.stringify(compiledTransactionMessage, null, 2));
Console
Click to execute the code.

Код ниже показывает вывод из предыдущих фрагментов кода. Формат отличается между 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.

Transaction Data
{
"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?

Содержание

Редактировать страницу
Транзакции | Solana