Підсумок
Програми компілюються в sBPF через LLVM і виконуються в ізольованій VM з бюджетом 1.4M CU на транзакцію. Runtime кешує до 512 скомпільованих програм, надає системні виклики для логування, CPI, криптографії та пам'яті, і затримує нові розгортання на 1 slot.
Компіляція
Solana використовує LLVM для компіляції програм у ELF бінарні файли, що містять Solana Bytecode Format (sBPF). ELF бінарний файл зберігається on-chain в виконуваному акаунті.
sBPF — це власний варіант eBPF байткоду від Solana, адаптований для Solana runtime. Це не стандартний eBPF і має специфічні для Solana модифікації.
Написання програм
Програми Solana переважно пишуться на Rust з використанням одного з двох підходів:
Anchor
Фреймворк, що використовує макроси Rust для зменшення шаблонного коду. Рекомендується для більшості розробників.
Native Rust
Прямий Rust без фреймворків. Надає повний контроль, але вимагає більше ручної реалізації.
Модель виконання програм
Коли транзакція обробляється, runtime виконує кожну інструкцію послідовно через
process_message().
Для кожної інструкції runtime:
-
Підготовлює контекст інструкції. Викликає
prepare_next_top_level_instruction()для відображення індексів акаунтів інструкції, встановлення прапорців підписувача та запису, і налаштуванняTransactionContext. -
Перевіряє прекомпіляції. Якщо програма є прекомпіляцією, runtime викликає
process_precompile(), який все одно додає та видаляє фрейм стека (черезpush()таpop()), але обходить sBPF VM та пошук у кеші програм, виконуючи нативний код безпосередньо. -
Додає кадр стека. (Кроки 3-6 виконуються всередині
InvokeContext::process_instruction()таprocess_executable_chain(), викликаних зprocess_message().) Викликаєpush()наInvokeContext, що збільшує висоту стека інструкцій і застосовує правило реентерабельності: програма може повторно викликати саму себе, лише якщо безпосередній викликач (програма на поточній вершині стека інструкцій) є тією самою програмою. Глибока саморекурсія (A -> A -> A) дозволена з урахуванням обмежень глибини стека. Інші шаблони реентерабельності (наприклад, A викликає B, яка викликає A) повертаютьInstructionError::ReentrancyNotAllowed. -
Визначає програму. Середовище виконання викликає
process_executable_chain(), що визначає завантажувач. Якщо власником program account є нативний завантажувач, програма є вбудованою, і її функція точки входу шукається безпосередньо уProgramCacheForTxBatch. Якщо власником є один із завантажувачів BPF (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeableабоloader_v4), натомість викликається власна вбудована точка входу завантажувача. -
Виконує програму BPF. Для програм BPF точка входу завантажувача шукає скомпільований виконуваний файл у кеші програм. Функція
execute()потім:- Серіалізує дані акаунта в плоский буфер параметрів
- Створює віртуальну машину sBPF зі стеком, купою та областями пам'яті
- Запускає скомпільований код, споживаючи обчислювальні одиниці під час
виконання. Повертає
ComputationalBudgetExceeded, якщо бюджет перевищено. - Десеріалізує дані акаунта з буфера назад у стан акаунта
-
Видаляє кадр стека. Викликає
pop(), що перевіряє, чи інструкція не порушила правила обліку середовища виконання (баланси lamport збалансовані, акаунти лише для читання не були змінені, розміри даних акаунтів у межах лімітів). -
Накопичує обчислювальні одиниці. Обчислювальні одиниці, спожиті інструкцією, додаються до загальної суми транзакції через
saturating_add.
Кеш програм
Середовище виконання підтримує глобальний
ProgramCache,
який зберігає верифіковані та скомпільовані програми. Він враховує структуру
форків і обробляє правила видимості розгортання, витіснення та рекомпіляцію на
межах епох.
Типи записів кешу
Кожна закешована програма має
ProgramCacheEntryType,
який визначає її поведінку під час виконання:
| Тип | Опис |
|---|---|
Loaded | Верифікована та скомпільована програма, готова до виконання. |
Builtin | Нативна програма, скомпільована у бінарний файл валідатора (System, Stake, Vote тощо). Не зберігається в блокчейні. |
Unloaded | Раніше верифікована програма, скомпільований виконуваний файл якої було витіснено з пам'яті для звільнення місця. Все ще відстежує статистику використання. Може бути перезавантажена без повторної верифікації. |
FailedVerification | Надгробок для програм, які не пройшли верифікатор sBPF за поточного набору функцій. Може стати Loaded, якщо активація функцій змінить правила верифікації. |
Closed | Надгробок для програм, які були явно закриті або ніколи не розгортались. Також використовується для акаунтів (таких як буферні акаунти), які належать до завантажувача, але не містять виконуваного коду. |
DelayVisibility | Синтетичний надгробок, що повертається ProgramCacheForTxBatch::find(), коли запис Loaded існує, але ще не набув чинності (його effective_slot у майбутньому). Ніколи не зберігається безпосередньо в кеші. |
Затримка видимості
Нові розгорнуті або оновлені програми не набувають чинності негайно. Константа
DELAY_VISIBILITY_SLOT_OFFSET
дорівнює 1, що означає, що програма, розгорнута у слоті N, набуває чинності у
слоті N+1. Під час слота розгортання будь-яка спроба викликати нову версію
повертає DelayVisibility, що змушує середовище виконання повідомити "Програму
не розгорнуто".
Політика витіснення
Кеш зберігає до
MAX_LOADED_ENTRY_COUNT
(512) скомпільованих записів програм. Коли досягається ліміт, найменш
використовувані програми витісняються до стану Unloaded. Використання
відстежується за допомогою
tx_usage_counter
(збільшується кожного разу, коли транзакція посилається на програму) та
latest_access_slot.
Рекомпіляція на межі epoch
Якщо активація функції змінює
ProgramRuntimeEnvironments
на межі epoch, усі закешовані програми
рекомпілюються
для нового середовища.
Повернення даних
Програми можуть встановлювати дані для повернення через системний виклик
sol_set_return_data. Дані зберігаються у структурі
TransactionReturnData
рівня транзакції, яка містить байти даних та program_id програми, чия
інструкція викликала системний виклик. Максимальний розмір становить 1024 байти
(MAX_RETURN_DATA).
Is this page helpful?