Transactiepijplijn

Samenvatting

Transacties doorlopen 8 fasen: ontvangen, sigverify, sanitize, budget/leeftijdscontroles, validatie van de fee payer, laden van accounts, uitvoering van instructies en commit.

Transactieverwerkingspijplijn

Wanneer een transactie bij een validator aankomt, doorloopt deze een reeks validatie- en uitvoeringsfasen. Het volgende beschrijft de volledige pijplijn van ontvangst tot commit, met verwijzingen naar bronbestanden in de agave validator-client.

1. Ontvangen en deserialiseren

De validator ontvangt transactiebytes via UDP/QUIC. De ruwe bytes moeten binnen één enkel pakket passen (PACKET_DATA_SIZE = 1.232 bytes). De bytes worden gedeserialiseerd naar een VersionedTransaction, die de handtekeningenarray en een VersionedMessage (legacy of v0) bevat.

2. Handtekeningverificatie (sigverify)

Handtekeningen worden geverifieerd in de sigverify-fase voordat de transactie de banking-fase ingaat. Voor elke handtekening op index i controleert de verifier Ed25519(signatures[i], account_keys[i], message_bytes). Als een handtekening ongeldig is, wordt het pakket verwijderd.

Verificatie wordt geparallelliseerd: de validator splitst pakketbatches in chunks van VERIFY_PACKET_CHUNK_SIZE (128) en verwerkt deze parallel.

3. Sanitize

De gedeserialiseerde transactie wordt gesanitized om een SanitizedTransaction (of RuntimeTransaction) te produceren. Sanitization valideert structurele invarianten:

  • Aantal handtekeningen komt overeen met num_required_signatures in de header
  • Alle instructie program_id_index en account_indices vallen binnen de grenzen
  • De fee payer (accountindex 0) is een schrijfbare ondertekenaar

De RuntimeTransaction wrapper cachet vooraf berekende metadata van TransactionMeta: de berichthash, vote-transactievlag, precompile-handtekeningaantallen (Ed25519/secp256k1/secp256r1), compute budget-instructiedetails en totale instructiedatalengte.

4. Controleer compute budget, leeftijd en statuscache

De check_transactions methode voert verschillende controles per transactie uit:

Compute budget: De compute budget-instructies van de transactie worden eerst geparseerd en gevalideerd. Kostendetails worden berekend op basis van de budgetlimieten en prioriteringskosten. Als het compute budget ongeldig of tegenstrijdig is, mislukt de transactie met compute-budget parsefouten zoals DuplicateInstruction, InstructionError(..., InvalidInstructionData), of InvalidLoadedAccountsDataSizeLimit.

Blockhash-leeftijd: De recent_blockhash van de transactie wordt opgezocht in de BlockhashQueue. Als de hash wordt gevonden en de leeftijd binnen MAX_PROCESSING_AGE (150 slots) valt, gaat de transactie door. Indien niet gevonden, controleert de validator op een geldige durable nonce.

Statuscache: De message hash van de transactie wordt gecontroleerd tegen een statuscache. Indien gevonden, wordt de transactie afgewezen met AlreadyProcessed.

5. Valideer nonce en fee payer

De validate_transaction_nonce_and_fee_payer methode in de SVM behandelt twee validaties:

Nonce-validatie (indien van toepassing): Voor nonce-transacties laadt de validator het nonce-account en verifieert:

  • Het account is eigendom van het System Program
  • Het parseert als State::Initialized
  • De opgeslagen durable nonce komt overeen met de recent_blockhash van de transactie
  • De nonce kan worden verhoogd (de huidige durable nonce verschilt van de volgende durable nonce, d.w.z. de nonce is nog niet gebruikt in het huidige blok)
  • De nonce-autoriteit heeft de transactie ondertekend

Indien geldig, wordt de nonce verhoogd naar de volgende durable nonce-waarde. Zie validate_transaction_nonce.

Fee payer-validatie: Het fee payer-account (altijd index 0) wordt geladen en gecontroleerd door validate_fee_payer:

  • Account moet bestaan (lamports > 0), anders AccountNotFound
  • Account moet een systeemaccount of nonce-account zijn, anders InvalidAccountForFee
  • Lamports moeten min_balance + total_fee dekken, waarbij min_balance 0 is voor systeemaccounts of rent.minimum_balance(NonceState::size()) voor nonce- accounts; anders InsufficientFundsForFee
  • Na aftrek van de kosten moet het account rent-exempt blijven (kan niet overgaan van rent-exempt naar rent-paying)

De vergoeding wordt in deze fase afgetrokken van de vergoedingsbetaler. Een momentopname van de vergoedingsbetaler na aftrek van de vergoeding (en geavanceerde nonce, indien van toepassing) wordt opgeslagen als RollbackAccounts, dit zijn de accounts die worden vastgelegd zelfs als de uitvoering mislukt.

6. Accounts laden

load_transaction laadt alle accounts waarnaar de transactie verwijst. De AccountLoader verpakt de externe accountopslag en onderhoudt een batch-lokale cache zodat accounts die zijn gewijzigd door eerdere transacties in dezelfde batch zichtbaar zijn voor latere transacties.

Voor elk account dat geen vergoedingsbetaler is, doet de loader het volgende:

  1. Haalt het account op uit de cache of accounts-db
  2. Werkt de huurvrijstellingsstatus bij indien nodig
  3. Accumuleert de gegevensgrootte van het account naar de loaded_accounts_data_size_limit (standaard 64 MiB). Elk account brengt een basis-overhead met zich mee van TRANSACTION_ACCOUNT_BASE_SIZE (64 bytes) plus de lengte van de gegevens

Voor elk programma dat wordt aangeroepen door de instructies van de transactie, verifieert de loader dat het program account bestaat en eigendom is van een geldige loader (NativeLoader of een van de PROGRAM_OWNERS). Ongeldige programma's mislukken met ProgramAccountNotFound of InvalidProgramForExecution.

LoaderV3 (upgradeable) programma's laden impliciet hun bijbehorende programdata account, wat ook meetelt voor de limiet van geladen gegevensgrootte.

Als het laden van accounts mislukt maar de vergoedingsbetaler succesvol is gevalideerd, wordt de transactie een FeesOnly resultaat: de vergoeding wordt nog steeds geïnd maar er worden geen instructies uitgevoerd.

7. Instructies uitvoeren

execute_loaded_transaction creëert een TransactionContext met alle geladen accounts en roept process_message aan. Instructies worden sequentieel uitgevoerd in de volgorde waarin ze in het bericht verschijnen. Elke instructie-aanroep creëert een InvokeContext en roept het doelprogramma aan.

Details van instructieverwerking

De process_message functie van de runtime itereert door elke instructie en roept het doelprogramma aan:

  1. Voor elke instructie roept de runtime prepare_next_top_level_instruction aan, die de InstructionContext opbouwt. Deze context bevat verwijzingen naar de accounts van de instructie (opgelost uit de gecompileerde indices), de instruction data en de program account index.
  2. De runtime controleert of het programma een precompile is (Ed25519, Secp256k1, Secp256r1). Precompiles worden direct geverifieerd zonder de BPF VM aan te roepen.
  3. Voor alle andere programma's roept de runtime process_instruction aan, die het programma uit de cache laadt en uitvoert in de BPF virtual machine.
  4. Nadat de instructie is voltooid, verifieert de runtime dat het totale lamport-saldo over alle instruction accounts niet is veranderd (UnbalancedInstruction-controle).
  5. Als een instructie mislukt, wordt de gehele transactie teruggedraaid. Geen tussentijdse statuswijzigingen worden vastgelegd.

Elke instructie verhoogt de instruction trace. De trace bevat zowel top-level instructies als alle CPI's die ze aanroepen. De totale trace-lengte (top-level instructies plus alle geneste CPI's) mag niet groter zijn dan 64 (MAX_INSTRUCTION_TRACE_LENGTH). Het overschrijden van deze limiet retourneert InstructionError::MaxInstructionTraceLengthExceeded.

Na uitvoering verifieert de runtime dat:

  • De som van lamports over alle accounts niet is veranderd
  • Geen enkel account is overgegaan van rent-exempt naar rent-paying

8. Vastleggen of terugdraaien

Als de uitvoering slaagt, worden de gewijzigde accountstatussen uit de TransactionContext teruggeschreven naar de batch-lokale cache van de AccountLoader. Als de uitvoering mislukt, worden alleen de RollbackAccounts (fee payer met ingehouden fee en gevorderde nonce) teruggeschreven. De fee wordt nog steeds geïnd, maar alle andere accountwijzigingen worden verworpen.

Pijplijn-overzicht

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)

Transactiefout-referentie

De volgende tabel toont alle TransactionError-varianten en in welke pijplijnfase ze optreden:

FoutFaseOorzaak
AccountInUseSchedulingAccount is al vergrendeld door een andere transactie in dezelfde batch
AccountLoadedTwiceSchedulingEen pubkey verschijnt twee keer in de account_keys van de transactie
AccountNotFoundFee payer validatieFee payer account bestaat niet
ProgramAccountNotFoundAccount ladenEen aangeroepen programma bestaat niet
InsufficientFundsForFeeFee payer validatieFee payer kan fee + rent-exempt minimum niet dekken
InvalidAccountForFeeFee payer validatieFee payer is geen system of nonce account
AlreadyProcessedStatus cacheTransactie is al verwerkt
BlockhashNotFoundLeeftijdscontroleBlockhash niet in wachtrij en geen geldige nonce
InstructionErrorUitvoeringEr is een fout opgetreden tijdens het verwerken van een instructie (bevat instructie-index en specifieke InstructionError)
CallChainTooDeepAccount ladenLoader call chain is te diep
MissingSignatureForFeeSanitizeTransactie vereist een fee maar heeft geen handtekening
InvalidAccountIndexSanitizeTransactie bevat een ongeldige accountreferentie
SignatureFailureSigverifyEd25519-handtekening kan niet worden geverifieerd (pakket wordt verwijderd)
InvalidProgramForExecutionAccount ladenProgramma is niet eigendom van een geldige loader
SanitizeFailureSanitizeTransactie kon accounts offsets niet correct sanitizen
ClusterMaintenanceSchedulingTransacties zijn momenteel uitgeschakeld vanwege clusteronderhoud
AccountBorrowOutstandingUitvoeringTransactieverwerking heeft een account achtergelaten met een openstaande geleende referentie
WouldExceedMaxBlockCostLimitSchedulingTransactie zou de maximale blokkostenlimiet overschrijden
UnsupportedVersionSanitizeTransactieversie wordt niet ondersteund
InvalidWritableAccountAccount ladenTransactie laadt een schrijfbaar account dat niet kan worden geschreven
WouldExceedMaxAccountCostLimitSchedulingTransactie zou de maximale accountkostenlimiet binnen het blok overschrijden
WouldExceedAccountDataBlockLimitSchedulingTransactie zou de accountdatalimiet binnen het blok overschrijden
TooManyAccountLocksSchedulingTransactie heeft te veel accounts vergrendeld
AddressLookupTableNotFoundAccount ladenAddress lookup table account bestaat niet
InvalidAddressLookupTableOwnerAccount ladenAddress lookup table is eigendom van het verkeerde programma
InvalidAddressLookupTableDataAccount ladenAddress lookup table bevat ongeldige data
InvalidAddressLookupTableIndexAccount ladenAddress table lookup gebruikt een ongeldige index
InvalidRentPayingAccountPost-execution checkAccount is overgegaan van rent-exempt naar rent-paying
WouldExceedMaxVoteCostLimitSchedulingTransactie zou de maximale vote kostenlimiet overschrijden
WouldExceedAccountDataTotalLimitSchedulingTransactie zou de totale accountdatalimiet overschrijden
DuplicateInstructionCompute budget parsingDubbele compute budget instructievariant in dezelfde transactie
InsufficientFundsForRentPost-execution checkAccount heeft niet genoeg lamports om rent voor zijn datagrootte te dekken
MaxLoadedAccountsDataSizeExceededAccount ladenTotaal geladen data overschrijdt 64 MiB limiet
InvalidLoadedAccountsDataSizeLimitCompute budget parsingSetLoadedAccountsDataSizeLimit ingesteld op 0
ResanitizationNeededSanitizeTransactie verschilde voor/na feature-activatie en vereist hersanitisatie
ProgramExecutionTemporarilyRestrictedAccount ladenProgramma-uitvoering is tijdelijk beperkt op het gerefereerde account
UnbalancedTransactionPost-execution checkTotaal lamport-saldo voor de transactie is niet gelijk aan het saldo erna
ProgramCacheHitMaxLimitAccount ladenProgramma-cache heeft maximale limiet bereikt
CommitCancelledCommitCommit intern geannuleerd

Is this page helpful?

Beheerd door

© 2026 Solana Foundation.
Alle rechten voorbehouden.
Blijf Verbonden