Середовище виконання облікових записів

Підсумок

Перед виконанням середовище виконання завантажує облікові записи, перевіряє платника комісії, перевіряє звільнення від орендної плати та серіалізує дані облікових записів у структуру пам'яті, до якої програми можуть отримати доступ.

Ця сторінка описує внутрішню будову середовища виконання. Більшості розробників ця інформація не потрібна для створення програм. Перегляньте Структуру облікового запису для ознайомлення з точки зору розробника.

Завантаження облікових записів

Перед виконанням транзакції середовище виконання завантажує всі посилання на облікові записи через load_transaction_accounts(). Цей процес виконує кілька перевірок:

  1. Перевірка платника комісії: платник комісії (перший обліковий запис) повинен існувати, бути системним обліковим записом або обліковим записом nonce, і мати достатньо lamports для покриття комісій (validate_fee_payer()). Після сплати комісій обліковий запис повинен або залишитися звільненим від орендної плати, або мати рівно 0 lamports. Він не може опинитися між 0 та мінімумом для звільнення від орендної плати. Облікові записи nonce завжди повинні зберігати достатньо lamports, щоб залишатися звільненими від орендної плати. Якщо платник не є ні системним обліковим записом, ні обліковим записом nonce, транзакція завершується помилкою TransactionError::InvalidAccountForFee.

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

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

  4. Неіснуючі облікові записи: облікові записи, які не існують в ланцюзі, завантажуються як типові облікові записи (0 лампортів, порожні дані, належать системній програмі) з rent_epoch встановленим на u64::MAX.

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

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

Буфер починається з u64 (8 байтів, little-endian), що містить кількість облікових записів. Потім для кожного облікового запису в інструкції буфер містить:

ЗміщенняРозмірПолеТип
01маркер дублікатаu8 (0xFF = унікальний, індекс = дублікат цього облікового запису)
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 + вирівнюванняпростір для realloc + вирівнюванняЗаповнено нулями до 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.
Всі права захищені.
Залишайтеся на зв'язку