Διαδικασία επεξεργασίας συναλλαγών

Περίληψη

Οι συναλλαγές περνούν από 8 στάδια: λήψη, επαλήθευση υπογραφής, εξυγίανση, έλεγχοι προϋπολογισμού/ηλικίας, επικύρωση πληρωτή προμήθειας, φόρτωση λογαριασμού, εκτέλεση εντολής και καταχώριση.

Διαδικασία επεξεργασίας συναλλαγών

Όταν μια συναλλαγή φτάνει σε έναν validator, περνά από μια σειρά σταδίων επικύρωσης και εκτέλεσης. Το παρακάτω περιγράφει την πλήρη διαδικασία από τη λήψη έως την καταχώριση, με αναφορές σε αρχεία πηγαίου κώδικα στον agave validator client.

1. Λήψη και αποσειριοποίηση

Ο validator λαμβάνει bytes συναλλαγής μέσω UDP/QUIC. Τα ακατέργαστα bytes πρέπει να χωρούν σε ένα μόνο πακέτο (PACKET_DATA_SIZE = 1.232 bytes). Τα bytes αποσειριοποιούνται σε μια VersionedTransaction, η οποία περιέχει τον πίνακα υπογραφών και ένα VersionedMessage (είτε legacy είτε v0).

2. Επαλήθευση υπογραφής (sigverify)

Οι υπογραφές επαληθεύονται στο στάδιο sigverify πριν η συναλλαγή εισέλθει στο στάδιο banking. Για κάθε υπογραφή στο ευρετήριο i, ο επαληθευτής ελέγχει Ed25519(signatures[i], account_keys[i], message_bytes). Αν κάποια υπογραφή είναι άκυρη, το πακέτο απορρίπτεται.

Η επαλήθευση είναι παραλληλοποιημένη: ο validator χωρίζει τις παρτίδες πακέτων σε τμήματα των VERIFY_PACKET_CHUNK_SIZE (128) και τα επεξεργάζεται παράλληλα.

3. Εξυγίανση

Η αποσειριοποιημένη συναλλαγή εξυγιαίνεται για να παραχθεί μια SanitizedTransactionRuntimeTransaction). Η εξυγίανση επικυρώνει δομικά αναλλοίωτα:

  • Ο αριθμός των υπογραφών ταιριάζει με το num_required_signatures στην κεφαλίδα
  • Όλα τα program_id_index και account_indices των εντολών βρίσκονται εντός ορίων
  • Ο πληρωτής προμήθειας (ευρετήριο λογαριασμού 0) είναι εγγράψιμος υπογράφων

Το περιτύλιγμα RuntimeTransaction αποθηκεύει προϋπολογισμένα μεταδεδομένα από το TransactionMeta: το hash του μηνύματος, τη σημαία συναλλαγής ψήφου, τους αριθμούς υπογραφών προμεταγλώττισης (Ed25519/secp256k1/secp256r1), τις λεπτομέρειες εντολής προϋπολογισμού υπολογισμού και το συνολικό μήκος δεδομένων εντολής.

4. Έλεγχος προϋπολογισμού υπολογισμού, ηλικίας και κρυφής μνήμης κατάστασης

Η μέθοδος check_transactions εκτελεί διάφορους ελέγχους ανά συναλλαγή:

Προϋπολογισμός υπολογισμού: Οι οδηγίες προϋπολογισμού υπολογισμού της συναλλαγής αναλύονται και επικυρώνονται πρώτα. Οι λεπτομέρειες χρέωσης υπολογίζονται από τα όρια προϋπολογισμού και τη χρέωση προτεραιότητας. Εάν ο προϋπολογισμός υπολογισμού είναι μη έγκυρος ή αντικρουόμενος, η συναλλαγή αποτυγχάνει με σφάλματα ανάλυσης προϋπολογισμού υπολογισμού όπως DuplicateInstruction, InstructionError(..., InvalidInstructionData) ή InvalidLoadedAccountsDataSizeLimit.

Ηλικία blockhash: Το recent_blockhash της συναλλαγής αναζητείται στο BlockhashQueue. Εάν το hash βρεθεί και η ηλικία του είναι εντός MAX_PROCESSING_AGE (150 slots), η συναλλαγή προχωρά. Εάν δεν βρεθεί, ο validator ελέγχει για έγκυρο durable nonce.

Κρυφή μνήμη κατάστασης: Το hash μηνύματος της συναλλαγής ελέγχεται έναντι μιας κρυφής μνήμης κατάστασης. Εάν βρεθεί, η συναλλαγή απορρίπτεται με AlreadyProcessed.

5. Επικύρωση nonce και πληρωτή χρέωσης

Η μέθοδος validate_transaction_nonce_and_fee_payer στο SVM χειρίζεται δύο επικυρώσεις:

Επικύρωση nonce (εάν ισχύει): Για συναλλαγές nonce, ο validator φορτώνει τον λογαριασμό nonce και επαληθεύει:

  • Ο λογαριασμός ανήκει στο System Program
  • Αναλύεται ως State::Initialized
  • Το αποθηκευμένο durable nonce ταιριάζει με το recent_blockhash της συναλλαγής
  • Το nonce μπορεί να προχωρήσει (το τρέχον durable nonce διαφέρει από το επόμενο durable nonce, δηλαδή το nonce δεν έχει ήδη χρησιμοποιηθεί στο τρέχον block)
  • Η εξουσιοδότηση nonce έχει υπογράψει τη συναλλαγή

Εάν είναι έγκυρο, το nonce προχωρά στην επόμενη τιμή durable nonce. Δείτε validate_transaction_nonce.

Επικύρωση πληρωτή χρέωσης: Ο λογαριασμός πληρωτή χρέωσης (πάντα δείκτης 0) φορτώνεται και ελέγχεται από τη validate_fee_payer:

  • Ο λογαριασμός πρέπει να υπάρχει (lamports > 0), διαφορετικά AccountNotFound
  • Ο λογαριασμός πρέπει να είναι system account ή nonce account, διαφορετικά InvalidAccountForFee
  • Τα lamports πρέπει να καλύπτουν το min_balance + total_fee, όπου το min_balance είναι 0 για system accounts ή rent.minimum_balance(NonceState::size()) για nonce accounts· διαφορετικά InsufficientFundsForFee
  • Μετά την αφαίρεση της χρέωσης, ο λογαριασμός πρέπει να παραμείνει απαλλαγμένος από ενοίκιο (δεν μπορεί να μεταβεί από απαλλαγμένος από ενοίκιο σε πληρωτής ενοικίου)

Η χρέωση αφαιρείται από τον πληρωτή χρέωσης σε αυτό το στάδιο. Ένα στιγμιότυπο του πληρωτή χρέωσης μετά την αφαίρεση της χρέωσης (και του προηγμένου nonce, εάν υπάρχει) αποθηκεύεται ως RollbackAccounts, που είναι οι λογαριασμοί που καταχωρούνται ακόμα και αν η εκτέλεση αποτύχει.

6. Φόρτωση λογαριασμών

Η load_transaction φορτώνει όλους τους λογαριασμούς στους οποίους αναφέρεται η συναλλαγή. Ο AccountLoader τυλίγει το εξωτερικό αποθετήριο λογαριασμών και διατηρεί μια τοπική cache για την ομάδα, ώστε οι λογαριασμοί που τροποποιήθηκαν από προηγούμενες συναλλαγές στην ίδια ομάδα να είναι ορατοί στις επόμενες.

Για κάθε λογαριασμό που δεν είναι πληρωτής χρέωσης, ο φορτωτής:

  1. Ανακτά τον λογαριασμό από την cache ή το accounts-db
  2. Ενημερώνει την κατάσταση απαλλαγής από ενοίκιο εάν χρειάζεται
  3. Συσσωρεύει το μέγεθος δεδομένων του λογαριασμού προς το loaded_accounts_data_size_limit (προεπιλογή 64 MiB). Κάθε λογαριασμός επιβαρύνεται με ένα βασικό overhead TRANSACTION_ACCOUNT_BASE_SIZE (64 bytes) συν το μήκος των δεδομένων του

Για κάθε πρόγραμμα που καλείται από τις οδηγίες της συναλλαγής, ο φορτωτής επαληθεύει ότι ο λογαριασμός του προγράμματος υπάρχει και ανήκει σε έγκυρο φορτωτή (NativeLoader ή έναν από τους PROGRAM_OWNERS). Μη έγκυρα προγράμματα αποτυγχάνουν με ProgramAccountNotFound ή InvalidProgramForExecution.

Τα προγράμματα LoaderV3 (αναβαθμίσιμα) φορτώνουν σιωπηρά τον συσχετισμένο λογαριασμό programdata, ο οποίος επίσης μετράει στο όριο μεγέθους φορτωμένων δεδομένων.

Εάν η φόρτωση λογαριασμού αποτύχει αλλά ο πληρωτής χρέωσης επικυρώθηκε επιτυχώς, η συναλλαγή γίνεται αποτέλεσμα FeesOnly: η χρέωση εξακολουθεί να εισπράττεται αλλά δεν εκτελούνται οδηγίες.

7. Εκτέλεση οδηγιών

Η execute_loaded_transaction δημιουργεί ένα TransactionContext με όλους τους φορτωμένους λογαριασμούς και καλεί την process_message. Οι οδηγίες εκτελούνται διαδοχικά με τη σειρά που εμφανίζονται στο μήνυμα. Κάθε κλήση οδηγίας δημιουργεί ένα InvokeContext και καλεί το πρόγραμμα-στόχο.

Λεπτομέρειες επεξεργασίας οδηγιών

Η συνάρτηση process_message του runtime επαναλαμβάνει κάθε οδηγία και καλεί το πρόγραμμα-στόχο:

  1. Για κάθε εντολή, το runtime καλεί το prepare_next_top_level_instruction, το οποίο δημιουργεί το InstructionContext. Αυτό το context περιέχει αναφορές στους λογαριασμούς της εντολής (επιλυμένους από τους μεταγλωττισμένους δείκτες), τα δεδομένα της εντολής και τον δείκτη του program account.
  2. Το runtime ελέγχει αν το πρόγραμμα είναι precompile (Ed25519, Secp256k1, Secp256r1). Τα precompiles επαληθεύονται απευθείας χωρίς να καλείται το BPF VM.
  3. Για όλα τα άλλα προγράμματα, το runtime καλεί το process_instruction, το οποίο φορτώνει το πρόγραμμα από την cache και το εκτελεί στο BPF virtual machine.
  4. Μετά την ολοκλήρωση της εντολής, το runtime επαληθεύει ότι το συνολικό υπόλοιπο lamport σε όλους τους λογαριασμούς εντολών δεν έχει αλλάξει (έλεγχος UnbalancedInstruction).
  5. Αν οποιαδήποτε εντολή αποτύχει, ολόκληρη η συναλλαγή ακυρώνεται. Καμία ενδιάμεση αλλαγή κατάστασης δεν καταχωρείται.

Κάθε εντολή αυξάνει το instruction trace. Το trace περιλαμβάνει τόσο εντολές ανώτατου επιπέδου όσο και οποιαδήποτε CPIs καλούν. Το συνολικό μήκος του trace (εντολές ανώτατου επιπέδου συν όλα τα ένθετα CPIs) δεν μπορεί να υπερβαίνει το 64 (MAX_INSTRUCTION_TRACE_LENGTH). Η υπέρβαση αυτού του ορίου επιστρέφει InstructionError::MaxInstructionTraceLengthExceeded.

Μετά την εκτέλεση, το runtime επαληθεύει ότι:

  • Το άθροισμα των lamports σε όλους τους λογαριασμούς δεν έχει αλλάξει
  • Κανένας λογαριασμός δεν μετέβη από rent-exempt σε rent-paying

8. Commit ή rollback

Αν η εκτέλεση πετύχει, οι τροποποιημένες καταστάσεις λογαριασμών από το TransactionContext γράφονται πίσω στην batch-local cache του AccountLoader. Αν η εκτέλεση αποτύχει, μόνο τα RollbackAccounts (fee payer με αφαιρεμένη χρέωση και προωθημένο nonce) γράφονται πίσω. Η χρέωση εξακολουθεί να εισπράττεται, αλλά όλες οι άλλες αλλαγές λογαριασμών απορρίπτονται.

Σύνοψη pipeline

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 και σε ποιο στάδιο του pipeline εμφανίζονται:

ΣφάλμαΣτάδιοΑιτία
AccountInUseΠρογραμματισμόςΟ λογαριασμός είναι ήδη κλειδωμένος από άλλη συναλλαγή στην ίδια ομάδα
AccountLoadedTwiceΠρογραμματισμόςΈνα pubkey εμφανίζεται δύο φορές στο account_keys της συναλλαγής
AccountNotFoundΕπικύρωση πληρωτή προμήθειαςΟ λογαριασμός πληρωτή προμήθειας δεν υπάρχει
ProgramAccountNotFoundΦόρτωση λογαριασμούΈνα πρόγραμμα που καλέστηκε δεν υπάρχει
InsufficientFundsForFeeΕπικύρωση πληρωτή προμήθειαςΟ πληρωτής προμήθειας δεν μπορεί να καλύψει την προμήθεια + το ελάχιστο rent-exempt
InvalidAccountForFeeΕπικύρωση πληρωτή προμήθειαςΟ πληρωτής προμήθειας δεν είναι λογαριασμός συστήματος ή nonce
AlreadyProcessedΠροσωρινή μνήμη κατάστασηςΗ συναλλαγή είχε ήδη επεξεργαστεί
BlockhashNotFoundΈλεγχος ηλικίαςΤο blockhash δεν βρίσκεται στην ουρά και δεν είναι έγκυρο nonce
InstructionErrorΕκτέλεσηΠροέκυψε σφάλμα κατά την επεξεργασία μιας εντολής (περιλαμβάνει δείκτη εντολής και συγκεκριμένο InstructionError)
CallChainTooDeepΦόρτωση λογαριασμούΗ αλυσίδα κλήσεων του φορτωτή είναι πολύ βαθιά
MissingSignatureForFeeΕξυγίανσηΗ συναλλαγή απαιτεί προμήθεια αλλά δεν έχει υπογραφή
InvalidAccountIndexΕξυγίανσηΗ συναλλαγή περιέχει μη έγκυρη αναφορά λογαριασμού
SignatureFailureΕπαλήθευση υπογραφήςΗ υπογραφή Ed25519 δεν επαληθεύεται (το πακέτο απορρίπτεται)
InvalidProgramForExecutionΦόρτωση λογαριασμούΤο πρόγραμμα δεν ανήκει σε έγκυρο φορτωτή
SanitizeFailureΕξυγίανσηΗ συναλλαγή απέτυχε να εξυγιάνει σωστά τις μετατοπίσεις λογαριασμών
ClusterMaintenanceΠρογραμματισμόςΟι συναλλαγές είναι προσωρινά απενεργοποιημένες λόγω συντήρησης του cluster
AccountBorrowOutstandingΕκτέλεσηΗ επεξεργασία της συναλλαγής άφησε έναν λογαριασμό με εκκρεμή δανεισμένη αναφορά
WouldExceedMaxBlockCostLimitΠρογραμματισμόςΗ συναλλαγή θα υπερέβαινε το μέγιστο όριο κόστους μπλοκ
UnsupportedVersionΕξυγίανσηΗ έκδοση της συναλλαγής δεν υποστηρίζεται
InvalidWritableAccountΦόρτωση λογαριασμούΗ συναλλαγή φορτώνει εγγράψιμο λογαριασμό που δεν μπορεί να γραφτεί
WouldExceedMaxAccountCostLimitΠρογραμματισμόςΗ συναλλαγή θα υπερέβαινε το μέγιστο όριο κόστους λογαριασμού εντός του μπλοκ
WouldExceedAccountDataBlockLimitΠρογραμματισμόςΗ συναλλαγή θα υπερέβαινε το όριο δεδομένων λογαριασμού εντός του μπλοκ
TooManyAccountLocksΠρογραμματισμόςΗ συναλλαγή κλείδωσε πάρα πολλούς λογαριασμούς
AddressLookupTableNotFoundΦόρτωση λογαριασμούΟ λογαριασμός πίνακα αναζήτησης διευθύνσεων δεν υπάρχει
InvalidAddressLookupTableOwnerΦόρτωση λογαριασμούΟ πίνακας αναζήτησης διευθύνσεων ανήκει στο λάθος πρόγραμμα
InvalidAddressLookupTableDataΦόρτωση λογαριασμούΟ πίνακας αναζήτησης διευθύνσεων περιέχει μη έγκυρα δεδομένα
InvalidAddressLookupTableIndexΦόρτωση λογαριασμούΗ αναζήτηση πίνακα διευθύνσεων χρησιμοποιεί μη έγκυρο δείκτη
InvalidRentPayingAccountΈλεγχος μετά την εκτέλεσηΟ λογαριασμός μετέβη από rent-exempt σε rent-paying
WouldExceedMaxVoteCostLimitΠρογραμματισμόςΗ συναλλαγή θα υπερέβαινε το μέγιστο όριο κόστους ψήφου
WouldExceedAccountDataTotalLimitΠρογραμματισμόςΗ συναλλαγή θα υπερέβαινε το συνολικό όριο δεδομένων λογαριασμού
DuplicateInstructionΑνάλυση προϋπολογισμού υπολογισμούΔιπλότυπη παραλλαγή εντολής προϋπολογισμού υπολογισμού στην ίδια συναλλαγή
InsufficientFundsForRentΈλεγχος μετά την εκτέλεσηΟ λογαριασμός δεν έχει αρκετά lamports για να καλύψει το rent για το μέγεθος δεδομένων του
MaxLoadedAccountsDataSizeExceededΦόρτωση λογαριασμούΤα συνολικά φορτωμένα δεδομένα υπερβαίνουν το όριο των 64 MiB
InvalidLoadedAccountsDataSizeLimitΑνάλυση προϋπολογισμού υπολογισμούΤο SetLoadedAccountsDataSizeLimit ορίστηκε σε 0
ResanitizationNeededΕξυγίανσηΗ συναλλαγή διέφερε πριν/μετά την ενεργοποίηση χαρακτηριστικού και χρειάζεται επανεξυγίανση
ProgramExecutionTemporarilyRestrictedΦόρτωση λογαριασμούΗ εκτέλεση προγράμματος είναι προσωρινά περιορισμένη στον αναφερόμενο λογαριασμό
UnbalancedTransactionΈλεγχος μετά την εκτέλεσηΤο συνολικό υπόλοιπο lamport πριν τη συναλλαγή δεν ισούται με το υπόλοιπο μετά
ProgramCacheHitMaxLimitΦόρτωση λογαριασμούΗ προσωρινή μνήμη προγράμματος έφτασε το μέγιστο όριο
CommitCancelledCommitΤο commit ακυρώθηκε εσωτερικά

Is this page helpful?

Διαχειρίζεται από

© 2026 Ίδρυμα Solana.
Με επιφύλαξη παντός δικαιώματος.
Συνδεθείτε