Выполнение аккаунта

Кратко

Перед выполнением рантайм загружает аккаунты, проверяет плательщика комиссии, проверяет освобождение от аренды и сериализует данные аккаунта в память, доступную для программ.

Эта страница посвящена внутренним механизмам рантайма. Большинству разработчиков эта информация не нужна для создания программ. Для информации, ориентированной на разработчиков, см. Структура аккаунта.

Загрузка аккаунтов

Перед выполнением транзакции рантайм загружает все указанные аккаунты через load_transaction_accounts(). В этом процессе выполняется несколько проверок:

  1. Проверка плательщика комиссии: Плательщик комиссии (первый аккаунт) должен существовать, быть системным аккаунтом или nonce-аккаунтом и иметь достаточно лампортов для оплаты комиссии (validate_fee_payer()). После оплаты комиссии аккаунт должен либо остаться освобождённым от аренды, либо иметь ровно 0 лампортов. Нельзя, чтобы баланс оказался между 0 и минимальным значением для освобождения от аренды. Nonce-аккаунты всегда должны сохранять достаточно лампортов для освобождения от аренды. Если плательщик не является ни системным аккаунтом, ни nonce-аккаунтом, транзакция завершится ошибкой TransactionError::InvalidAccountForFee.

  2. Ограничение размера загружаемых данных: Общий размер всех загруженных аккаунтов (включая TRANSACTION_ACCOUNT_BASE_SIZE по 64 байта на аккаунт) не должен превышать MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES (64 МБ). Превышение этого лимита приведёт к ошибке TransactionError::MaxLoadedAccountsDataSizeExceeded.

  3. Проверка program account: Каждый program account, вызываемый инструкцией, должен существовать и принадлежать корректному загрузчику: одному из PROGRAM_OWNERS (BPF Loader Upgradeable, BPF Loader, BPF Loader Deprecated, Loader V4) или нативному загрузчику. Если program account не существует, транзакция завершится ошибкой TransactionError::ProgramAccountNotFound. Если он существует, но имеет некорректного владельца, транзакция завершится ошибкой TransactionError::InvalidProgramForExecution.

  4. Несуществующие аккаунты: Аккаунты, которые не существуют в сети, загружаются как аккаунты по умолчанию (0 лампортов, пустые данные, владелец — System Program) с rent_epoch, установленным в u64::MAX.

Формат сериализации BPF

Когда программа вызывается, рантайм сериализует аккаунты в непрерывный буфер памяти и передаёт его в BPF VM. Формат сериализации (стандартный выровненный формат, используемый всеми загрузчиками, кроме устаревшего loader-v1) определён в serialize_parameters_aligned().

Буфер начинается с u64 (8 байт, little-endian), содержащего количество аккаунтов. Затем для каждого аккаунта в инструкции буфер содержит:

СмещениеРазмерПолеТип
01маркер дубликатаu8 (0xFF = уникальный, index = дубликат этого аккаунта)
11is_signeru8 (0 или 1)
21is_writableu8 (0 или 1)
31executableu8 (0 или 1)
44original_data_len (зарезервировано, всегда 0)[0u8; 4]
832keyPubkey
4032ownerPubkey
728lamportsu64 (little-endian)
808data_lenu64 (little-endian)
88data_lendata[u8]
88 + data_len10240 + paddingrealloc space + alignmentЗаполнено нулями до MAX_PERMITTED_DATA_INCREASE (10 КиБ) + выравнивание до BPF_ALIGN_OF_U128 (8 байт)
...8rent_epochu64 (little-endian)

После всех аккаунтов буфер добавляет:

РазмерПоле
8instruction_data_len (u64, little-endian)
instruction_data_leninstruction_data
32program_id (Pubkey)

Для дублирующихся аккаунтов записывается только 1 байт (маркер дубликата с индексом оригинала) плюс 7 байт заполнения.

Дедупликация аккаунтов

Когда один и тот же публичный ключ аккаунта встречается несколько раз в массиве accounts инструкции, рантайм проводит дедупликацию. Каждая запись в списке аккаунтов инструкции получает свою собственную InstructionAccount структуру, но записи, которые ссылаются на один и тот же аккаунт на уровне транзакции, указывают на одни и те же исходные данные.

Метод is_instruction_account_duplicate определяет, является ли данный индекс аккаунта инструкции первым вхождением или дубликатом, путем поиска индекса аккаунта на уровне транзакции и нахождения первого индекса на уровне инструкции, который на него ссылается:

  • Если текущий индекс аккаунта инструкции равен первому сопоставленному индексу, это не дубликат (возвращает None).
  • В противном случае возвращается Some(first_index), где first_index — индекс первого вхождения.

Поскольку все ссылки на один и тот же аккаунт используют одни и те же исходные AccountSharedData, изменения через любую из ссылок сразу видны через все остальные. Однако одновременно может быть только одна изменяемая ссылка. Попытка получить изменяемую ссылку на один и тот же аккаунт через два разных индекса аккаунта инструкции одновременно вернет InstructionError::AccountBorrowFailed. Программы должны освободить одну ссылку прежде чем получить другую на тот же аккаунт.

После выполнения программы рантайм десериализует буфер обратно (deserialize_parameters_aligned()) и применяет все изменения к lamports, data (включая изменения длины до MAX_PERMITTED_DATA_INCREASE), и owner.

Is this page helpful?

Содержание

Редактировать страницу

Управляется

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