Eksekusi dan hak akses CPI

Ringkasan

CPI melewati 11 langkah runtime termasuk pemeriksaan hak akses, translasi akun, dan sinkronisasi data. Kedalaman panggilan maksimal: 5 (9 dengan SIMD-0268). Aturan hak akses mencegah callee meningkatkan hak akses melebihi yang diberikan caller.

Aturan hak akses

CPI memperluas hak akses akun caller ke callee dengan penegakan yang ketat. Runtime memeriksa aturan-aturan ini di prepare_next_instruction:

SkenarioDiizinkan?Titik penegakanError
Caller meneruskan akun sebagai writable, callee menandai writableYa----
Caller meneruskan akun sebagai read-only, callee menandai writableTidakprepare_next_instructionPrivilegeEscalation
Caller meneruskan akun sebagai writable, callee menandai read-onlyYa----
Caller meneruskan akun sebagai signer, callee menandai signerYa----
Caller meneruskan akun sebagai non-signer, callee menandai signer, akun adalah PDA yang diturunkan dari seed callerYaprepare_next_instruction--
Caller meneruskan akun sebagai non-signer, callee menandai signer, akun BUKAN PDA dari callerTidakprepare_next_instructionPrivilegeEscalation
Caller meneruskan akun sebagai signer, callee menandai non-signerYa----
Program A memanggil dirinya sendiri secara langsung (A -> A)Yapush()--
Program A memanggil B yang memanggil A (reentrancy tidak langsung)Tidakpush()ReentrancyNotAllowed
CPI ke native loader, bpf_loader, bpf_loader_deprecated, atau precompileTidakcheck_authorized_programProgramNotSupported
Akun tidak ditemukan dalam transaksiTidakprepare_next_instructionMissingAccount

Aturan privilege dapat dirangkum sebagai:

  1. Privilege writable tidak dapat ditingkatkan. Jika pemanggil menandai akun sebagai read-only, yang dipanggil tidak dapat menandainya sebagai writable.
  2. Privilege signer memerlukan otorisasi. Sebuah akun dapat menjadi signer dalam yang dipanggil hanya jika (a) akun tersebut sudah menjadi signer dalam pemanggil, ATAU (b) akun tersebut adalah PDA yang diturunkan dari seed program pemanggil melalui invoke_signed.
  3. Pengurangan privilege selalu diizinkan. Yang dipanggil dapat menggunakan privilege lebih sedikit daripada yang diberikan oleh pemanggil.

Alur eksekusi CPI

CPI melewati beberapa lapisan runtime. Bagian ini mendokumentasikan pipeline lengkap dari panggilan SDK program melalui batas syscall ke dalam runtime dan kembali. Setiap langkah merujuk pada file sumber yang mengimplementasikannya.

Tinggi maksimum dari pemanggilan instruksi program disebut max_instruction_stack_depth dan diatur ke konstanta MAX_INSTRUCTION_STACK_DEPTH sebesar 5. Dengan MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 aktif, ini meningkat menjadi 9.

Stack height 1 adalah instruksi transaksi awal. Setiap CPI menambah height sebesar 1. Maksimum 5 berarti program dapat melakukan CPI hingga kedalaman 4 level (8 level dengan SIMD-0268).

Langkah 1: Program memanggil invoke atau invoke_signed

Program memanggil invoke atau invoke_signed. invoke adalah wrapper tipis yang memanggil invoke_signed dengan array signer seeds kosong. Fungsi SDK menserialisasi Instruction, slice AccountInfo, dan signer seeds ke dalam memori VM, kemudian memicu syscall.

Langkah 2: Entry syscall

SBF VM melakukan dispatch ke handler syscall sol_invoke_signed_rust, yang memanggil entry point bersama: cpi_common.

Langkah 3: Konsumsi biaya invokasi

Tindakan pertama di dalam cpi_common adalah menagih biaya invokasi tetap dari compute meter bersama: invoke_units = 1.000 CU (atau 946 CU dengan SIMD-0339).

Langkah 4: Terjemahkan instruksi dari memori VM

Syscall handler menerjemahkan instruksi dari ruang alamat VM program ke tipe Rust sisi host melalui translate_instruction_rust, yang membaca struct StableInstruction, memvalidasi panjang data terhadap MAX_INSTRUCTION_DATA_LEN (10.240 byte), kemudian menagih biaya serialisasi data.

Langkah 5: Terjemahkan signer seed dan turunkan PDA

Handler memanggil translate_signers_rust. Untuk setiap set signer seed, runtime:

  1. Memeriksa jumlah set signer seed terhadap MAX_SIGNERS (16).
  2. Memeriksa panjang setiap set seed terhadap MAX_SEEDS (16 seed per set).
  3. Memanggil Pubkey::create_program_address dengan seed dan program ID pemanggil. Jika seed tidak menghasilkan PDA yang valid, CPI gagal dengan BadSeeds.
  4. Mengumpulkan pubkey PDA yang dihasilkan ke dalam vec signers.

PDA yang diturunkan ini diperlakukan sebagai signer yang valid untuk instruksi callee.

Langkah 6: Periksa program yang diotorisasi

Sebelum melanjutkan, runtime memanggil check_authorized_program untuk memverifikasi program target diizinkan untuk CPI. Program berikut diblokir:

  • Native loader
  • bpf_loader dan bpf_loader_deprecated
  • bpf_loader_upgradeable (kecuali instruksi manajemen tertentu: upgrade, set_authority, set_authority_checked (feature-gated), extend_program_checked (feature-gated), close)
  • Program precompile (ed25519, secp256k1, dll.)

Pelanggaran mengembalikan ProgramNotSupported.

Langkah 7: Verifikasi privilege (prepare_next_instruction)

Runtime memanggil prepare_next_instruction yang membangun daftar InstructionAccount callee dan menerapkan aturan privilege. Lihat Aturan privilege di bawah untuk tabel keputusan lengkap.

Langkah 8: Terjemahkan info akun

Handler memanggil translate_accounts yang:

  1. Memvalidasi jumlah info akun terhadap MAX_CPI_ACCOUNT_INFOS (128, atau 255 dengan SIMD-0339).
  2. Mengenakan biaya terjemahan info akun (hanya SIMD-0339): (num_account_infos * 80) / 250 CU.
  3. Untuk setiap akun yang tidak dapat dieksekusi dan tidak duplikat, membangun CallerAccount dengan menerjemahkan pointer dari memori VM ke memori host. Ini termasuk mengenakan biaya serialisasi data per akun: account_data_len / cpi_bytes_per_unit CU.

Langkah 9: Sinkronisasi akun pra-CPI (pemanggil ke yang dipanggil)

Sebelum mengeksekusi yang dipanggil, runtime menyinkronkan modifikasi akun pemanggil sehingga yang dipanggil dapat melihatnya. Fungsi update_callee_account dipanggil untuk setiap akun yang diterjemahkan, menyalin lamport, data, dan pemilik. Lihat Sinkronisasi data akun untuk pemetaan field yang detail.

Langkah 10: Push konteks instruksi, eksekusi yang dipanggil, dan pop

Runtime memanggil process_instruction, yang:

  1. Memanggil push() untuk menambahkan frame baru ke stack instruksi. push() memberlakukan aturan reentrancy: sebuah program hanya dapat memanggil dirinya sendiri jika ia adalah pemanggil langsung (yaitu, program A dapat memanggil A, tetapi A tidak dapat memanggil B yang memanggil A). Pelanggaran mengembalikan ReentrancyNotAllowed.
  2. Memanggil process_executable_chain yang menyelesaikan entry point program yang dipanggil dan memanggilnya. Yang dipanggil berjalan dengan compute meter bersama yang sama. Semua konsumsi CU oleh yang dipanggil mengurangi anggaran yang tersisa dari pemanggil.
  3. Memanggil pop() untuk menghapus frame yang dipanggil dan memverifikasi bahwa saldo lamport tidak berubah (UnbalancedInstruction jika tidak).

Langkah 11: Sinkronisasi akun pasca-CPI (yang dipanggil ke pemanggil)

Setelah process_instruction kembali (yang mencakup pop), runtime menyinkronkan perubahan kembali ke pemanggil melalui update_caller_account untuk setiap akun yang dapat ditulis. Selain itu, update_caller_account_region memperbarui pemetaan region memori VM untuk akun yang region datanya berubah. Lihat Sinkronisasi data akun untuk pemetaan field yang detail.

Syscall CPI mengembalikan 0 (sukses) ke program pemanggil.

Is this page helpful?

Dikelola oleh

© 2026 Yayasan Solana.
Semua hak dilindungi.
Terhubung