Σύνοψη
Τα προγράμματα μεταγλωττίζονται σε sBPF μέσω LLVM και εκτελούνται σε μια απομονωμένη VM με προϋπολογισμό 1,4M CU ανά συναλλαγή. Το runtime αποθηκεύει στην κρυφή μνήμη έως 512 μεταγλωττισμένα προγράμματα, παρέχει syscalls για καταγραφή, CPI, κρυπτογραφία και μνήμη, και καθυστερεί νέες αναπτύξεις κατά 1 slot.
Μεταγλώττιση
Η Solana χρησιμοποιεί LLVM για να μεταγλωττίζει προγράμματα σε ELF δυαδικά αρχεία που περιέχουν το Solana Bytecode Format (sBPF). Το δυαδικό αρχείο ELF αποθηκεύεται onchain σε ένα εκτελέσιμο λογαριασμό.
Το sBPF είναι η προσαρμοσμένη παραλλαγή της Solana του eBPF bytecode, προσαρμοσμένη για το Solana runtime. Δεν είναι τυπικό eBPF και διαθέτει τροποποιήσεις ειδικές για τη Solana.
Συγγραφή προγραμμάτων
Τα προγράμματα Solana γράφονται κυρίως σε Rust χρησιμοποιώντας μία από τρεις προσεγγίσεις:
Anchor
Ένα framework που χρησιμοποιεί Rust macros για να μειώσει τον επαναλαμβανόμενο κώδικα. Συνιστάται για τους περισσότερους προγραμματιστές.
Pinocchio
Μια ελαφριά βιβλιοθήκη Rust χωρίς αντιγραφή, βελτιστοποιημένη για κατανάλωση υπολογισμού και μέγεθος δυαδικού αρχείου. Περιλαμβάνει crates ειδικά για προγράμματα για κοινά CPIs.
Native Rust
Απευθείας Rust χωρίς frameworks. Προσφέρει πλήρη έλεγχο αλλά απαιτεί περισσότερη χειροκίνητη υλοποίηση.
Δείτε Cross-Program Invocations για έννοιες και παραδείγματα CPI που ισχύουν σε αυτές τις προσεγγίσεις.
Μοντέλο εκτέλεσης προγράμματος
Όταν επεξεργάζεται μια συναλλαγή, το runtime εκτελεί κάθε εντολή
διαδοχικά μέσω
process_message().
Για κάθε εντολή, το runtime:
-
Προετοιμάζει το πλαίσιο εντολής. Καλεί
prepare_next_top_level_instruction()για να αντιστοιχίσει τους δείκτες λογαριασμού της εντολής, να ορίσει τις σημαίες υπογράφοντος και εγγράψιμου, και να διαμορφώσει τοTransactionContext. -
Ελέγχει precompiles. Εάν το πρόγραμμα είναι ένα precompile, το runtime καλεί
process_precompile(), το οποίο εξακολουθεί να ωθεί και να αναδύει ένα stack frame (μέσωpush()καιpop()) αλλά παρακάμπτει την sBPF VM και την αναζήτηση κρυφής μνήμης προγράμματος, εκτελώντας native κώδικα απευθείας. -
Ωθεί ένα stack frame. (Τα βήματα 3-6 συμβαίνουν μέσα στο
InvokeContext::process_instruction()καιprocess_executable_chain(), που καλούνται απόprocess_message().) Καλείpush()στοInvokeContext, το οποίο αυξάνει το ύψος στοίβας εντολών και επιβάλλει τον κανόνα επανεισόδου: ένα πρόγραμμα μπορεί να επανεισέλθει στον εαυτό του μόνο εάν ο άμεσος καλών (το πρόγραμμα στην κορυφή της τρέχουσας στοίβας εντολών) είναι το ίδιο πρόγραμμα. Η βαθιά αυτο-αναδρομή (A -> A -> A) επιτρέπεται, υπό τα όρια βάθους στοίβας. Άλλα μοτίβα επανεισόδου (π.χ., το A καλεί το B που καλεί το A) επιστρέφουνInstructionError::ReentrancyNotAllowed. -
Επιλύει το πρόγραμμα. Το runtime καλεί
process_executable_chain()το οποίο προσδιορίζει τον φορτωτή. Εάν ο κάτοχος του program account είναι ο native loader, το πρόγραμμα είναι builtin και η συνάρτηση entrypoint του αναζητείται απευθείας από τοProgramCacheForTxBatch. Εάν ο κάτοχος είναι ένας από τους BPF loaders (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeable, ήloader_v4), το δικό του builtin entrypoint του φορτωτή καλείται αντ' αυτού. -
Εκτελεί το BPF πρόγραμμα. Για BPF προγράμματα, το loader entrypoint αναζητά το μεταγλωττισμένο εκτελέσιμο από την κρυφή μνήμη προγράμματος. Η συνάρτηση
execute()στη συνέχεια:- Σειριοποιεί δεδομένα λογαριασμού σε ένα επίπεδο buffer παραμέτρων
- Δημιουργεί την sBPF VM με στοίβα, heap και περιοχές μνήμης
- Εκτελεί τον μεταγλωττισμένο κώδικα, καταναλώνοντας μονάδες υπολογισμού κατά την εκτέλεση. Επιστρέφει
ComputationalBudgetExceededεάν ο προϋπολογισμός υπερβαίνεται. - Αποσειριοποιεί δεδομένα λογαριασμού από το buffer πίσω στην κατάσταση λογαριασμού
-
Αναδύει το stack frame. Καλεί
pop()το οποίο επαληθεύει ότι η εντολή δεν παραβίασε τους κανόνες λογιστικής του runtime (τα υπόλοιπα lamport είναι ισορροπημένα, οι λογαριασμοί μόνο ανάγνωσης δεν τροποποιήθηκαν, τα μεγέθη δεδομένων λογαριασμού είναι εντός ορίων). -
Συσσωρεύει μονάδες υπολογισμού. Οι μονάδες υπολογισμού που καταναλώθηκαν από την εντολή προστίθενται στο σύνολο της συναλλαγής μέσω
saturating_add.
Κρυφή μνήμη προγράμματος
Το runtime διατηρεί μια παγκόσμια
ProgramCache
που αποθηκεύει επαληθευμένα και μεταγλωττισμένα προγράμματα. Είναι ενήμερη για το fork-graph και χειρίζεται
κανόνες ορατότητας ανάπτυξης, εκτόπιση και επαναμεταγλώττιση ορίων epoch.
Τύποι καταχώρισης κρυφής μνήμης
Κάθε αποθηκευμένο πρόγραμμα στην κρυφή μνήμη έχει έναν
ProgramCacheEntryType
που καθορίζει τη συμπεριφορά του κατά την εκτέλεση:
| Τύπος | Περιγραφή |
|---|---|
Loaded | Επαληθευμένο και μεταγλωττισμένο πρόγραμμα, έτοιμο για εκτέλεση. |
Builtin | Native πρόγραμμα μεταγλωττισμένο στο δυαδικό αρχείο validator (System, Stake, Vote, κ.λπ.). Δεν αποθηκεύεται onchain. |
Unloaded | Προηγουμένως επαληθευμένο πρόγραμμα του οποίου το μεταγλωττισμένο εκτελέσιμο εκτοπίστηκε από τη μνήμη για να ελευθερωθεί χώρος. Εξακολουθεί να παρακολουθεί στατιστικά χρήσης. Μπορεί να επαναφορτωθεί χωρίς επαναλήπτη επαλήθευση. |
FailedVerification | Tombstone για προγράμματα που δεν πέρασαν τον sBPF verifier στο τρέχον σύνολο χαρακτηριστικών. Μπορεί να γίνει Loaded εάν οι ενεργοποιήσεις χαρακτηριστικών αλλάξουν τους κανόνες επαλήθευσης. |
Closed | Tombstone για προγράμματα που έκλεισαν ρητά ή δεν αναπτύχθηκαν ποτέ. Χρησιμοποιείται επίσης για λογαριασμούς (όπως buffer accounts) που ανήκουν σε φορτωτή αλλά δεν περιέχουν εκτελέσιμο κώδικα. |
DelayVisibility | Συνθετικό tombstone που επιστρέφεται από ProgramCacheForTxBatch::find() όταν υπάρχει μια καταχώριση Loaded αλλά δεν είναι ακόμη αποτελεσματική (το effective_slot της βρίσκεται στο μέλλον). Δεν αποθηκεύεται ποτέ απευθείας στην κρυφή μνήμη. |
Καθυστέρηση ορατότητας
Τα νεοαναπτυχθέντα ή αναβαθμισμένα προγράμματα δεν τίθενται σε ισχύ αμέσως. Η σταθερά
DELAY_VISIBILITY_SLOT_OFFSET
έχει τιμή 1, που σημαίνει ότι ένα πρόγραμμα που αναπτύχθηκε στο slot N τίθεται σε ισχύ στο slot
N+1. Κατά τη διάρκεια του slot ανάπτυξης, κάθε προσπάθεια επίκλησης της νέας έκδοσης επιστρέφει
DelayVisibility, προκαλώντας το runtime να αναφέρει "Το πρόγραμμα δεν έχει αναπτυχθεί."
Πολιτική εκτόπισης
Η κρυφή μνήμη χωράει έως
MAX_LOADED_ENTRY_COUNT
(512) μεταγλωττισμένες καταχωρίσεις προγραμμάτων. Όταν φτάσει το όριο, τα λιγότερο χρησιμοποιούμενα
προγράμματα εκτοπίζονται στην κατάσταση Unloaded. Η χρήση παρακολουθείται από
tx_usage_counter
(αυξάνεται κάθε φορά που μια συναλλαγή αναφέρεται στο πρόγραμμα) και
latest_access_slot.
Επαναμεταγλώττιση ορίου epoch
Εάν μια ενεργοποίηση χαρακτηριστικού αλλάξει τα
ProgramRuntimeEnvironments
σε ένα όριο epoch, όλα τα αποθηκευμένα προγράμματα
επαναμεταγλωττίζονται
έναντι του νέου περιβάλλοντος.
Δεδομένα επιστροφής
Τα προγράμματα μπορούν να ορίσουν δεδομένα επιστροφής μέσω του syscall sol_set_return_data. Τα δεδομένα αποθηκεύονται σε μια δομή
TransactionReturnData
σε επίπεδο συναλλαγής που περιέχει τα bytes δεδομένων και το program_id του προγράμματος του οποίου η
εντολή κάλεσε το syscall. Το μέγιστο μέγεθος είναι 1.024 bytes
(MAX_RETURN_DATA).
Is this page helpful?