Выполнение программ

Резюме

Программы компилируются в 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 с использованием одного из трёх подходов:

См. Cross-Program Invocations для ознакомления с концепциями и примерами CPI, применимыми во всех этих подходах.

Модель выполнения программ

При обработке транзакции среда выполнения последовательно выполняет каждую инструкцию через process_message(). Для каждой инструкции среда выполнения:

  1. Подготавливает контекст инструкции. Вызывает prepare_next_top_level_instruction() для сопоставления индексов аккаунтов инструкции, установки флагов подписанта и записи, а также настройки TransactionContext.

  2. Проверяет прекомпиляции. Если программа является прекомпиляцией, среда выполнения вызывает process_precompile(), которая по-прежнему помещает и извлекает кадр стека (через push() и pop()), но обходит sBPF VM и поиск в кэше программ, выполняя нативный код напрямую.

  3. Помещает кадр в стек. (Шаги 3–6 выполняются внутри InvokeContext::process_instruction() и process_executable_chain(), вызываемых из process_message().) Вызывает push() на InvokeContext, что увеличивает высоту стека инструкций и применяет правило реентерабельности: программа может повторно войти в себя только в том случае, если непосредственный вызывающий (программа на текущей вершине стека инструкций) является той же программой. Глубокая саморекурсия (A -> A -> A) допускается с учётом ограничений глубины стека. Другие паттерны реентерабельности (например, A вызывает B, B вызывает A) возвращают InstructionError::ReentrancyNotAllowed.

  4. Разрешает программу. Среда выполнения вызывает process_executable_chain(), которая определяет загрузчик. Если владельцем program account является нативный загрузчик, программа является встроенной, и её точка входа извлекается напрямую из ProgramCacheForTxBatch. Если владелец — один из BPF-загрузчиков (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable или loader_v4), вместо этого вызывается собственная встроенная точка входа загрузчика.

  5. Выполняет BPF-программу. Для BPF-программ точка входа загрузчика извлекает скомпилированный исполняемый файл из кэша программ. Функция execute() затем:

    • Сериализует данные аккаунтов в плоский буфер параметров
    • Создаёт sBPF VM со стеком, кучей и регионами памяти
    • Выполняет скомпилированный код, расходуя вычислительные единицы в процессе выполнения. Возвращает ComputationalBudgetExceeded, если бюджет превышен.
    • Десериализует данные аккаунтов из буфера обратно в состояние аккаунтов
  6. Извлекает кадр из стека. Вызывает pop(), которая проверяет, что инструкция не нарушила правила учёта среды выполнения (балансы lamport сбалансированы, аккаунты только для чтения не изменялись, размеры данных аккаунтов находятся в допустимых пределах).

  7. Накапливает вычислительные единицы. Вычислительные единицы, потреблённые инструкцией, добавляются к итогу транзакции через 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?