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_signaturesw nagłówku - Wszystkie instrukcje
program_id_indexiaccount_indicesmieszczą 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_blockhashtransakcji - 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, gdziemin_balanceto 0 dla kont systemowych lubrent.minimum_balance(NonceState::size())dla kont nonce; w przeciwnym razieInsufficientFundsForFee - 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:
- Pobiera konto z pamięci podręcznej lub accounts-db
- Aktualizuje status zwolnienia z opłat za wynajem, jeśli to konieczne
- Sumuje rozmiar danych konta do
loaded_accounts_data_size_limit(domyślnie 64 MiB). Każde konto generuje podstawowy narzutTRANSACTION_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:
- Dla każdej instrukcji środowisko uruchomieniowe wywołuje
prepare_next_top_level_instruction, która budujeInstructionContext. Ten kontekst zawiera referencje do kont instrukcji (rozwiązane na podstawie skompilowanych indeksów), instruction data oraz indeksu program account. - Środowisko uruchomieniowe sprawdza, czy program jest prekompilatem (Ed25519, Secp256k1, Secp256r1). Prekompilaty są weryfikowane bezpośrednio, bez wywoływania BPF VM.
- 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. - Po zakończeniu instrukcji środowisko uruchomieniowe
weryfikuje,
że całkowity bilans lamportów na wszystkich kontach instrukcji nie uległ
zmianie (
UnbalancedInstructioncheck). - 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łąd | Etap | Przyczyna |
|---|---|---|
AccountInUse | Planowanie | Konto jest już zablokowane przez inną transakcję w tej samej partii |
AccountLoadedTwice | Planowanie | pubkey pojawia się dwukrotnie w account_keys tej transakcji |
AccountNotFound | Walidacja fee payera | Konto fee payera nie istnieje |
ProgramAccountNotFound | Ładowanie konta | Wywoływany program nie istnieje |
InsufficientFundsForFee | Walidacja fee payera | Fee payer nie może pokryć opłaty + minimum zwolnienia z rent |
InvalidAccountForFee | Walidacja fee payera | Fee payer nie jest kontem systemowym ani nonce |
AlreadyProcessed | Cache statusu | Transakcja została już przetworzona |
BlockhashNotFound | Sprawdzenie wieku | Blockhash nie znajduje się w kolejce i nie jest prawidłowym nonce |
InstructionError | Wykonanie | Wystą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 |
MissingSignatureForFee | Sanityzacja | Transakcja wymaga opłaty, ale nie zawiera podpisu |
InvalidAccountIndex | Sanityzacja | Transakcja zawiera nieprawidłowe odwołanie do konta |
SignatureFailure | Weryfikacja podpisu | Podpis Ed25519 nie został zweryfikowany (pakiet odrzucony) |
InvalidProgramForExecution | Ładowanie konta | Program nie jest własnością prawidłowego loadera |
SanitizeFailure | Sanityzacja | Nie udało się poprawnie sanityzować offsetów kont |
ClusterMaintenance | Planowanie | Transakcje są obecnie wyłączone z powodu konserwacji klastra |
AccountBorrowOutstanding | Wykonanie | Przetwarzanie transakcji pozostawiło konto z nieuregulowanym odniesieniem do pożyczki |
WouldExceedMaxBlockCostLimit | Planowanie | Transakcja przekroczyłaby maksymalny limit kosztów bloku |
UnsupportedVersion | Sanityzacja | Wersja transakcji nie jest obsługiwana |
InvalidWritableAccount | Ładowanie konta | Transakcja ładuje zapisywalne konto, które nie może być zapisane |
WouldExceedMaxAccountCostLimit | Planowanie | Transakcja przekroczyłaby maksymalny limit kosztów kont w bloku |
WouldExceedAccountDataBlockLimit | Planowanie | Transakcja przekroczyłaby limit danych kont w bloku |
TooManyAccountLocks | Planowanie | Transakcja zablokowała zbyt wiele kont |
AddressLookupTableNotFound | Ładowanie konta | Konto tabeli lookup adresów nie istnieje |
InvalidAddressLookupTableOwner | Ładowanie konta | Tabela lookup adresów jest własnością niewłaściwego programu |
InvalidAddressLookupTableData | Ładowanie konta | Tabela lookup adresów zawiera nieprawidłowe dane |
InvalidAddressLookupTableIndex | Ładowanie konta | Lookup tabeli adresów używa nieprawidłowego indeksu |
InvalidRentPayingAccount | Kontrola po wykonaniu | Konto przeszło ze stanu zwolnionego z rent do płacącego rent |
WouldExceedMaxVoteCostLimit | Planowanie | Transakcja przekroczyłaby maksymalny limit kosztów głosowania |
WouldExceedAccountDataTotalLimit | Planowanie | Transakcja przekroczyłaby całkowity limit danych kont |
DuplicateInstruction | Parsowanie budżetu obliczeń | Zduplikowana instrukcja wariantu budżetu obliczeń w tej samej transakcji |
InsufficientFundsForRent | Kontrola po wykonaniu | Konto nie ma wystarczającej liczby lamportów, aby pokryć rent za swój rozmiar danych |
MaxLoadedAccountsDataSizeExceeded | Ładowanie konta | Całkowita załadowana ilość danych przekracza limit 64 MiB |
InvalidLoadedAccountsDataSizeLimit | Parsowanie budżetu obliczeń | SetLoadedAccountsDataSizeLimit ustawiony na 0 |
ResanitizationNeeded | Sanityzacja | Transakcja różni się przed/po aktywacji funkcji i wymaga ponownej sanityzacji |
ProgramExecutionTemporarilyRestricted | Ładowanie konta | Wykonywanie programu jest tymczasowo ograniczone na wskazanym koncie |
UnbalancedTransaction | Kontrola po wykonaniu | Całkowity bilans lamportów przed transakcją nie jest równy bilansowi po |
ProgramCacheHitMaxLimit | Ładowanie konta | Cache programu osiągnął maksymalny limit |
| INLINE_CODE_PLACEHOLDER_500659bbedde5f5_END | Commit | Commit został anulowany wewnętrznie |
Is this page helpful?