Podsumowanie
Programy są kompilowane do sBPF przez LLVM i uruchamiane w odizolowanej maszynie wirtualnej z budżetem 1,4M CU na transakcję. Runtime przechowuje w cache do 512 skompilowanych programów, udostępnia wywołania systemowe do logowania, CPI, kryptografii i pamięci oraz opóźnia nowe wdrożenia o 1 slot.
Kompilacja
Solana używa LLVM do kompilowania programów do binarek ELF zawierających Solana Bytecode Format (sBPF). Binarka ELF jest przechowywana on-chain w koncie wykonawczym.
sBPF to autorska wersja eBPF dostosowana do środowiska Solana. Nie jest to standardowy eBPF i zawiera modyfikacje specyficzne dla Solany.
Pisanie programów
Programy Solana są najczęściej pisane w języku Rust na dwa sposoby:
Anchor
Framework wykorzystujący makra Rust do ograniczenia boilerplate'u. Zalecany dla większości deweloperów.
Native Rust
Czysty Rust bez frameworków. Daje pełną kontrolę, ale wymaga więcej ręcznej implementacji.
Model wykonywania programów
Podczas przetwarzania transakcji runtime wykonuje każdą instrukcję sekwencyjnie
przez
process_message().
Dla każdej instrukcji runtime:
-
Przygotowuje kontekst instrukcji. Wywołuje
prepare_next_top_level_instruction(), aby zmapować indeksy kont instrukcji, ustawić flagi podpisującego i zapisywalności oraz skonfigurowaćTransactionContext. -
Sprawdza prekompilacje. Jeśli program jest prekompilacją, runtime wywołuje
process_precompile(), co nadal powoduje dodanie i usunięcie ramki stosu (przezpush()ipop()), ale pomija maszynę wirtualną sBPF i wyszukiwanie w cache programu, wykonując natywny kod bezpośrednio. -
Dodaje ramkę stosu. (Kroki 3–6 odbywają się wewnątrz
InvokeContext::process_instruction()orazprocess_executable_chain(), wywoływanych zprocess_message().) Wywołujepush()naInvokeContext, co zwiększa wysokość stosu instrukcji i wymusza zasadę reentrancji: program może ponownie wejść w siebie tylko wtedy, gdy bezpośredni wywołujący (program na szczycie stosu instrukcji) jest tym samym programem. Głęboka rekurencja własna (A -> A -> A) jest dozwolona, pod warunkiem zachowania limitów głębokości stosu. Inne wzorce reentrancji (np. A wywołuje B, które wywołuje A) zwracająInstructionError::ReentrancyNotAllowed. -
Rozwiązuje program. Środowisko uruchomieniowe wywołuje
process_executable_chain(), który określa loader. Jeśli właścicielem program account jest native loader, program jest wbudowany i jego punkt wejścia jest wyszukiwany bezpośrednio zProgramCacheForTxBatch. Jeśli właścicielem jest jeden z loaderów BPF (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeablelubloader_v4), wywoływany jest wbudowany punkt wejścia loadera. -
Wykonuje program BPF. Dla programów BPF loader entrypoint wyszukuje skompilowany plik wykonywalny z cache programu. Następnie funkcja
execute():- Serializuje dane kont do płaskiego bufora parametrów
- Tworzy maszynę wirtualną sBPF ze stosem, stertą i regionami pamięci
- Uruchamia skompilowany kod, zużywając jednostki obliczeniowe podczas
wykonania. Zwraca
ComputationalBudgetExceeded, jeśli przekroczono budżet. - Deserializuje dane kont z bufora z powrotem do stanu konta
-
Zdejmuje ramkę stosu. Wywołuje
pop(), który weryfikuje, czy instrukcja nie naruszyła zasad rozliczania środowiska uruchomieniowego (salda lamport są zbilansowane, konta tylko do odczytu nie zostały zmodyfikowane, rozmiary danych kont mieszczą się w limitach). -
Sumuje jednostki obliczeniowe. Jednostki obliczeniowe zużyte przez instrukcję są dodawane do sumy transakcji przez
saturating_add.
Program cache
Środowisko uruchomieniowe utrzymuje globalny
ProgramCache,
który przechowuje zweryfikowane i skompilowane programy. Jest świadomy grafu
forków i obsługuje zasady widoczności wdrożeń, usuwanie z pamięci oraz
rekompilację na granicy epoch.
Typy wpisów w cache'u
Każdy program w cache'u posiada
ProgramCacheEntryType,
który określa jego zachowanie w środowisku uruchomieniowym:
| Typ | Opis |
|---|---|
Loaded | Zweryfikowany i skompilowany program, gotowy do wykonania. |
Builtin | Program natywny skompilowany do binarki validatora (System, Stake, Vote itd.). Nie jest przechowywany on-chain. |
Unloaded | Wcześniej zweryfikowany program, którego skompilowany plik wykonywalny został usunięty z pamięci w celu zwolnienia miejsca. Nadal śledzi statystyki użycia. Może zostać ponownie załadowany bez ponownej weryfikacji. |
FailedVerification | Nagrobek dla programów, które nie przeszły weryfikacji sBPF w ramach aktualnego zestawu funkcji. Może stać się Loaded, jeśli aktywacje funkcji zmienią zasady weryfikacji. |
Closed | Nagrobek dla programów, które zostały jawnie zamknięte lub nigdy nie zostały wdrożone. Używany także dla kont (np. kont buforowych) należących do loadera, ale nie zawierających kodu wykonywalnego. |
DelayVisibility | Syntetyczny nagrobek zwracany przez ProgramCacheForTxBatch::find(), gdy istnieje wpis Loaded, ale nie jest jeszcze aktywny (jego effective_slot jest w przyszłości). Nigdy nie jest przechowywany bezpośrednio w cache'u. |
Opóźnienie widoczności
Nowo wdrożone lub zaktualizowane programy nie stają się aktywne natychmiast.
Stała
DELAY_VISIBILITY_SLOT_OFFSET
ma wartość 1, co oznacza, że program wdrożony w slocie N staje się aktywny w
slocie N+1. Podczas slotu wdrożenia każda próba wywołania nowej wersji zwraca
DelayVisibility, powodując, że środowisko uruchomieniowe zgłasza "Program
nie jest wdrożony."
Polityka usuwania (eviction policy)
Cache przechowuje maksymalnie
MAX_LOADED_ENTRY_COUNT
(512) skompilowanych wpisów programów. Po osiągnięciu limitu, najmniej używane
programy są usuwane do stanu Unloaded. Użycie jest śledzone przez
tx_usage_counter
(zwiększane za każdym razem, gdy transakcja odwołuje się do programu) oraz
latest_access_slot.
Rekompilacja na granicy epoch
Jeśli aktywacja funkcji zmienia
ProgramRuntimeEnvironments
na granicy epoch, wszystkie programy w cache są
rekompilowane
w nowym środowisku.
Dane zwrotne
Programy mogą ustawiać dane zwrotne za pomocą syscalla sol_set_return_data.
Dane są przechowywane w strukturze na poziomie transakcji
TransactionReturnData,
która zawiera bajty danych oraz program_id programu, którego instrukcja
wywołała syscall. Maksymalny rozmiar to 1 024 bajty (MAX_RETURN_DATA).
Is this page helpful?