Кратко
Проверки во время выполнения после каждой инструкции: только владелец может списывать лампорты или изменять данные, данные могут увеличиваться максимум на 10 КиБ за инструкцию, смена владельца требует нулевой инициализации данных, а флаг исполняемости необратим.
Среда выполнения Solana применяет эти правила после выполнения каждой инструкции
с помощью
BorrowedInstructionAccount
методов. Каждое правило проверяется в момент изменения, и транзакция
откатывается, если какая-либо проверка не пройдена.
Правила для лампортов
| Правило | Применение | Ошибка |
|---|---|---|
| Только владелец может списывать лампорты | set_lamports(): проверяет is_owned_by_current_program() при lamports < current | ExternalAccountLamportSpend |
| В режиме только для чтения лампорты не изменяются | set_lamports(): проверяет is_writable() | ReadonlyLamportChange |
| Любая программа может зачислять лампорты на writable аккаунт | set_lamports(): проверка владельца применяется только если новый баланс меньше текущего; writable-проверка всё равно применяется | ReadonlyLamportChange |
| Баланс лампортов должен сходиться в инструкции | TransactionContext::pop(): проверяет get_lamports_delta() == 0 | UnbalancedInstruction |
Правила для данных
| Правило | Применение | Ошибка |
|---|---|---|
| Только владелец может изменять данные | can_data_be_changed(): проверяет is_owned_by_current_program() | ExternalAccountDataModified |
| В режиме только для чтения данные не изменяются | can_data_be_changed(): проверяет is_writable() | ReadonlyDataModified |
| Только владелец может изменять размер данных | can_data_be_resized(): проверяет is_owned_by_current_program() при new_len != old_len | AccountDataSizeChanged |
| Максимальный размер данных: 10 МиБ | TransactionAccounts::can_data_be_resized(): проверяет new_len <= MAX_ACCOUNT_DATA_LEN | InvalidRealloc |
| Максимальный рост за инструкцию: 10 КиБ | Десериализация в deserialize_parameters_aligned(): проверяет post_len - pre_len <= MAX_PERMITTED_DATA_INCREASE | InvalidRealloc |
| Максимальный рост за транзакцию: 20 МиБ | TransactionAccounts::can_data_be_resized(): проверяет совокупный resize_delta <= MAX_ACCOUNT_DATA_GROWTH_PER_TRANSACTION | MaxAccountsDataAllocationsExceeded |
Правила для владельца
| Правило | Применение | Ошибка |
|---|---|---|
| Только текущий владелец может переназначить владельца | set_owner(): проверяет is_owned_by_current_program() | ModifiedProgramId |
| Аккаунт должен быть доступен для записи | set_owner(): проверяет is_writable() | INLINE_CODE_PLACEHOLDER_06ed73c4723a857е_END |
| Данные должны быть инициализированы нулями | set_owner(): проверяет is_zeroed(data) | ModifiedProgramId |
Правила для флага исполняемости
| Правило | Применение | Ошибка |
|---|---|---|
| Аккаунт должен быть освобождён от rent | set_executable(): проверяет rent.is_exempt(lamports, data_len) | ExecutableAccountNotRentExempt |
| Только владелец может установить флаг | set_executable(): проверяет is_owned_by_current_program() | ExecutableModified |
| Аккаунт должен быть доступен для записи | set_executable(): проверяет is_writable() | ExecutableModified |
Переходы состояния rent
Аккаунты существуют в одном из трёх
RentState
состояний: Uninitialized (0 лампортов), RentPaying (больше 0, но
меньше минимального значения для освобождения от rent), и RentExempt
(равно или больше минимума). Запрещённые переходы вызывают
TransactionError::InsufficientFundsForRent.
Выполнение этих правил обеспечивается рантаймом через
transition_allowed():
- Любой аккаунт может перейти в
Uninitialized(закрытие) илиRentExempt. - Ни один аккаунт не может попасть в
RentPayingизUninitializedилиRentExempt. Все новые аккаунты должны быть освобождены от rent.
Правила заимствования аккаунта
Во время выполнения инструкции рантайм обеспечивает семантику единственного
писателя для заимствования аккаунтов. Программа может получить либо одну
изменяемую ссылку, либо несколько неизменяемых ссылок на аккаунт, но не
одновременно. Если программа пытается заимствовать аккаунт, который уже
изменяемо заимствован (или изменить заимствование аккаунта, который уже
неизменяемо заимствован), инструкция завершится с ошибкой
AccountBorrowFailed через
try_borrow()
и
try_borrow_mut().
Если инструкция завершается, пока заимствование всё ещё активно, рантайм
возвращает AccountBorrowOutstanding в
TransactionContext::pop().
Is this page helpful?