Ö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şaretler | Evet | -- | -- |
| Çağıran hesabı salt okunur olarak geçirir, çağrılan yazılabilir olarak işaretler | Hayır | prepare_next_instruction | PrivilegeEscalation |
| Çağıran hesabı yazılabilir olarak geçirir, çağrılan salt okunur olarak işaretler | Evet | -- | -- |
| Çağıran hesabı imzalayan olarak geçirir, çağrılan imzalayan olarak işaretler | Evet | -- | -- |
| Çağıran hesabı imzalayan olmayan olarak geçirir, çağrılan imzalayan olarak işaretler, hesap çağıranın seed'lerinden türetilmiş bir PDA | Evet | prepare_next_instruction | -- |
| Çağıran hesabı imzalayan olmayan olarak geçirir, çağrılan imzalayan olarak işaretler, hesap çağırandan bir PDA DEĞİL | Hayır | prepare_next_instruction | PrivilegeEscalation |
| Çağıran hesabı imzalayan olarak geçirir, çağrılan imzalayan olmayan olarak işaretler | Evet | -- | -- |
| Program A kendisini doğrudan çağırır (A -> A) | Evet | push() | -- |
| Program A, B'yi çağırır, B de A'yı çağırır (dolaylı yeniden giriş) | Hayır | push() | ReentrancyNotAllowed |
| Native loader, bpf_loader, bpf_loader_deprecated veya precompile'a CPI | Hayır | check_authorized_program | ProgramNotSupported |
| Hesap işlemde bulunamadı | Hayır | prepare_next_instruction | MissingAccount |
Ayrıcalık kuralları şu şekilde özetlenebilir:
- 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.
- İ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_signedaracılığıyla çağıran programın seed'lerinden türetilmiş bir PDA ise. - 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:
- İmzalayan seed setlerinin sayısını
MAX_SIGNERS(16) ile kontrol eder. - Her seed setinin uzunluğunu
MAX_SEEDS(set başına 16 seed) ile kontrol eder. - Seed'ler ve çağıranın program ID'si ile
Pubkey::create_program_addressfonksiyonunu çağırır. Eğer seed'ler geçerli bir PDA üretmezse, CPIBadSeedshatası ile başarısız olur. - Elde edilen PDA pubkey'lerini bir
signersvektö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_loadervebpf_loader_deprecatedbpf_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:
- Hesap bilgisi sayısını doğrular
MAX_CPI_ACCOUNT_INFOSdeğerine göre (128 veya SIMD-0339 ile 255). - Hesap bilgisi çeviri maliyetini hesaplar
(yalnızca SIMD-0339):
(num_account_infos * 80) / 250CU. - Her çalıştırılamayan, tekrarlanmayan hesap için, VM belleğinden ana
bilgisayar belleğine işaretçileri çevirerek bir
CallerAccountoluşturur. Bu, hesap başına veri serileştirme maliyetinin hesaplanmasını içerir:account_data_len / cpi_bytes_per_unitCU.
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:
- 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 durumundaReentrancyNotAlloweddöndürür. - Çağrılanın program giriş noktasını çözen ve onu çağıran
process_executable_chainfonksiyonunu ç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. - Ç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şseUnbalancedInstructiondö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?