CPI yürütme ve yetkiler

Özet

CPI'lar yetki kontrolü, hesap çevirisi ve veri senkronizasyonu dahil 11 çalışma zamanı adımından geçer. Maksimum çağrı derinliği: 5 (SIMD-0268 ile 9). Yetki kuralları, çağrılanın çağıranın verdiği yetkinin ötesine geçmesini engeller.

Yetki kuralları

CPI'lar çağıranın hesap yetkilerini katı bir şekilde çağrılana genişletir. Çalışma zamanı bu kuralları prepare_next_instruction içinde kontrol eder:

Senaryoİzinli mi?Uygulama noktasıHata
Çağıran hesabı yazılabilir olarak geçirir, çağrılan yazılabilir olarak işaretlerEvet----
Çağıran hesabı salt okunur olarak geçirir, çağrılan yazılabilir olarak işaretlerHayırprepare_next_instructionPrivilegeEscalation
Çağıran hesabı yazılabilir olarak geçirir, çağrılan salt okunur olarak işaretlerEvet----
Çağıran hesabı imzalayan olarak geçirir, çağrılan imzalayan olarak işaretlerEvet----
Çağıran hesabı imzalayan olmayan olarak geçirir, çağrılan imzalayan olarak işaretler, hesap çağıranın seed'lerinden türetilmiş bir PDAEvetprepare_next_instruction--
Çağıran hesabı imzalayan olmayan olarak geçirir, çağrılan imzalayan olarak işaretler, hesap çağırandan bir PDA DEĞİLHayırprepare_next_instructionPrivilegeEscalation
Çağıran hesabı imzalayan olarak geçirir, çağrılan imzalayan olmayan olarak işaretlerEvet----
Program A kendisini doğrudan çağırır (A -> A)Evetpush()--
Program A, B'yi çağırır, B de A'yı çağırır (dolaylı yeniden giriş)Hayırpush()ReentrancyNotAllowed
Native loader, bpf_loader, bpf_loader_deprecated veya precompile'a CPIHayırcheck_authorized_programProgramNotSupported
Hesap işlemde bulunamadıHayırprepare_next_instructionMissingAccount

Ayrıcalık kuralları şu şekilde özetlenebilir:

  1. Yazılabilir ayrıcalık yükseltilemez. Çağıran bir hesabı salt okunur olarak işaretlerse, çağrılan onu yazılabilir olarak işaretleyemez.
  2. İmzalayan ayrıcalığı yetkilendirme gerektirir. Bir hesap, çağrılanda yalnızca şu durumlarda imzalayan olabilir: (a) çağıran içinde zaten imzalayan ise, VEYA (b) invoke_signed aracılığıyla çağıran programın seed'lerinden türetilmiş bir PDA ise.
  3. Ayrıcalık azaltmaya her zaman izin verilir. Çağrılan, çağıranın verdiğinden daha az ayrıcalık kullanabilir.

CPI yürütme akışı

Bir CPI, birkaç runtime katmanından geçer. Bu bölüm, program SDK çağrısından syscall sınırı üzerinden runtime'a ve geri dönüşe kadar olan tam pipeline'ı belgeler. Her adım, onu uygulayan kaynak dosyasına referans verir.

Program instruction çağrısının maksimum yüksekliğine max_instruction_stack_depth denir ve MAX_INSTRUCTION_STACK_DEPTH sabiti olan 5'e ayarlanmıştır. MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 aktif olduğunda, bu 9'a yükselir.

Stack yüksekliği 1, başlangıç transaction instruction'ıdır. Her CPI, yüksekliği 1 artırır. Maksimum 5, bir programın 4 seviye derinliğe kadar CPI yapabileceği anlamına gelir (SIMD-0268 ile 8 seviye derinliğe kadar).

Adım 1: Program invoke veya invoke_signed çağırır

Program, invoke veya invoke_signed çağırır. invoke, boş bir imzalayan seed dizisi ile invoke_signed çağıran ince bir wrapper'dır. SDK fonksiyonu, Instruction, AccountInfo slice'ını ve imzalayan seed'lerini VM belleğine serileştirir, ardından syscall'u tetikler.

Adım 2: Syscall girişi

SBF VM, sol_invoke_signed_rust syscall handler'ına dispatch eder, bu da paylaşılan giriş noktasını çağırır: cpi_common.

Adım 3: Çağrı maliyetini tüket

cpi_common içindeki ilk işlem, paylaşılan hesaplama ölçerinden sabit çağrı maliyetini düşmektir: invoke_units = 1.000 CU (veya SIMD-0339 ile 946 CU).

Adım 4: Talimatı VM belleğinden çevir

Syscall işleyicisi, talimatı programın VM adres alanından ana taraf Rust tiplerine translate_instruction_rust aracılığıyla çevirir. Bu işlem bir StableInstruction yapısını okur, veri uzunluğunu MAX_INSTRUCTION_DATA_LEN (10.240 bayt) ile doğrular, ardından veri serileştirme maliyetini düşer.

Adım 5: İmzalayan seed'lerini çevir ve PDA'ları türet

İşleyici translate_signers_rust fonksiyonunu çağırır. Her imzalayan seed seti için runtime:

  1. İmzalayan seed setlerinin sayısını MAX_SIGNERS (16) ile kontrol eder.
  2. Her seed setinin uzunluğunu MAX_SEEDS (set başına 16 seed) ile kontrol eder.
  3. Seed'ler ve çağıranın program ID'si ile Pubkey::create_program_address fonksiyonunu çağırır. Eğer seed'ler geçerli bir PDA üretmezse, CPI BadSeeds hatası ile başarısız olur.
  4. Elde edilen PDA pubkey'lerini bir signers vektöründe toplar.

Bu türetilmiş PDA'lar, çağrılan talimat için geçerli imzalayanlar olarak kabul edilir.

Adım 6: Yetkili programı kontrol et

Devam etmeden önce runtime, hedef programın CPI için izinli olduğunu doğrulamak üzere check_authorized_program fonksiyonunu çağırır. Aşağıdaki programlar engellenmiştir:

  • Native loader
  • bpf_loader ve bpf_loader_deprecated
  • bpf_loader_upgradeable (belirli yönetim talimatları hariç: upgrade, set_authority, set_authority_checked (feature-gated), extend_program_checked (feature-gated), close)
  • Precompile programları (ed25519, secp256k1, vb.)

İhlal durumunda ProgramNotSupported döner.

Adım 7: Yetki doğrulaması (prepare_next_instruction)

Runtime, çağrılanın InstructionAccount listesini oluşturan ve yetki kurallarını uygulayan prepare_next_instruction fonksiyonunu çağırır. Tam karar tablosu için aşağıdaki Yetki kuralları bölümüne bakın.

Adım 8: Hesap bilgilerini çevir

İşleyici, aşağıdakileri yapan translate_accounts fonksiyonunu çağırır:

  1. Hesap bilgisi sayısını doğrular MAX_CPI_ACCOUNT_INFOS değerine göre (128 veya SIMD-0339 ile 255).
  2. Hesap bilgisi çeviri maliyetini hesaplar (yalnızca SIMD-0339): (num_account_infos * 80) / 250 CU.
  3. Her çalıştırılamayan, tekrarlanmayan hesap için, VM belleğinden ana bilgisayar belleğine işaretçileri çevirerek bir CallerAccount oluşturur. Bu, hesap başına veri serileştirme maliyetinin hesaplanmasını içerir: account_data_len / cpi_bytes_per_unit CU.

Adım 9: CPI öncesi hesap senkronizasyonu (çağıran'dan çağrılan'a)

Çağrılanı yürütmeden önce, runtime çağıranın hesap değişikliklerini senkronize eder, böylece çağrılan bunları görebilir. update_callee_account fonksiyonu her çevrilen hesap için çağrılır ve lamport'ları, verileri ve sahibi kopyalar. Hesap verisi senkronizasyonu bölümünde ayrıntılı alan eşlemesine bakın.

Adım 10: Komut bağlamını yığına ekle, çağrılanı yürüt ve çıkar

Runtime, process_instruction fonksiyonunu çağırır ve bu fonksiyon:

  1. Komut yığınına yeni bir çerçeve eklemek için push() fonksiyonunu çağırır. push(), yeniden giriş kuralını uygular: bir program kendisini yalnızca doğrudan çağıran ise çağırabilir (yani, program A A'yı çağırabilir, ancak A, A'yı çağıran B'yi çağıramaz). İhlal durumunda ReentrancyNotAllowed döndürür.
  2. Çağrılanın program giriş noktasını çözen ve onu çağıran process_executable_chain fonksiyonunu çağırır. Çağrılan, aynı paylaşılan hesaplama ölçeri ile çalışır. Çağrılan tarafından yapılan tüm CU tüketimi, çağıranın kalan bütçesini azaltır.
  3. Çağrılanın çerçevesini kaldırmak ve lamport bakiyelerinin değişmediğini doğrulamak için pop() fonksiyonunu çağırır (değişmişse UnbalancedInstruction döndürür).

Adım 11: CPI sonrası hesap senkronizasyonu (çağrılan'dan çağıran'a)

process_instruction döndükten sonra (çıkarma işlemini içerir), runtime değişiklikleri her yazılabilir hesap için update_caller_account aracılığıyla çağırana geri senkronize eder. Ek olarak, update_caller_account_region veri bölgeleri değişen hesaplar için VM bellek bölgesi eşlemelerini günceller. Hesap verisi senkronizasyonu bölümünde ayrıntılı alan eşlemesine bakın.

CPI syscall, çağıran programa 0 (başarılı) döndürür.

Is this page helpful?

Yönetici

© 2026 Solana Vakfı.
Tüm hakları saklıdır.
Bağlanın