Модель вартості CPI та синхронізація даних

Підсумок

Кожен CPI коштує ~1,000 CU базово плюс витрати на серіалізацію. Дані облікових записів синхронізуються до та після виконання викликаної програми. Підпис PDA використовує program ID викликаючої програми. Повернення даних обмежене 1,024 байтами.

Модель вартості CPI

Витрати CPI вираховуються з того самого бюджету обчислень транзакції (спільний лічильник). Повна формула вартості для кожного виклику invoke / invoke_signed:

total_cpi_cost = invocation_cost
+ instruction_data_cost
+ account_meta_cost (SIMD-0339 only)
+ account_info_cost (SIMD-0339 only)
+ per_account_data_cost (for each non-executable account)
+ callee_execution_cost

Розбивка витрат

Компонент витратФормулаДжерело
Виклик (фіксований)invoke_units = 1,000 CU (946 з SIMD-0339)Стягується при вході в CPI
Серіалізація instruction datainstruction_data_len / cpi_bytes_per_unitcpi_bytes_per_unit = 250. Стягується в translate_instruction.
Серіалізація account meta (SIMD-0339)(num_account_metas * 34) / cpi_bytes_per_unitКожен AccountMeta становить 34 байти (32 pubkey + 1 is_signer + 1 is_writable). Стягується в translate_instruction.
Трансляція account info (SIMD-0339)(num_account_infos * 80) / cpi_bytes_per_unitACCOUNT_INFO_BYTE_SIZE = 80 байтів (32 key + 32 owner + 8 lamports + 8 data_len). Стягується в translate_account_infos.
Дані на обліковий записaccount_data_len / cpi_bytes_per_unitСтягується для кожного облікового запису в CallerAccount::from_account_info та для виконуваних облікових записів.
Виконання викликаної програмиСтільки CU, скільки споживає викликана програмаВираховується зі спільного лічильника під час виконання викликаної програми.

Приклад розрахунку вартості

CPI зі 100 байтами instruction data, 5 account meta, 5 account info (кожен з 1,000 байтами даних), SIMD-0339 активний:

invocation_cost = 946
instruction_data_cost = 100 / 250 = 0 (integer division)
account_meta_cost = (5 * 34) / 250 = 0
account_info_cost = (5 * 80) / 250 = 1
per_account_data_cost = 5 * (1000 / 250) = 20
total (before callee) = 967 CUs

Синхронізація даних облікового запису

Стан облікового запису синхронізується між викликаючою та викликаною стороною у двох точках під час CPI. Це забезпечує узгоджене бачення даних облікового запису з обох сторін.

Синхронізація перед CPI (від викликаючої до викликаної сторони)

Перед виконанням викликаної сторони, update_callee_account копіює поточні модифікації викликаючої сторони до представлення облікового запису викликаної сторони:

ПолеНапрямокКоли
LamportsВикликаюча -> Викликана сторонаЯкщо значення відрізняється від поточного представлення викликаної сторони
Довжина данихВикликаюча -> Викликана сторонаЯкщо викликаюча сторона змінила розмір облікового запису. Не повинна перевищувати original_data_len + MAX_PERMITTED_DATA_INCREASE (10 КіБ).
Вміст данихВикликаюча -> Викликана сторонаЯкщо дані облікового запису можна модифікувати (can_data_be_changed проходить перевірку)
ВласникВикликаюча -> Викликана сторонаВстановлюється останнім, щоб зміни даних/lamports були дозволені під старим власником

Синхронізація після CPI (від викликаної до викликаючої сторони)

Після повернення викликаної сторони, update_caller_account копіює модифікації викликаної сторони назад до представлення викликаючої сторони. Це виконується лише для облікових записів, позначених як доступні для запису:

ПолеНапрямокКоли
LamportsВикликана -> Викликаюча сторонаЗавжди копіюється назад
ВласникВикликана -> Викликаюча сторонаЗавжди копіюється назад
Довжина данихВикликана -> Викликаюча сторонаЯкщо змінено. Вказівник на зріз даних VM та поле серіалізованої довжини оновлюються. Якщо обліковий запис було зменшено, звільнена пам'ять обнуляється. Якщо нова довжина перевищує original_data_len + MAX_PERMITTED_DATA_INCREASE, повертається InvalidRealloc.
Вміст данихВикликана -> Викликаюча сторонаКопіюється з буфера даних викликаної сторони до серіалізованої області даних викликаючої сторони

Обмеження realloc

Розмір даних облікового запису може бути змінений під час CPI до MAX_PERMITTED_DATA_INCREASE = 10 240 байтів (10 КіБ) понад довжину даних облікового запису на початку поточної інструкції верхнього рівня. Це обмеження застосовується як у update_callee_account (перед CPI), так і в update_caller_account (після CPI). Перевищення цього ліміту повертає InvalidRealloc.

Підписування PDA

Коли викликається invoke_signed, середовище виконання виводить адреси PDA з наданих seed та ідентифікатора програми викликача. Це відбувається в translate_signers_rust:

  1. Масив signer seeds валідується: максимум MAX_SIGNERS (16) наборів signer seed.
  2. Кожен набір seed валідується: максимум MAX_SEEDS (16) seed на набір, кожен seed максимум MAX_SEED_LEN (32) байти.
  3. Викликається Pubkey::create_program_address з seed та ідентифікатором програми викликача (а не викликуваної програми).
  4. Якщо seed не створюють валідну PDA (тобто результуюча точка знаходиться на кривій ed25519), CPI завершується з помилкою BadSeeds.
  5. Виведені pubkey PDA збираються та передаються до prepare_next_instruction як валідні підписувачі. Під час перевірки привілеїв, якщо обліковий запис викликуваної програми позначений як підписувач і його pubkey збігається з одним із цих виведених PDA, перевірка підписувача проходить успішно.

PDA виводиться з використанням ідентифікатора програми викликача, а не викликуваної програми. Це означає, що лише програма, яка володіє PDA (та, чий ідентифікатор використовувався для її виведення), може підписувати від її імені. Програма не може підписувати для PDA, виведених з інших програм.

Повернення даних

Програми можуть передавати дані назад викликачам, використовуючи механізм повернення даних. Для цього використовуються два системні виклики:

  • sol_set_return_data: Встановлює до MAX_RETURN_DATA (1024) байтів даних для повернення для поточної інструкції. Вартість становить data_len / cpi_bytes_per_unit + syscall_base_cost обчислювальних одиниць.
  • sol_get_return_data: Зчитує дані, встановлені останньою виконаною інструкцією. Повертає дані разом з ідентифікатором програми, яка їх встановила. Вартість становить (data_len + 32) / cpi_bytes_per_unit + syscall_base_cost обчислювальних одиниць (32 байти для ідентифікатора програми).

Дані, що повертаються, зберігаються для кожної транзакції і перезаписуються кожною інструкцією, яка викликає sol_set_return_data. На початку кожного виклику програми середовище виконання скидає дані, що повертаються до порожнього стану. Після повернення CPI викликач може прочитати будь-які дані, які останніми встановив викликаний код (або будь-яка програма, яку він викликав).

Дані, що повертаються, обмежені 1024 байтами. Тільки остання програма, яка викликала sol_set_return_data у ланцюжку викликів, визначає те, що бачить викликач. Якщо викликаний код здійснює подальші CPI, які встановлюють дані для повернення, його власні дані перезаписуються.

Is this page helpful?

Керується

© 2026 Фонд Solana.
Всі права захищені.
Залишайтеся на зв'язку