Pipeline transakcji

Podsumowanie

Transakcje przechodzą przez 8 etapów: odbiór, weryfikacja podpisu, sanityzacja, sprawdzenie budżetu/wieku, walidacja płatnika opłat, ładowanie kont, wykonanie instrukcji oraz zatwierdzenie.

Pipeline przetwarzania transakcji

Gdy transakcja dociera do validatora, przechodzi przez serię etapów walidacji i wykonania. Poniżej opisano pełny pipeline od otrzymania do zatwierdzenia, wraz z odwołaniami do plików źródłowych w agave validator client.

1. Odbiór i deserializacja

Validator odbiera bajty transakcji przez UDP/QUIC. Surowe bajty muszą zmieścić się w jednym pakiecie (PACKET_DATA_SIZE = 1232 bajtów). Bajty są deserializowane do VersionedTransaction, który zawiera tablicę podpisów oraz VersionedMessage (legacy lub v0).

2. Weryfikacja podpisu (sigverify)

Podpisy są weryfikowane na etapie sigverify zanim transakcja trafi do etapu bankowego. Dla każdego podpisu o indeksie i, weryfikator sprawdza Ed25519(signatures[i], account_keys[i], message_bytes). Jeśli którykolwiek podpis jest nieprawidłowy, pakiet jest odrzucany.

Weryfikacja jest zrównoleglona: validator dzieli pakiety na fragmenty po VERIFY_PACKET_CHUNK_SIZE (128) i przetwarza je równolegle.

3. Sanityzacja

Zdeserializowana transakcja jest sanityzowana do SanitizedTransaction (lub RuntimeTransaction). Sanityzacja sprawdza niezmienniki strukturalne:

  • Liczba podpisów odpowiada num_required_signatures w nagłówku
  • Wszystkie instrukcje program_id_index i account_indices mieszczą się w dozwolonych granicach
  • Płatnik opłat (indeks konta 0) jest podpisującym z prawem zapisu

Wrapper RuntimeTransaction buforuje wstępnie obliczone metadane z TransactionMeta: hash wiadomości, flagę transakcji głosowania, liczbę podpisów prekompilowanych (Ed25519/secp256k1/secp256r1), szczegóły instrukcji budżetowych oraz całkowitą długość instruction data.

4. Sprawdzenie budżetu obliczeniowego, wieku i cache statusu

Metoda check_transactions wykonuje kilka kontroli dla każdej transakcji:

Budżet obliczeniowy: Instrukcje dotyczące budżetu obliczeniowego transakcji są najpierw analizowane i weryfikowane. Szczegóły opłat są wyliczane na podstawie limitów budżetowych oraz opłaty za priorytet. Jeśli budżet obliczeniowy jest nieprawidłowy lub sprzeczny, transakcja zostaje odrzucona z błędami parsowania budżetu obliczeniowego, takimi jak DuplicateInstruction, InstructionError(..., InvalidInstructionData) lub InvalidLoadedAccountsDataSizeLimit.

Wiek blockhasha: recent_blockhash transakcji jest wyszukiwany w BlockhashQueue. Jeśli hash zostanie znaleziony i jego wiek mieści się w MAX_PROCESSING_AGE (150 slotów), transakcja jest kontynuowana. Jeśli nie zostanie znaleziony, validator sprawdza ważność trwałego nonce.

Cache statusu: Hash wiadomości transakcji jest sprawdzany w cache statusu. Jeśli zostanie znaleziony, transakcja zostaje odrzucona z AlreadyProcessed.

5. Walidacja nonce i fee payera

Metoda validate_transaction_nonce_and_fee_payer w SVM obsługuje dwie walidacje:

Walidacja nonce (jeśli dotyczy): Dla transakcji z nonce validator ładuje konto nonce i weryfikuje:

  • Konto jest własnością System Program
  • Parsuje się jako State::Initialized
  • Przechowywany trwały nonce odpowiada recent_blockhash transakcji
  • Nonce może zostać zaktualizowany (jego obecny trwały nonce różni się od następnego trwałego nonce, tzn. nonce nie został już użyty w bieżącym bloku)
  • Autorytet nonce podpisał transakcję

Jeśli wszystko jest poprawne, nonce zostaje zaktualizowany do następnej wartości trwałego nonce. Zobacz validate_transaction_nonce.

Walidacja fee payera: Konto fee payera (zawsze indeks 0) jest ładowane i sprawdzane przez validate_fee_payer:

  • Konto musi istnieć (lamports > 0), w przeciwnym razie AccountNotFound
  • Konto musi być kontem systemowym lub nonce, w przeciwnym razie InvalidAccountForFee
  • Lamports muszą pokrywać min_balance + total_fee, gdzie min_balance to 0 dla kont systemowych lub rent.minimum_balance(NonceState::size()) dla kont nonce; w przeciwnym razie InsufficientFundsForFee
  • Po odjęciu opłaty konto musi pozostać zwolnione z czynszu (nie może przejść ze stanu zwolnionego z czynszu do płacącego czynsz)

Opłata jest pobierana od płatnika opłat na tym etapie. Tworzony jest migawkowy zapis płatnika opłat po odjęciu opłaty (oraz zaawansowanego nonce, jeśli dotyczy), który jest zapisywany jako RollbackAccounts. Są to konta, które zostają zatwierdzone nawet w przypadku niepowodzenia wykonania.

6. Ładowanie kont

load_transaction ładuje wszystkie konta, do których odwołuje się transakcja. AccountLoader opakowuje zewnętrzny magazyn kont i utrzymuje lokalną pamięć podręczną dla batcha, dzięki czemu konta zmodyfikowane przez wcześniejsze transakcje w tej samej partii są widoczne dla kolejnych.

Dla każdego konta niebędącego płatnikiem opłat, loader:

  1. Pobiera konto z pamięci podręcznej lub accounts-db
  2. Aktualizuje status zwolnienia z opłat za wynajem, jeśli to konieczne
  3. Sumuje rozmiar danych konta do loaded_accounts_data_size_limit (domyślnie 64 MiB). Każde konto generuje podstawowy narzut TRANSACTION_ACCOUNT_BASE_SIZE (64 bajty) plus długość jego danych.

Dla każdego programu wywoływanego przez instrukcje transakcji loader sprawdza, czy program account istnieje i jest własnością prawidłowego loadera (NativeLoader lub jednego z PROGRAM_OWNERS). Nieprawidłowe programy kończą się błędem ProgramAccountNotFound lub InvalidProgramForExecution.

Programy LoaderV3 (upgradeable) domyślnie ładują powiązane konto programdata, które również wlicza się do limitu załadowanych danych.

Jeśli ładowanie konta się nie powiedzie, ale płatnik opłat został poprawnie zweryfikowany, transakcja staje się FeesOnly: opłata jest nadal pobierana, ale żadne instrukcje nie są wykonywane.

7. Wykonywanie instrukcji

execute_loaded_transaction tworzy TransactionContext ze wszystkimi załadowanymi kontami i wywołuje process_message. Instrukcje są wykonywane sekwencyjnie w kolejności, w jakiej pojawiają się w wiadomości. Każde wywołanie instrukcji tworzy InvokeContext i wywołuje docelowy program.

Szczegóły przetwarzania instrukcji

Funkcja środowiska wykonawczego process_message iteruje przez każdą instrukcję i wywołuje docelowy program:

  1. Dla każdej instrukcji środowisko uruchomieniowe wywołuje prepare_next_top_level_instruction, która buduje InstructionContext. Ten kontekst zawiera referencje do kont instrukcji (rozwiązane na podstawie skompilowanych indeksów), instruction data oraz indeksu program account.
  2. Środowisko uruchomieniowe sprawdza, czy program jest prekompilatem (Ed25519, Secp256k1, Secp256r1). Prekompilaty są weryfikowane bezpośrednio, bez wywoływania BPF VM.
  3. Dla wszystkich pozostałych programów środowisko uruchomieniowe wywołuje process_instruction, która ładuje program z cache i wykonuje go w wirtualnej maszynie BPF.
  4. Po zakończeniu instrukcji środowisko uruchomieniowe weryfikuje, że całkowity bilans lamportów na wszystkich kontach instrukcji nie uległ zmianie (UnbalancedInstruction check).
  5. Jeśli jakakolwiek instrukcja zakończy się niepowodzeniem, cała transakcja jest wycofywana. Żadne pośrednie zmiany stanu nie są zapisywane.

Każda instrukcja inkrementuje ślad instrukcji (instruction trace). Ślad obejmuje zarówno instrukcje najwyższego poziomu, jak i wszystkie wywołane przez nie CPI. Całkowita długość śladu (instrukcje najwyższego poziomu plus wszystkie zagnieżdżone CPI) nie może przekroczyć 64 (MAX_INSTRUCTION_TRACE_LENGTH). Przekroczenie tego limitu skutkuje zwróceniem InstructionError::MaxInstructionTraceLengthExceeded.

Po wykonaniu środowisko uruchomieniowe weryfikuje, że:

  • Suma lamportów na wszystkich kontach nie uległa zmianie
  • Żadne konto nie przeszło ze stanu zwolnionego z opłat za wynajem do stanu płacącego za wynajem

8. Zatwierdzenie lub wycofanie

Jeśli wykonanie zakończy się sukcesem, zmodyfikowane stany kont z TransactionContext są zapisywane z powrotem do lokalnej pamięci podręcznej batch AccountLoader. Jeśli wykonanie zakończy się niepowodzeniem, tylko RollbackAccounts (płatnik opłaty z potrąconą opłatą i zaawansowanym nonce) są zapisywane z powrotem. Opłata jest nadal pobierana, ale wszystkie inne zmiany na kontach są odrzucane.

Podsumowanie pipeline'u

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)

Referencja błędów transakcji

Poniższa tabela zawiera wszystkie warianty TransactionError i etapy pipeline'u, na których występują:

BłądEtapPrzyczyna
AccountInUsePlanowanieKonto jest już zablokowane przez inną transakcję w tej samej partii
AccountLoadedTwicePlanowaniepubkey pojawia się dwukrotnie w account_keys tej transakcji
AccountNotFoundWalidacja fee payeraKonto fee payera nie istnieje
ProgramAccountNotFoundŁadowanie kontaWywoływany program nie istnieje
InsufficientFundsForFeeWalidacja fee payeraFee payer nie może pokryć opłaty + minimum zwolnienia z rent
InvalidAccountForFeeWalidacja fee payeraFee payer nie jest kontem systemowym ani nonce
AlreadyProcessedCache statusuTransakcja została już przetworzona
BlockhashNotFoundSprawdzenie wiekuBlockhash nie znajduje się w kolejce i nie jest prawidłowym nonce
InstructionErrorWykonanieWystąpił błąd podczas przetwarzania instrukcji (zawiera indeks instrukcji i szczegóły InstructionError)
CallChainTooDeepŁadowanie kontaŁańcuch wywołań loadera jest zbyt głęboki
MissingSignatureForFeeSanityzacjaTransakcja wymaga opłaty, ale nie zawiera podpisu
InvalidAccountIndexSanityzacjaTransakcja zawiera nieprawidłowe odwołanie do konta
SignatureFailureWeryfikacja podpisuPodpis Ed25519 nie został zweryfikowany (pakiet odrzucony)
InvalidProgramForExecutionŁadowanie kontaProgram nie jest własnością prawidłowego loadera
SanitizeFailureSanityzacjaNie udało się poprawnie sanityzować offsetów kont
ClusterMaintenancePlanowanieTransakcje są obecnie wyłączone z powodu konserwacji klastra
AccountBorrowOutstandingWykonaniePrzetwarzanie transakcji pozostawiło konto z nieuregulowanym odniesieniem do pożyczki
WouldExceedMaxBlockCostLimitPlanowanieTransakcja przekroczyłaby maksymalny limit kosztów bloku
UnsupportedVersionSanityzacjaWersja transakcji nie jest obsługiwana
InvalidWritableAccountŁadowanie kontaTransakcja ładuje zapisywalne konto, które nie może być zapisane
WouldExceedMaxAccountCostLimitPlanowanieTransakcja przekroczyłaby maksymalny limit kosztów kont w bloku
WouldExceedAccountDataBlockLimitPlanowanieTransakcja przekroczyłaby limit danych kont w bloku
TooManyAccountLocksPlanowanieTransakcja zablokowała zbyt wiele kont
AddressLookupTableNotFoundŁadowanie kontaKonto tabeli lookup adresów nie istnieje
InvalidAddressLookupTableOwnerŁadowanie kontaTabela lookup adresów jest własnością niewłaściwego programu
InvalidAddressLookupTableDataŁadowanie kontaTabela lookup adresów zawiera nieprawidłowe dane
InvalidAddressLookupTableIndexŁadowanie kontaLookup tabeli adresów używa nieprawidłowego indeksu
InvalidRentPayingAccountKontrola po wykonaniuKonto przeszło ze stanu zwolnionego z rent do płacącego rent
WouldExceedMaxVoteCostLimitPlanowanieTransakcja przekroczyłaby maksymalny limit kosztów głosowania
WouldExceedAccountDataTotalLimitPlanowanieTransakcja przekroczyłaby całkowity limit danych kont
DuplicateInstructionParsowanie budżetu obliczeńZduplikowana instrukcja wariantu budżetu obliczeń w tej samej transakcji
InsufficientFundsForRentKontrola po wykonaniuKonto nie ma wystarczającej liczby lamportów, aby pokryć rent za swój rozmiar danych
MaxLoadedAccountsDataSizeExceededŁadowanie kontaCałkowita załadowana ilość danych przekracza limit 64 MiB
InvalidLoadedAccountsDataSizeLimitParsowanie budżetu obliczeńSetLoadedAccountsDataSizeLimit ustawiony na 0
ResanitizationNeededSanityzacjaTransakcja różni się przed/po aktywacji funkcji i wymaga ponownej sanityzacji
ProgramExecutionTemporarilyRestrictedŁadowanie kontaWykonywanie programu jest tymczasowo ograniczone na wskazanym koncie
UnbalancedTransactionKontrola po wykonaniuCałkowity bilans lamportów przed transakcją nie jest równy bilansowi po
ProgramCacheHitMaxLimitŁadowanie kontaCache programu osiągnął maksymalny limit
INLINE_CODE_PLACEHOLDER_500659bbedde5f5_ENDCommitCommit został anulowany wewnętrznie

Is this page helpful?

Zarządzane przez

© 2026 Solana Foundation.
Wszelkie prawa zastrzeżone.
Bądź na bieżąco