Підсумок
Транзакції проходять через 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
обгортає зовнішнє сховище акаунтів і підтримує локальний кеш пакета, щоб
акаунти, змінені попередніми транзакціями в тому ж пакеті, були видимі для
наступних.
Для кожного акаунта, що не є платником комісії, завантажувач:
- Отримує акаунт з кешу або accounts-db
- Оновлює статус звільнення від оренди за потреби
- Накопичує розмір даних акаунта до
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
ітерує через кожну інструкцію та викликає цільову програму:
- Для кожної інструкції середовище виконання викликає
prepare_next_top_level_instruction, який створюєInstructionContext. Цей контекст містить посилання на акаунти інструкції (отримані з скомпільованих індексів), дані інструкції та індекс акаунта програми. - Середовище виконання перевіряє, чи є програма прекомпіляцією (Ed25519, Secp256k1, Secp256r1). Прекомпіляції перевіряються безпосередньо без виклику BPF VM.
- Для всіх інших програм середовище виконання викликає
process_instruction, який завантажує програму з кешу та виконує її у віртуальній машині BPF. - Після завершення інструкції середовище виконання
перевіряє,
що загальний баланс lamport по всіх акаунтах інструкції не змінився
(перевірка
UnbalancedInstruction). - Якщо будь-яка інструкція завершується невдало, вся транзакція відкочується. Жодні проміжні зміни стану не фіксуються.
Кожна інструкція збільшує трасування інструкцій. Трасування включає як
інструкції верхнього рівня, так і будь-які 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?