ملخص
تمر استدعاءات CPI عبر 11 خطوة في بيئة التشغيل تشمل فحص الصلاحيات، وترجمة الحسابات، ومزامنة البيانات. الحد الأقصى لعمق الاستدعاء: 5 (9 مع SIMD-0268). تمنع قواعد الصلاحيات البرنامج المستدعى من التصعيد إلى ما هو أبعد مما منحه البرنامج المستدعي.
قواعد الصلاحيات
تقوم استدعاءات CPI بتوسيع صلاحيات حسابات البرنامج المستدعي إلى البرنامج المستدعى
مع فرض صارم. تفحص بيئة التشغيل هذه القواعد في
prepare_next_instruction:
| السيناريو | مسموح؟ | نقطة الفرض | الخطأ |
|---|---|---|---|
| يمرر المستدعي الحساب كقابل للكتابة، ويضع المستدعى علامة قابل للكتابة | نعم | -- | -- |
| يمرر المستدعي الحساب كقراءة فقط، ويضع المستدعى علامة قابل للكتابة | لا | prepare_next_instruction | PrivilegeEscalation |
| يمرر المستدعي الحساب كقابل للكتابة، ويضع المستدعى علامة قراءة فقط | نعم | -- | -- |
| يمرر المستدعي الحساب كموقّع، ويضع المستدعى علامة موقّع | نعم | -- | -- |
| يمرر المستدعي الحساب كغير موقّع، ويضع المستدعى علامة موقّع، والحساب هو PDA مشتق من بذور المستدعي | نعم | prepare_next_instruction | -- |
| يمرر المستدعي الحساب كغير موقّع، ويضع المستدعى علامة موقّع، والحساب ليس PDA من المستدعي | لا | prepare_next_instruction | PrivilegeEscalation |
| يمرر المستدعي الحساب كموقّع، ويضع المستدعى علامة غير موقّع | نعم | -- | -- |
| البرنامج A يستدعي نفسه مباشرة (A -> A) | نعم | push() | -- |
| البرنامج A يستدعي B الذي يستدعي A (إعادة دخول غير مباشرة) | لا | push() | ReentrancyNotAllowed |
| استدعاء CPI إلى native loader أو bpf_loader أو bpf_loader_deprecated أو precompile | لا | check_authorized_program | ProgramNotSupported |
| الحساب غير موجود في المعاملة | لا | prepare_next_instruction | MissingAccount |
يمكن تلخيص قواعد الامتيازات كالتالي:
- لا يمكن تصعيد امتياز الكتابة. إذا قام المستدعي بوضع علامة على حساب كقراءة فقط، لا يمكن للمستدعى وضع علامة عليه كقابل للكتابة.
- امتياز التوقيع يتطلب تفويضاً. يمكن أن يكون الحساب موقعاً في المستدعى فقط
إذا (أ) كان بالفعل موقعاً في المستدعي، أو (ب) كان PDA مشتقاً من بذور البرنامج
المستدعي عبر
invoke_signed. - تقليل الامتيازات مسموح دائماً. يمكن للمستدعى استخدام امتيازات أقل من تلك التي منحها المستدعي.
تدفق تنفيذ CPI
يمر CPI عبر عدة طبقات تشغيلية. يوثق هذا القسم المسار الكامل من استدعاء SDK للبرنامج عبر حدود syscall إلى وقت التشغيل والعودة. تشير كل خطوة إلى الملف المصدري الذي ينفذها.
الارتفاع الأقصى لاستدعاء تعليمات البرنامج يسمى
max_instruction_stack_depth
ويتم تعيينه إلى
MAX_INSTRUCTION_STACK_DEPTH
الثابت 5. مع MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 نشط، يزداد هذا إلى 9.
ارتفاع المكدس 1 هو تعليمة المعاملة الأولية. كل CPI يزيد الارتفاع بمقدار 1. الحد الأقصى 5 يعني أن البرنامج يمكنه إجراء CPIs حتى عمق 4 مستويات (8 مستويات عمق مع SIMD-0268).
الخطوة 1: البرنامج يستدعي invoke أو invoke_signed
يستدعي البرنامج
invoke
أو
invoke_signed.
invoke هو غلاف رقيق يستدعي invoke_signed مع مصفوفة بذور موقع فارغة.
تقوم دالة SDK بتسلسل Instruction، وشريحة AccountInfo، وبذور الموقع
في ذاكرة VM، ثم تشغل syscall.
الخطوة 2: دخول Syscall
يرسل SBF VM إلى معالج syscall
sol_invoke_signed_rust،
الذي يستدعي نقطة الدخول المشتركة:
cpi_common.
الخطوة 3: استهلاك تكلفة الاستدعاء
الإجراء الأول داخل cpi_common هو
خصم تكلفة الاستدعاء الثابتة
من عداد الحوسبة المشترك:
invoke_units
= 1,000 وحدة حوسبة (أو 946 وحدة حوسبة مع SIMD-0339).
الخطوة 4: ترجمة التعليمة من ذاكرة الآلة الافتراضية
يقوم معالج استدعاء النظام بترجمة التعليمة من مساحة عنوان الآلة الافتراضية
للبرنامج إلى أنواع Rust على جانب المضيف عبر
translate_instruction_rust،
والذي يقرأ بنية StableInstruction، ويتحقق من طول البيانات مقابل
MAX_INSTRUCTION_DATA_LEN
(10,240 بايت)، ثم يخصم
تكلفة تسلسل البيانات.
الخطوة 5: ترجمة بذور الموقّع واشتقاق عناوين PDA
يستدعي المعالج
translate_signers_rust.
لكل مجموعة من بذور الموقّع، يقوم وقت التشغيل بـ:
- التحقق من عدد مجموعات بذور الموقّع مقابل
MAX_SIGNERS(16). - التحقق من طول كل مجموعة بذور مقابل
MAX_SEEDS(16 بذرة لكل مجموعة). - استدعاء
Pubkey::create_program_addressمع البذور ومعرّف برنامج المستدعي. إذا لم تنتج البذور عنوان PDA صالح، يفشل CPI معBadSeeds. - جمع مفاتيح PDA الناتجة في متجه
signers.
تُعامل عناوين PDA المشتقة هذه كموقّعين صالحين لتعليمة المستدعى.
الخطوة 6: التحقق من البرنامج المصرح به
قبل المتابعة، يستدعي وقت التشغيل
check_authorized_program
للتحقق من أن البرنامج المستهدف مسموح به لـ CPI. البرامج التالية محظورة:
- المحمّل الأصلي
bpf_loaderوbpf_loader_deprecatedbpf_loader_upgradeable(باستثناء تعليمات إدارة محددة:upgrade،set_authority،set_authority_checked(محدودة بميزة)،extend_program_checked(محدودة بميزة)،close)- برامج التجميع المسبق (ed25519، secp256k1، إلخ.)
يُرجع الانتهاك
ProgramNotSupported.
الخطوة 7: التحقق من الصلاحيات (prepare_next_instruction)
يستدعي وقت التشغيل
prepare_next_instruction
والذي يبني قائمة InstructionAccount للمستدعى ويفرض قواعد الصلاحيات. راجع
قواعد الصلاحيات أدناه للحصول على جدول القرارات الكامل.
الخطوة 8: ترجمة معلومات الحساب
يستدعي المعالج translate_accounts والذي:
- يتحقق من عدد معلومات الحساب
مقابل
MAX_CPI_ACCOUNT_INFOS(128، أو 255 مع SIMD-0339). - يحسب تكلفة ترجمة معلومات الحساب
(SIMD-0339 فقط):
(num_account_infos * 80) / 250وحدة حسابية. - لكل حساب غير قابل للتنفيذ وغير مكرر، يبني
CallerAccountعن طريق ترجمة المؤشرات من ذاكرة الجهاز الافتراضي إلى ذاكرة المضيف. يتضمن ذلك حساب تكلفة تسلسل البيانات لكل حساب:account_data_len / cpi_bytes_per_unitوحدة حسابية.
الخطوة 9: مزامنة الحساب قبل CPI (من المستدعي إلى المستدعى)
قبل تنفيذ المستدعى، يقوم وقت التشغيل بمزامنة تعديلات حساب المستدعي حتى يتمكن
المستدعى من رؤيتها. يتم استدعاء الدالة
update_callee_account
لكل حساب مترجم، لنسخ lamports والبيانات والمالك. راجع
مزامنة بيانات الحساب
للحصول على تفاصيل تعيين الحقول.
الخطوة 10: إضافة سياق التعليمات، وتنفيذ المستدعى، والإزالة
يستدعي وقت التشغيل
process_instruction،
والذي:
- يستدعي
push()لإضافة إطار جديد إلى مكدس التعليمات. تفرضpush()قاعدة إعادة الدخول: يمكن للبرنامج استدعاء نفسه فقط إذا كان المستدعي المباشر (أي يمكن للبرنامج A استدعاء A، لكن لا يمكن لـ A استدعاء B الذي يستدعي A). يؤدي الانتهاك إلى إرجاعReentrancyNotAllowed. - يستدعي
process_executable_chainوالذي يحدد نقطة دخول برنامج المستدعى ويستدعيه. يعمل المستدعى بنفس عداد الحساب المشترك. يقلل كل استهلاك للوحدات الحسابية من قبل المستدعى من الميزانية المتبقية للمستدعي. - يستدعي
pop()لإزالة إطار المستدعى والتحقق من أن أرصدة lamport لم تتغير (UnbalancedInstructionإذا لم يكن كذلك).
الخطوة 11: مزامنة الحساب بعد CPI (من المستدعى إلى المستدعي)
بعد عودة process_instruction (والذي يتضمن الإزالة)، يقوم وقت التشغيل بمزامنة
التغييرات مرة أخرى إلى المستدعي عبر
update_caller_account
لكل حساب قابل للكتابة. بالإضافة إلى ذلك،
update_caller_account_region
يحدث تعيينات منطقة ذاكرة الجهاز الافتراضي للحسابات التي تغيرت مناطق بياناتها.
راجع
مزامنة بيانات الحساب
للحصول على تفاصيل تعيين الحقول.
يُرجع استدعاء النظام CPI القيمة 0 (نجاح) إلى البرنامج المُستدعي.
Is this page helpful?