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