Sealevel — это модель исполнения Solana, позволяющая запускать несколько транзакций одновременно, если они не конфликтуют по данным. Ключевая идея проста: каждая транзакция заранее объявляет все аккаунты (участки состояния), с которыми она собирается работать, и права доступа к ним (read/write, требуется ли подпись). Благодаря этому рантайм способен построить «сетку независимости» и распараллелить вызовы. В связке с моделью аккаунтов и источником времени Solana это даёт предсказуемую пропускную способность и низкие задержки.
Эта страница — техническая опора для разработчиков и продвинутых пользователей. Здесь собраны принципы Sealevel, поведение под нагрузкой, анти-паттерны и практические приёмы проектирования dApp. За обзор протокола см. Архитектура Solana: Объяснение Высокой Производительности, PoH, Sealevel, терминологию виртуальной машины — Svm, устройство хранения — Accounts Model Solana.
Коротко про Sealevel: параллельное исполнение в Solana (рантайм и планировщик)
- Параллелизм идёт по данным, а не «по блокам»: независимые наборы аккаунтов исполняются одновременно.
- Транзакция обязана заранее указать все аккаунты, которыми будет пользоваться (включая те, что понадобятся во внутренних вызовах — CPI).
- Конфликт — это пересечение по аккаунту с правами записи (или по «горячим» системным ресурсам); чтение-чтение совместимо.
- Планировщик Sealevel строит партии транзакций, минимизируя конфликты; внутри партии операции идут параллельно.
- Комиссии и приоритеты работают локально вокруг «очагов данных», см. Local Fee Market и Priority Fee.
Базовые сущности Sealevel
Программа (program) Развёрнутый исполняемый код (аналог «контракта»), который вызывает пользовательская транзакция или другая программа (через CPI).
Аккаунт (account) Хранилище данных и/или объект-ресурс (включая токены, записи DEX, ордера и т. п.). В контексте вызова каждому аккаунту назначаются флаги: *is_signer*, *is_writable*, а также порядок.
Инструкция (instruction) Мини-вызов функции программы с явным списком «account metas» — именно он определяет доступ к конкретным аккаунтам. Транзакция содержит одну или несколько инструкций.
Compute Units (CU) Квота вычислений на транзакцию. Предел CU регулирует сложность работы; экономия важна для пропускной способности и UX.
Cross-Program Invocation (CPI) Вызов одной программы из другой внутри той же транзакции. Важное правило Sealevel: CPI не может «тайно» открыть новый аккаунт — все потенциальные аккаунты должны быть перечислены заранее.
Как именно достигается параллелизм
Sealevel реализует предварительное планирование по данным. Упрощённый жизненный цикл обработки партии:
- Узел-лидер принимает поток транзакций и «нарезает» их на партии.
- Для каждой транзакции строится множество аккаунтов с типами доступа (R/W).
- Рантайм пытается набрать в одну партию транзакции, для которых нет пересечений по write (RW-конфликты).
- Чтение-чтение* совместимо; *чтение-запись* или *запись-запись* на одном аккаунте — конфликт.
- Партия отдаётся на параллельное исполнение (в пределах ядер/воркеров). Завершившиеся транзакции освобождают «замки» на аккаунтах, следующая волна стартует.
Если в процессе CPI возникает попытка доступа к аккаунту, не указанному в транзакции, или меняется режим доступа (нужна запись вместо чтения), рантайм прерывает выполнение — конфликты не утаить.
Типы конфликтов и их признаки
| Тип конфликта | Что это означает | Как выявлять/лечить в dApp |
|---|---|---|
| write–write на одном аккаунте | Две транзакции хотят одновременно изменить один и тот же аккаунт | Шардируйте состояние: разбивайте глобальные «счётчики/пулы» на пользовательские или «по рынкам» |
| read–write на «горячем» аккаунте | Одна транзакция пишет, другая читает тот же аккаунт (пока он «под замком») | Используйте «снимки»/кэши, ETag-подобные стратегии и финальные проверки инвариантов |
| Скрытая зависимость через CPI | Внутренний вызов пытается использовать неуказанный в транзакции аккаунт | Явно перечисляйте «пороговые» аккаунты; проектируйте API программ так, чтобы их невозможно было вызвать без полного списка |
| Системные/арифметические лимиты CU | Транзакция «съедает» квоту и блокирует воркер | Профилируйте и оптимизируйте горячие пути, дробите инструкции |
Модель аккаунтов и «шахматка независимости»
Solana хранит данные в аккаунтах, а не в «глобальном сторидже». Это задаёт геометрию параллелизма:
- Если два действия затрагивают разные аккаунты, Sealevel может их запустить одновременно.
- Если они делят один и тот же аккаунт на запись, возникнет блокировка — в партию попадёт что-то одно.
- При разумном проектировании схемы хранения (PDA, «ключ-разделитель», per-user/per-market состояния) достигается почти линейный рост Throughput с числом независимых «островков данных».
Подробнее об устройстве см. Accounts Model Solana.
CPI: мощный инструмент с жёсткими рамками
CPI позволяет строить композицию программ: AMM вызывает токен-программу, ордербук — клиринг, кошелёк — системные операции и т. д. Но у этого есть следствия:
- Внешняя транзакция обязана принести с собой все аккаунты, которые могут понадобиться глубже по стеку CPI.
- Набор прав (read/write, signer) не может «усугубляться» вглубь без явного разрешения на поверхности.
- Поэтому API ваших программ должен быть детерминирован в части перечня ресурсов: указывайте в документации ровно, какие аккаунты нужны.
Этот контракт защищает Sealevel от «динамического» конфликта, который нельзя было бы спланировать заранее.
Локальные рынки комиссий и приоритеты
Параллелизм Sealevel тесно связан с локальными рынками комиссий: там, где много транзакций бьются за один и тот же «горячий» аккаунт (пул ликвидности, минт коллекции и т. п.), образуется локальный аукцион приоритета. Транзакции, не затрагивающие этот очаг, не переплачивают. Смотрите базовые страницы Local Fee Market и Priority Fee для пользовательского UX и ценообразования.
Для инфраструктуры и потоков заказов в моменты пиков используйте модели приватного/пакетного отправления; архитектурно это раскрывается в связке с Jito Block Engine, где бандлеры и блок-энджины минимизируют вредные перестановки и фронт-раннинг (Mev).
Проектные паттерны для высокой параллельности
- Шардируйте по пользователю/рынку. Храните состояние так, чтобы каждая операция касалась минимального набора аккаунтов. Пример: не общий «супер-счётчик» для всей dApp, а счётчики на пользователя/рынок/ординарный ключ.
- Разделяйте «горячее» и «холодное». Метаданные, конфигурации и редко меняющиеся записи держите в отдельных аккаунтах без записи в каждом вызове.
- Фиксируйте инварианты «на входе». Пусть каждая инструкция валидирует пред-условия и завершает работу раньше, если конфликт неминуем.
- Делайте «узкие» инструкции. Разбейте тяжёлый путь на несколько независимых инструкций, чтобы планировщик мог впитать их в разные партии.
- Идентификаторы-разделители (PDA). Проектируйте адреса аккаунтов так, чтобы они естественно «раскладывались» по ключам, исключая ненужные пересечения.
- Контролируйте CU. Используйте лимиты и профилирование, не допускайте «взрыва» вычислений одной транзакцией.
Анти-паттерны (и как их исправить)
- Глобальные счётчики и «общие корзины». Любая запись в такой объект серийно блокирует всех — заменяйте на иерархию счётчиков/балансов.
- Длинные цепочки CPI с «сюрпризами». Не вносите вглубь новые зависимости; публикуйте явные требования к аккаунтам в API.
- Смешивание прав доступа. Если вам почти всегда нужна запись, объявляйте write сразу; излишне «оптимистичное» объявление read → повышает риск отказа.
- Одна транзакция «за всё». Склеивание множества логических шагов в один вызов делает параллелизм невозможным и повышает риск лимитов CU.
Поведение под нагрузкой и диагностика
| Симптом | Возможная причина | Что делать разработчику |
|---|---|---|
| Рост отказов без явных ошибок кода | Транзакции сталкиваются по «горячим» аккаунтам, планировщик вынужден серийно ставить их в очередь | Проведите профилирование конфликтов: какие аккаунты чаще всего в write; переразбейте хранение |
| «Флапающая» латентность (p95/p99) | Перемежающаяся перегрузка конкретного рынка/пула | Добавьте подсказки по Priority Fee в UI, предложите режимы «обычно/быстро/срочно» |
| Высокий расход CU, деградация TPS | Излишняя логика на стороне одной транзакции, мало переиспользуемых кэшей | Декомпозируйте инструкции, оптимизируйте сериализацию/десериализацию |
| Отказы CPI «account not found/borrowed as immutable» | Неполный перечень аккаунтов или неверный режим доступа | Явно перечислите все аккаунты на поверхности, согласуйте флаги R/W и signer |
Пример: почему два свопа могут идти параллельно
Представьте две транзакции A и B:
- A меняет токен X↔Y в пуле «PX», трогая аккаунты: кошелёк пользователя A, два аккаунта токенов, записи пула «PX».
- B меняет токен U↔V в пуле «PY», трогая кошелёк пользователя B, два аккаунта токенов, записи пула «PY».
Если PX ≠ PY и нет пересечений по кошелькам/посредникам на запись, то A и B независимы — Sealevel отправит их в одну партию, и обе завершатся без ожидания друг друга. Если же две операции касаются одного и того же пула «PX» и пишут в его состояние, они будут сериализованы.
Сравнение Sealevel и классической модели EVM
| Аспект | Sealevel (Solana) | Классическая EVM (L1) |
|---|---|---|
| Единица состояния | Аккаунты с явными правами в инструкциях | Глобальный storage, адресуемый контрактами |
| Планирование | Параллельно по несовпадающим наборам аккаунтов | Последовательно (одна глобальная очередь) |
| Зависимости | Видны до исполнения (по списку account metas) | Выявляются во время исполнения |
| CPI / композиция | Разрешена, но только поверх объявленных аккаунтов | Вызовы свободны, зависимости — динамические |
| Ценообразование | Локальные рынки вокруг «горячих» данных (Local Fee Market) | Глобальный аукцион газа для всего блока |
| UX при пиках | Стабильно вне «очагов»; внутри — локальный аукцион приоритета | Растёт цена для всех, даже «непричастных» транзакций |
Безопасность и инварианты
- Подписи и владение. Любой аккаунт, требующий изменения, должен быть открыт с флагом *is_signer* у соответствующего владельца/программы.
- Borrow-правила. Аккаунт не может одновременно быть изменяемым и заимствованным как «immutable» в том же контексте — это защищает от классов ошибок на уровне ссылок.
- Детерминизм доступа. Поскольку все аккаунты указаны заранее, классы атак «скрытой подмены» через CPI существенно ограничены.
- Ограничение CU. Предел вычислений защищает узел от «пылесоса CPU», а сеть — от «залипания» одной транзакции.
Практические рекомендации по API программ
- Проектируйте плоские и предсказуемые интерфейсы: по названию инструкции и её параметрам должно быть ясно, какие аккаунты требуются.
- Документируйте минимальные и достаточные наборы аккаунтов для каждого пути.
- Валидируйте режимы доступа на старте: если нужен *write* — отклоняйте «read-транзакции» ранним кодом возврата.
- Синхронизируйте версии программ с клиентскими SDK: несовместимости часто проявляются в неправильно собранных списках account metas.
Метрики и наблюдаемость
- Доля конфликтов по аккаунтам. Сколько транзакций в партии пришлось сериализовать.
- Распределение CU. Где «горит» вычисление — в сериализации, криптографии, логике приложения.
- Латентность p95/p99 по типам операций, отдельно внутри и вне «горячих» рынков.
- Ошибки CPI. Частота «account not found», «borrowed as immutable», «insufficient privileges» — прямые подсказки, куда смотреть в API.
Частые вопросы (FAQ)
Это «параллельный консенсус»? Нет. Параллелится исполнение транзакций. Консенсус и финализация описаны отдельно (см. Solana Proof of History (PoH): как работает «доказательство истории» и Tower Bft).
Можно ли скрыть конфликт, подтянув аккаунт через CPI? Нельзя: все аккаунты должны быть перечислены в транзакции, иначе выполнение прервётся.
Почему две «простые» операции иногда ждут друг друга? Потому что они пишут в один и тот же «горячий» аккаунт. Перепроектируйте хранение, чтобы сократить пересечения.
Помогают ли высокие приоритетные комиссии «обогнать» конкурентов? Да, но только внутри локального рынка вокруг затронутых аккаунтов. См. Priority Fee.
Влияет ли Jito/бандлинг на параллелизм? Да: грамотное упорядочивание и пакетирование потоков снижает вредные перестановки и улучшает использование «пустот» параллелизма (см. Jito Block Engine).
Мини-глоссарий
- Account metas — список аккаунтов с правами доступа, передаваемый в инструкцию.
- CPI (Cross-Program Invocation) — вызов одной программы из другой в рамках одной транзакции.
- Conflict set — пересечение транзакций по аккаунтам на запись; определяет сериализацию.
- Compute Units (CU) — лимит вычислений на транзакцию.
- PDA (Program-Derived Address) — адрес аккаунта, детерминированно получаемый программой по сид-параметрам.
