Конвеєр транзакцій

Підсумок

Транзакції проходять через 8 етапів: отримання, перевірка підпису, санітизація, перевірка бюджету/віку, валідація платника комісії, завантаження облікових записів, виконання інструкцій та фіксація.

Конвеєр обробки транзакцій

Коли транзакція надходить до валідатора, вона проходить через серію етапів валідації та виконання. Нижче описано повний конвеєр від отримання до фіксації з посиланнями на файли вихідного коду клієнта валідатора agave.

1. Отримання та десеріалізація

Валідатор отримує байти транзакції через UDP/QUIC. Необроблені байти мають поміститися в один пакет (PACKET_DATA_SIZE = 1 232 байти). Байти десеріалізуються в VersionedTransaction, який містить масив підписів та VersionedMessage (legacy або v0).

2. Перевірка підпису (sigverify)

Підписи перевіряються на етапі sigverify перед тим, як транзакція потрапляє на етап banking. Для кожного підпису з індексом i верифікатор перевіряє Ed25519(signatures[i], account_keys[i], message_bytes). Якщо будь-який підпис недійсний, пакет відкидається.

Перевірка паралелізована: валідатор розділяє пакети на фрагменти по VERIFY_PACKET_CHUNK_SIZE (128) і обробляє їх паралельно.

3. Санітизація

Десеріалізована транзакція санітизується для створення SanitizedTransaction (або RuntimeTransaction). Санітизація валідує структурні інваріанти:

  • Кількість підписів відповідає num_required_signatures у заголовку
  • Усі program_id_index та account_indices інструкцій знаходяться в межах допустимих значень
  • Платник комісії (обліковий запис з індексом 0) є підписантом з правом запису

Обгортка RuntimeTransaction кешує попередньо обчислені метадані з TransactionMeta: хеш повідомлення, прапорець транзакції голосування, кількість підписів прекомпіляції (Ed25519/secp256k1/secp256r1), деталі інструкцій обчислювального бюджету та загальну довжину даних інструкцій.

4. Перевірка бюджету обчислень, віку та кешу статусу

Метод check_transactions виконує кілька перевірок для кожної транзакції:

Бюджет обчислень: інструкції бюджету обчислень транзакції спочатку парсяться та валідуються. Деталі комісії розраховуються на основі лімітів бюджету та комісії пріоритизації. Якщо бюджет обчислень недійсний або суперечливий, транзакція завершується помилками парсингу бюджету обчислень, такими як DuplicateInstruction, InstructionError(..., InvalidInstructionData) або InvalidLoadedAccountsDataSizeLimit.

Вік блокхешу: recent_blockhash транзакції шукається в BlockhashQueue. Якщо хеш знайдено і його вік знаходиться в межах MAX_PROCESSING_AGE (150 слотів), транзакція продовжується. Якщо не знайдено, валідатор перевіряє наявність дійсного довготривалого nonce.

Кеш статусу: хеш повідомлення транзакції перевіряється в кеші статусу. Якщо знайдено, транзакція відхиляється з помилкою AlreadyProcessed.

5. Валідація nonce та платника комісії

Метод validate_transaction_nonce_and_fee_payer в SVM обробляє дві валідації:

Валідація nonce (якщо застосовно): для nonce-транзакцій валідатор завантажує nonce-акаунт і перевіряє:

  • Акаунт належить System Program
  • Він парситься як State::Initialized
  • Збережений довготривалий nonce відповідає recent_blockhash транзакції
  • Nonce може бути просунутий (його поточний довготривалий nonce відрізняється від наступного довготривалого nonce, тобто nonce ще не використовувався в поточному блоці)
  • Авторитет nonce підписав транзакцію

Якщо валідація успішна, nonce просувається до наступного значення довготривалого nonce. Див. validate_transaction_nonce.

Валідація платника комісії: акаунт платника комісії (завжди індекс 0) завантажується та перевіряється методом validate_fee_payer:

  • Акаунт повинен існувати (lamports > 0), інакше AccountNotFound
  • Акаунт повинен бути системним акаунтом або nonce-акаунтом, інакше InvalidAccountForFee
  • Lamports повинні покривати min_balance + total_fee, де min_balance дорівнює 0 для системних акаунтів або rent.minimum_balance(NonceState::size()) для nonce-акаунтів; інакше InsufficientFundsForFee
  • Після вирахування комісії акаунт повинен залишатися звільненим від оренди (не може переходити зі стану звільнення від оренди до стану сплати оренди)

Комісія вираховується з платника комісії на цьому етапі. Знімок платника комісії після вирахування комісії (та просунутого nonce, якщо застосовно) зберігається як RollbackAccounts, це акаунти, які фіксуються навіть якщо виконання завершується невдачею.

6. Завантаження акаунтів

load_transaction завантажує всі акаунти, на які посилається транзакція. AccountLoader обгортає зовнішнє сховище акаунтів і підтримує локальний кеш пакета, щоб акаунти, змінені попередніми транзакціями в тому ж пакеті, були видимі для наступних.

Для кожного акаунта, що не є платником комісії, завантажувач:

  1. Отримує акаунт з кешу або accounts-db
  2. Оновлює статус звільнення від оренди за потреби
  3. Накопичує розмір даних акаунта до loaded_accounts_data_size_limit (за замовчуванням 64 МіБ). Кожен акаунт має базові витрати TRANSACTION_ACCOUNT_BASE_SIZE (64 байти) плюс довжина його даних

Для кожної програми, викликаної інструкціями транзакції, завантажувач перевіряє, що program account існує і належить валідному завантажувачу (NativeLoader або одному з PROGRAM_OWNERS). Невалідні програми завершуються з помилкою ProgramAccountNotFound або InvalidProgramForExecution.

Програми LoaderV3 (оновлювані) неявно завантажують свій асоційований programdata account, який також враховується в ліміт розміру завантажених даних.

Якщо завантаження акаунта завершується невдачею, але платник комісії був успішно валідований, транзакція стає FeesOnly результатом: комісія все одно стягується, але жодна інструкція не виконується.

7. Виконання інструкцій

execute_loaded_transaction створює TransactionContext з усіма завантаженими акаунтами та викликає process_message. Інструкції виконуються послідовно в порядку, в якому вони з'являються в повідомленні. Кожен виклик інструкції створює InvokeContext і викликає цільову програму.

Деталі обробки інструкцій

Функція runtime process_message ітерує через кожну інструкцію та викликає цільову програму:

  1. Для кожної інструкції середовище виконання викликає prepare_next_top_level_instruction, який створює InstructionContext. Цей контекст містить посилання на акаунти інструкції (отримані з скомпільованих індексів), дані інструкції та індекс акаунта програми.
  2. Середовище виконання перевіряє, чи є програма прекомпіляцією (Ed25519, Secp256k1, Secp256r1). Прекомпіляції перевіряються безпосередньо без виклику BPF VM.
  3. Для всіх інших програм середовище виконання викликає process_instruction, який завантажує програму з кешу та виконує її у віртуальній машині BPF.
  4. Після завершення інструкції середовище виконання перевіряє, що загальний баланс lamport по всіх акаунтах інструкції не змінився (перевірка UnbalancedInstruction).
  5. Якщо будь-яка інструкція завершується невдало, вся транзакція відкочується. Жодні проміжні зміни стану не фіксуються.

Кожна інструкція збільшує трасування інструкцій. Трасування включає як інструкції верхнього рівня, так і будь-які CPI, які вони викликають. Загальна довжина трасування (інструкції верхнього рівня плюс усі вкладені CPI) не може перевищувати 64 (MAX_INSTRUCTION_TRACE_LENGTH). Перевищення цього ліміту повертає InstructionError::MaxInstructionTraceLengthExceeded.

Після виконання середовище виконання перевіряє, що:

  • Сума lamport по всіх акаунтах не змінилася
  • Жоден акаунт не перейшов зі стану звільнення від оренди до стану оплати оренди

8. Фіксація або відкат

Якщо виконання успішне, змінені стани акаунтів з TransactionContext записуються назад у локальний кеш пакета AccountLoader. Якщо виконання завершується невдало, записуються назад лише RollbackAccounts (платник комісії з вирахуваною комісією та просунутим nonce). Комісія все одно стягується, але всі інші зміни акаунтів відкидаються.

Підсумок конвеєра

Receive packet (UDP/QUIC)
--> Deserialize into VersionedTransaction
--> Sigverify (parallel Ed25519 verification)
--> Sanitize (structural validation, metadata extraction)
--> Parse compute budget, calculate fees
--> Check blockhash age (or verify nonce account)
--> Check status cache (dedup)
--> Validate nonce authority and advanceability (if nonce transaction)
--> Validate fee payer (load, check balance, deduct fee)
--> Load all accounts (with data size limits)
--> Load programs (verify loaders)
--> Execute instructions sequentially
--> Verify post-conditions (lamport balance, rent state)
--> Commit account changes (or rollback on failure)

Довідник помилок транзакцій

Наступна таблиця містить усі варіанти TransactionError та етапи конвеєра, на яких вони виникають:

ПомилкаЕтапПричина
AccountInUseПлануванняОбліковий запис вже заблоковано іншою транзакцією в тому самому пакеті
AccountLoadedTwiceПлануванняPubkey з'являється двічі в account_keys транзакції
AccountNotFoundПеревірка платника комісіїОбліковий запис платника комісії не існує
ProgramAccountNotFoundЗавантаження облікових записівВикликана програма не існує
InsufficientFundsForFeeПеревірка платника комісіїПлатник комісії не може покрити комісію + мінімум для звільнення від rent
InvalidAccountForFeeПеревірка платника комісіїПлатник комісії не є системним обліковим записом або обліковим записом nonce
AlreadyProcessedКеш статусуТранзакція вже була оброблена
BlockhashNotFoundПеревірка вікуBlockhash відсутній у черзі і не є дійсним nonce
InstructionErrorВиконанняПід час обробки інструкції сталася помилка (включає індекс інструкції та конкретний InstructionError)
CallChainTooDeepЗавантаження облікових записівЛанцюг викликів завантажувача занадто глибокий
MissingSignatureForFeeСанітизаціяТранзакція потребує комісії, але не має підпису
InvalidAccountIndexСанітизаціяТранзакція містить недійсне посилання на обліковий запис
SignatureFailureПеревірка підписуПідпис Ed25519 не перевіряється (пакет відкидається)
InvalidProgramForExecutionЗавантаження облікових записівПрограма не належить дійсному завантажувачу
SanitizeFailureСанітизаціяТранзакція не змогла правильно санітизувати зміщення облікових записів
ClusterMaintenanceПлануванняТранзакції наразі вимкнено через обслуговування кластера
AccountBorrowOutstandingВиконанняОбробка транзакції залишила обліковий запис із непогашеним запозиченим посиланням
WouldExceedMaxBlockCostLimitПлануванняТранзакція перевищить максимальний ліміт вартості блоку
UnsupportedVersionСанітизаціяВерсія транзакції не підтримується
InvalidWritableAccountЗавантаження облікових записівТранзакція завантажує обліковий запис для запису, який не може бути записаний
WouldExceedMaxAccountCostLimitПлануванняТранзакція перевищить максимальний ліміт вартості облікового запису в блоці
WouldExceedAccountDataBlockLimitПлануванняТранзакція перевищить ліміт даних облікового запису в блоці
TooManyAccountLocksПлануванняТранзакція заблокувала занадто багато облікових записів
AddressLookupTableNotFoundЗавантаження облікових записівОбліковий запис таблиці пошуку адрес не існує
InvalidAddressLookupTableOwnerЗавантаження облікових записівТаблиця пошуку адрес належить неправильній програмі
InvalidAddressLookupTableDataЗавантаження облікових записівТаблиця пошуку адрес містить недійсні дані
InvalidAddressLookupTableIndexЗавантаження облікових записівПошук у таблиці адрес використовує недійсний індекс
InvalidRentPayingAccountПеревірка після виконанняОбліковий запис перейшов зі звільненого від rent до платного за rent
WouldExceedMaxVoteCostLimitПлануванняТранзакція перевищить максимальний ліміт вартості голосування
WouldExceedAccountDataTotalLimitПлануванняТранзакція перевищить загальний ліміт даних облікового запису
DuplicateInstructionРозбір бюджету обчисленьДублікат варіанта інструкції бюджету обчислень у тій самій транзакції
InsufficientFundsForRentПеревірка після виконанняОбліковий запис не має достатньо lamport для покриття rent за розмір його даних
MaxLoadedAccountsDataSizeExceededЗавантаження облікових записівЗагальний обсяг завантажених даних перевищує ліміт 64 МіБ
InvalidLoadedAccountsDataSizeLimitРозбір бюджету обчисленьSetLoadedAccountsDataSizeLimit встановлено на 0
ResanitizationNeededСанітизаціяТранзакція відрізнялася до/після активації функції та потребує повторної санітизації
ProgramExecutionTemporarilyRestrictedЗавантаження облікових записівВиконання програми тимчасово обмежено для вказаного облікового запису
UnbalancedTransactionПеревірка після виконанняЗагальний баланс lamport до транзакції не дорівнює балансу після
ProgramCacheHitMaxLimitЗавантаження облікових записівКеш програми досяг максимального ліміту
CommitCancelledФіксаціяФіксацію скасовано внутрішньо

Is this page helpful?

Керується

© 2026 Фонд Solana.
Всі права захищені.
Залишайтеся на зв'язку