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

Кратко

Программы компилируются в sBPF через LLVM и выполняются в изолированной виртуальной машине с лимитом 1,4 млн вычислительных единиц (CU) на транзакцию. В рантайме кэшируется до 512 скомпилированных программ, предоставляются системные вызовы для логирования, CPI, криптографии и работы с памятью, а новые деплойменты задерживаются на 1 slot.

Компиляция

Solana использует LLVM для компиляции программ в бинарные файлы ELF, содержащие байткод Solana Bytecode Format (sBPF). ELF-бинарник хранится в сети на исполняемом аккаунте.

sBPF — это собственный вариант байткода eBPF, адаптированный для рантайма Solana. Это не стандартный eBPF, а версия с модификациями, специфичными для Solana.

Написание программ

Программы для Solana в основном пишутся на Rust двумя способами:

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

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

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

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

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

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

  5. Выполняет BPF-программу. Для BPF-программ loader entrypoint ищет скомпилированный исполняемый файл в кэше программ. Затем функция execute():

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

  7. Суммирует вычислительные юниты. Вычислительные юниты, потраченные инструкцией, добавляются к общему количеству транзакции через saturating_add.

Кэш программ

В рантайме поддерживается глобальный ProgramCache, который хранит проверенные и скомпилированные программы. Он учитывает структуру fork-graph и управляет правилами видимости деплоя, удалением из кэша и перекомпиляцией на границе epoch.

Типы записей в кэше

Каждая закэшированная программа имеет ProgramCacheEntryType, который определяет её поведение во время выполнения:

ТипОписание
LoadedПроверенная и скомпилированная программа, готовая к исполнению.
BuiltinНативная программа, скомпилированная в бинарник validator (System, Stake, Vote и др.). Не хранится on-chain.
UnloadedРанее проверенная программа, чей скомпилированный исполняемый файл был выгружен из памяти для освобождения места. Статистика использования продолжает отслеживаться. Может быть загружена обратно без повторной проверки.
FailedVerificationTombstone для программ, не прошедших проверку sBPF при текущем наборе функций. Может стать Loaded, если активация функций изменит правила верификации.
ClosedTombstone для программ, которые были явно закрыты или никогда не деплоились. Также используется для аккаунтов (например, buffer-аккаунтов), которые принадлежат загрузчику, но не содержат исполняемый код.
DelayVisibilityСинтетический tombstone, возвращаемый ProgramCacheForTxBatch::find(), когда существует запись Loaded, но она ещё не активна (её effective_slot в будущем). Никогда не хранится напрямую в кэше.

Задержка видимости

Только что задеплоенные или обновлённые программы становятся активными не сразу. Константа DELAY_VISIBILITY_SLOT_OFFSET равна 1, то есть программа, задеплоенная в slot N, становится активной в slot N+1. В течение слота деплоя любая попытка вызвать новую версию возвращает DelayVisibility, и рантайм сообщает: "Программа не задеплоена."

Политика вытеснения

Кэш хранит до MAX_LOADED_ENTRY_COUNT (512) скомпилированных программ. Когда лимит достигнут, наименее используемые программы вытесняются в состояние Unloaded. Использование отслеживается с помощью tx_usage_counter (увеличивается каждый раз, когда транзакция ссылается на программу) и latest_access_slot.

Рекомпиляция на границе эпохи

Если активация функции изменяет ProgramRuntimeEnvironments на границе epoch, все закэшированные программы рекомпилируются для новой среды.

Возвращаемые данные

Программы могут устанавливать возвращаемые данные через системный вызов sol_set_return_data. Данные сохраняются в структуре уровня транзакции TransactionReturnData, которая содержит байты данных и program_id программы, вызвавшей этот системный вызов. Максимальный размер — 1 024 байта (MAX_RETURN_DATA).

Is this page helpful?

Управляется

© 2026 Solana Foundation.
Все права защищены.
Связаться с нами
Выполнение программ | Solana