ملخص
تُجمَّع البرامج إلى sBPF عبر LLVM وتعمل في آلة افتراضية معزولة بميزانية 1.4 مليون وحدة حوسبة لكل معاملة. تخزن بيئة التشغيل ما يصل إلى 512 برنامجًا مُجمَّعًا في الذاكرة المؤقتة، وتوفر نداءات نظام للتسجيل، وCPI، والتشفير، والذاكرة، وتؤخر عمليات النشر الجديدة بمقدار slot واحد.
التجميع
تستخدم سولانا LLVM لتجميع البرامج إلى ملفات ثنائية ELF تحتوي على صيغة بايت كود سولانا (sBPF). يُخزَّن الملف الثنائي ELF على السلسلة في حساب قابل للتنفيذ.
sBPF هو متغير سولانا المخصص من بايت كود eBPF، مُصمَّم خصيصًا لبيئة تشغيل سولانا. وهو ليس eBPF قياسيًا ويحتوي على تعديلات خاصة بسولانا.
كتابة البرامج
تُكتب برامج سولانا أساسًا بلغة Rust باستخدام أحد ثلاثة أساليب:
Anchor
إطار عمل يستخدم وحدات ماكرو Rust لتقليل الشيفرة المكررة. مُوصى به لمعظم المطورين.
Pinocchio
مكتبة Rust خفيفة الوزن وعديمة النسخ، مُحسَّنة لاستهلاك الحوسبة وحجم الملف الثنائي. تتضمن حزمًا خاصة بالبرنامج لـCPIs الشائعة.
Rust الأصلي
Rust مباشر بدون أطر عمل. يوفر تحكمًا كاملًا لكنه يتطلب تنفيذًا يدويًا أكثر.
راجع استدعاءات البرامج المتقاطعة لمفاهيم CPI وأمثلة تنطبق على هذه الأساليب.
نموذج تنفيذ البرنامج
عند معالجة معاملة، تُنفِّذ بيئة التشغيل كل تعليمة
بالتسلسل عبر
process_message().
لكل تعليمة، تقوم بيئة التشغيل بما يلي:
-
تجهيز سياق التعليمة. تستدعي
prepare_next_top_level_instruction()لتعيين مؤشرات حسابات التعليمة، وضبط علامات الموقِّع والقابلية للكتابة، وتهيئةTransactionContext. -
التحقق من التجميعات المسبقة. إذا كان البرنامج تجميعًا مسبقًا، تستدعي بيئة التشغيل
process_precompile()، التي لا تزال تدفع وتسحب إطار مكدس (عبرpush()و_rspop()_) لكنها تتجاوز آلة sBPF الافتراضية وبحث ذاكرة التخزين المؤقتة للبرامج، وتُنفِّذ الشيفرة الأصلية مباشرةً. -
دفع إطار مكدس. (تحدث الخطوات 3-6 داخل
InvokeContext::process_instruction()وprocess_executable_chain()، المُستدعاتَين منprocess_message().) تستدعيpush()علىInvokeContext، الذي يزيد ارتفاع مكدس التعليمة ويُطبِّق قاعدة إعادة الدخول: يُسمح للبرنامج بإعادة دخول نفسه فقط إذا كان المُستدعي المباشر (البرنامج في أعلى مكدس التعليمة الحالي) هو البرنامج ذاته. يُسمح بالتكرار الذاتي العميق (A -> A -> A)، مع مراعاة حدود عمق المكدس. تُعيد أنماط إعادة الدخول الأخرى (مثل، A يستدعي B يستدعي A)InstructionError::ReentrancyNotAllowed. -
تحليل البرنامج. تستدعي بيئة التشغيل
process_executable_chain()التي تحدد المُحمِّل. إذا كان مالك program account هو المُحمِّل الأصلي، فالبرنامج مدمج ويُبحث عن دالة نقطة دخوله مباشرةً منProgramCacheForTxBatch. إذا كان المالك أحد مُحمِّلات BPF (bpf_loader_deprecated،bpf_loader،bpf_loader_upgradeable، أوloader_v4)، يُستدعى نقطة دخول المُحمِّل المدمجة الخاصة به بدلًا من ذلك. -
تنفيذ برنامج BPF. بالنسبة لبرامج BPF، تبحث نقطة دخول المُحمِّل عن الملف التنفيذي المُجمَّع في ذاكرة التخزين المؤقتة للبرامج. ثم تقوم دالة
execute()بما يلي:- تسلسل بيانات الحساب في مخزن معاملات مؤقت مسطّح
- إنشاء آلة sBPF الافتراضية بالمكدس والكومة ومناطق الذاكرة
- تشغيل الشيفرة المُجمَّعة مع استهلاك وحدات الحوسبة خلال التنفيذ. يُعيد
ComputationalBudgetExceededإذا تجاوزت الميزانية. - إلغاء تسلسل بيانات الحساب من المخزن المؤقت إلى حالة الحساب
-
سحب إطار المكدس. تستدعي
pop()التي تتحقق من أن التعليمة لم تنتهك قواعد محاسبة بيئة التشغيل (أرصدة lamport متوازنة، ولم تُعدَّل الحسابات للقراءة فقط، وأحجام بيانات الحساب ضمن الحدود). -
تجميع وحدات الحوسبة. تُضاف وحدات الحوسبة المستهلكة بالتعليمة إلى إجمالي المعاملة عبر
saturating_add.
ذاكرة التخزين المؤقتة للبرامج
تحتفظ بيئة التشغيل بـProgramCache عالمية
تخزِّن البرامج المُتحقَّق منها والمُجمَّعة. وهي مدركة لرسم بياني التفرع وتتعامل مع
قواعد رؤية النشر، والإخلاء، وإعادة التجميع عند حدود epoch.
أنواع إدخالات ذاكرة التخزين المؤقتة
لكل برنامج مخزَّن مؤقتًا نوع
ProgramCacheEntryType
يحدد سلوكه في بيئة التشغيل:
| النوع | الوصف |
|---|---|
Loaded | برنامج مُتحقَّق منه ومُجمَّع، جاهز للتنفيذ. |
Builtin | برنامج أصلي مُجمَّع في الملف الثنائي لـvalidator (النظام، الرهان، التصويت، إلخ). غير مخزَّن على السلسلة. |
Unloaded | برنامج سبق التحقق منه وأُخلي ملفه التنفيذي المُجمَّع من الذاكرة لتحرير المساحة. لا يزال يتتبع إحصائيات الاستخدام. يمكن إعادة تحميله دون إعادة التحقق. |
FailedVerification | علامة قبر للبرامج التي لم تجتز مُتحقِّق sBPF في ظل مجموعة الميزات الحالية. قد تصبح Loaded إذا غيَّرت تفعيلات الميزات قواعد التحقق. |
Closed | علامة قبر للبرامج التي أُغلقت صراحةً أو لم تُنشر قط. تُستخدم أيضًا للحسابات (كحسابات المخزن المؤقت) التي تنتمي إلى مُحمِّل لكن لا تحتوي على شيفرة قابلة للتنفيذ. |
DelayVisibility | علامة قبر اصطناعية تُعيدها ProgramCacheForTxBatch::find() عندما يوجد إدخال Loaded لكنه غير نافذ بعد (حيث إن effective_slot الخاص به في المستقبل). لا تُخزَّن مباشرةً في ذاكرة التخزين المؤقتة. |
تأخير الرؤية
البرامج المُنشورة أو المُحدَّثة حديثًا لا تصبح نافذة فورًا. الثابت
DELAY_VISIBILITY_SLOT_OFFSET
قيمته 1، مما يعني أن البرنامج المُنشور في slot N يصبح نافذًا في slot
N+1. خلال slot النشر، يؤدي أي محاولة لاستدعاء النسخة الجديدة إلى إعادة
DelayVisibility، مما يجعل بيئة التشغيل تُبلِّغ عن "البرنامج غير مُنشور."
سياسة الإخلاء
تستوعب ذاكرة التخزين المؤقتة ما يصل إلى
MAX_LOADED_ENTRY_COUNT
(512) إدخالًا من البرامج المُجمَّعة. عند بلوغ الحد، تُخلى البرامج الأقل استخدامًا إلى حالة Unloaded. يُتتبَّع الاستخدام بواسطة
tx_usage_counter
(يُزاد في كل مرة تُشير فيها معاملة إلى البرنامج) و
latest_access_slot.
إعادة التجميع عند حدود الـepoch
إذا غيَّر تفعيل ميزة
ProgramRuntimeEnvironments
عند حد epoch، تُعاد
تجميع
جميع البرامج المخزَّنة مؤقتًا مقابل البيئة الجديدة.
بيانات الإرجاع
يمكن للبرامج تعيين بيانات الإرجاع عبر نداء النظام sol_set_return_data. تُخزَّن البيانات في
بنية TransactionReturnData
على مستوى المعاملة التي تحتوي على بايتات البيانات وprogram_id للبرنامج الذي
استدعت تعليمته نداء النظام. الحجم الأقصى هو 1,024 بايت
(MAX_RETURN_DATA).
Is this page helpful?