اقرأ هذه الصفحة لتفهم الآلة التي تقف خلف كل إثبات على Rand: ما الذي تثبته، وكيف يتحول تشغيل برنامج ما إلى شيء يستطيع المُصادِق التحقق منه في أجزاء من الثانية، ومن أين تأتي الخصوصية. أما تكلفة الإثبات وكيف تنتجه المحفظة فانظر Proofs. وللاطلاع على المعاملات وبُنى التجزئة انظر Cryptography. ولمجموعة التعليمات التي يُكتب البرنامج على أساسها انظر The RISC-V ISA.
العبارة الواحدة التي يثبتها كل إثبات
يثبت zkVM نوعًا واحدًا من العبارات، لكل برنامج تشغّله السلسلة:
برنامج بصمته
hc، بدأ التنفيذ عندbase_pc، وعمل على مُدخَل خاص ملتزَم به عبرH_INومقطع مُدخَل عام ملتزَم به عبرH_PUB، وتوقف خلال2ᵗ − 1دورة على الأكثر، وأنفقGAS_LIMITمن الغاز على الأكثر، وكتب كلمات المُخرَج الثماني هذه.
التحويل المحمي هو هذه العبارة نفسها عن برنامج واحد ثابت هو ضيف حزمة الأصول المخفية، وقد ثُبّتت قيمة hc الخاصة به في ملف genesis باسم hc_bundle. ومنذ السلسلة 17 تُنتَج هذه العبارة مرتين لكل تحويل: إثبات ثانٍ صغير عن ضيف المصادقة (hc_auth في ملف genesis) يبيّن أن المرسل يملك مفتاح الإنفاق (Proofs). والاستدعاء السري هو العبارة نفسها عن برنامج منشور. فثمة آلة واحدة ومُتحقِّق واحد ومجموعة قيود واحدة للجميع. ولذلك فإن نشر برنامج عملية على البيانات (تخزين كلماته وhc) وليس عملية تشفيرية: لا توجد دارة لكل برنامج ولا إعداد موثوق.
نظام الإثبات هو STARK مبني بـ Plonky3. وهو لا يحتاج إلا إلى دالة تجزئة: لا حفل إعداد ولا منحنيات إهليلجية، ولذلك لا يوجد فيه ما تكسره خوارزمية Shor.
خط المعالجة
secret input ──► run the program ──► execution trace ──► polynomials ──► commit ──► FRI ──► proof
(keys, (RV32IM emulator) (nine tables, (one per (8× code, (80 queries,
notes, paths) one row per step) column) Merkle) 20-bit grinding)
every validator: recompute the challenges from the Merkle roots, open 80 random positions,
check the rules there → ≈ 16 ms
يُنفَّذ الصف العلوي مرة واحدة على جهاز المرسل، في نحو 100 ثانية للحزمة ونحو 7 ثوانٍ لإثبات التفويض المرافق لها. أما الصف السفلي فيُنفَّذ على كل مُصادِق، انطلاقًا من الحقول العامة والإثبات وحدهما.
1. تشغيل البرنامج وتدوين كل شيء
الآلة معالج RISC-V بعرض 32 بتًا: RV32IM ناقص FENCE وتعليمات CSR وEBREAK (The RISC-V ISA). يشغّل المُثبِت البرنامج أولًا على محاكٍ ويسجّل صفًا واحدًا لكل نبضة ساعة. هذا السجل هو أثر التنفيذ. وأي طرف يملكه يستطيع إعادة التشغيل خطوة بخطوة؛ أما بقية المخطط فوُجدت ليقتنع المُتحقِّق بصحة الأثر دون أن يملكه.
يتواصل البرنامج مع الخارج عبر ثمانية استدعاءات نظام ولا شيء غيرها:
| استدعاء النظام | الرقم | ما يفعله |
|---|---|---|
HALT | 0 | التوقف؛ تصبح كلمات المُخرَج الثماني نهائية |
WRITE_OUTPUT | 1 | تعيين إحدى كلمات المُخرَج العامة الثماني |
READ_INPUT | 2 | قراءة كلمة من المُدخَل الخاص |
POSEIDON2 | 3 | تجزئة مخزن مؤقت في الذاكرة في مكانه |
KECCAK | 4 | تبديل Keccak-f[1600] واحد |
SHA256 | 5 | ضغطة SHA-256 واحدة |
READ_PUBLIC | 6 | قراءة كلمة من مقطع المُدخَل العام |
POSEIDON2_LEN | 7 | POSEIDON2 مع ربط طول الرسالة بالتجزئة (مجموعة القيود 7) |
التزامات الملاحظات (note) والمُبطِلات (nullifiers) ومسارات Merkle ليست مضمّنة في الآلة. إنها شيفرة ضيف عادية تجهّز رسالة موسومة بالنطاق في الذاكرة ثم تستدعي POSEIDON2. ولا يقوم هذا الاستدعاء بالحشو، فيعطي [a] و[a, 0] التجزئة نفسها. والضيوف المرفقة لا تستدعيه إلا على رسائل ثابتة الطول. أما البرنامج الجديد الذي يجزّئ بيانات قد يتغير طولها فيستخدم POSEIDON2_LEN (The RISC-V ISA).
لا يوجد إجهاض
لا يملك المعالج تعليمة ترفض الإنهاء، فلا يستطيع البرنامج التوقف حين يفشل فحص ما. لذلك يسجّل ضيف الحزمة الفشل بدلًا من ذلك: كل فحص فاشل يُجري OR للقيمة 1 في مسجّل يسمى bad، وتكون bad آخر كلمة تُجزَّأ في بصمة المُخرَج:
D = H(64; anchor, nf₀..₃, cm₀..₃, fee, burn_a, burn_r, burn_asset, time, c, bad)
c هو التزام التفويض الذي ينشره إثبات المصادقة أيضًا (منذ السلسلة 17؛ أما بصمة السلاسل من 14 إلى 16 فلم تكن فيها c).
لا تأخذ السلسلة أبدًا الصورة الأصلية لـ D من المرسل. إنها تعيد بناء الصورة الأصلية من الحقول العامة للمعاملة، وتضبط bad = 0 بنفسها، ثم تجزّئ وتقارن. يستطيع المحتال أن يصنع إثباتًا صحيحًا تمامًا لتشغيل فشل فيه فحص، لكن هذا التشغيل أخرج H(…, 1)، والسلسلة تبحث عن H(…, 0). ومطابقتهما تتطلب تصادم تجزئة.
2. تقسيم الأثر إلى تسعة جداول
جدول واحد يصف معالجًا كاملًا سيكون عريضًا بإسراف، لذلك يُقسَّم الأثر إلى جداول متخصصة تُثبَت معًا في دفعة واحدة:
| الجدول | الصفوف عند المستوى t | ما يسجّله |
|---|---|---|
program | يُعلَن عنها، ≥ 2⁷ | كلمات تعليمات البرنامج، مفكوكة داخل الدارة؛ يحدد ما يجوز للمعالج جلبه |
cpu | 2ᵗ | صف واحد لكل دورة، تسبقه صفوف تجزّئ البرنامج والمُدخَل الخاص والمقطع العام؛ ويحمل القيم العامة الـ 35 |
memory | يُعلَن عنها، ≥ 2ᵗ⁺² | المسجّلات وذاكرة RAM، مرتبة بحسب (الفضاء، العنوان، الزمن) |
alu | 2ᵗ⁺¹ | كل عملية حسابية ومنطقية وإزاحة ومقارنة وضرب وقسمة، على أطراف من البايتات |
range، nibble | 256 لكل منهما | جداول بحث ثابتة: البايتات، وقوى العدد اثنين، وعمليات AND/OR/XOR على 4 بتات |
poseidon2 | 2ᵗ⁺² | تبديل التجزئة، بصف واحد لكل جولة |
input | يُعلَن عنها، ≥ 2⁷ | كلمات المُدخَل الخاص الملتزَم بها |
public | يُعلَن عنها، ≥ 2⁷ | كلمات المقطع العام (وهو في الحزمة ربط المعاملة) |
يوجد جدولان آخران، keccak وsha256، لا يظهران إلا في إثبات استدعى برنامجه أيًّا منهما، ولكل منهما 2⁷ صف على الأقل عند وجوده؛ ولا يستخدم ضيف الحزمة أيًّا منهما. وقد جاء حدّ 2⁷ صف الأدنى على كل جدول مُعلَن مع مجموعة القيود 7 (القسم 9).
المستوى t هو واحد من 10 أو 12 أو 14 أو 16 أو 18 أو 20، ويحدد أحجام الجداول. وكل حزمة تُثبَت عند المستوى 14: جدول CPU من 16,384 صفًا، يستخدم الضيف منها نحو 12,400. ويشغّل ضيف المصادقة 250 دورة عند المستوى 10. والباقي حشو، فيكشف الإثبات أي مستوى احتاجه البرنامج ولا يكشف أبدًا كم خطوة استغرق داخله (Tiers).
جدول الذاكرة هو ما يجعل RAM قابلة للتحقق. فلأنه مرتب بحسب العنوان ثم الزمن، تصبح عبارة «القراءة تعيد آخر قيمة كُتبت في ذلك العنوان» قاعدة بين صفين متجاورين.
3. ربط الجداول بالنواقل
كيف يعرف المُتحقِّق أن المجموع الذي استخدمه CPU هو المجموع الذي حسبته ALU؟ عبر النواقل (buses). حين يحتاج CPU إلى جمع فإنه يرسل رسالة (op, a, b, c)؛ وتستقبل ALU رسالة واحدة عن كل صف تحسبه. وللآلة سبعة عشر ناقلًا:
| الناقل | بين |
|---|---|
PROGRAM، PROGRAM_WORD | cpu ← program: جلب التعليمات، والكلمات التي تمتصها بصمة البرنامج |
MEMORY | cpu ↔ memory: كل قراءة وكتابة |
ALU | cpu → alu |
RANGE8، POW2، AND4، OR4، XOR4 | فحوص نطاق البايتات والإزاحات والعمليات الثنائية مقابل الجداول الثابتة |
POSEIDON2 | cpu → poseidon2: رسالة واحدة لكل تبديل |
INPUT_DIGEST، INPUT_READ | cpu ← input: الكلمات التي تمتصها بصمة المُدخَل، وقراءات البرنامج |
PUBLIC_DIGEST، PUBLIC_READ | cpu ← public: الزوج نفسه للمقطع العام |
KECCAK، SHA256 | cpu → جدولا التجزئة الاختياريان |
BLIND | كل جدول → الجدول التالي في دورة ثابتة: قيمة عشوائية واحدة لكل منها، في الصف الأول (مجموعة القيود 7) |
تُفحص النواقل بـحجة المشتقة اللوغاريتمية (LogUp). فبعد الالتزام بالجداول تُسحب قيمة عشوائية α، ويحتفظ كل جدول بمجموع تراكمي لـ ±1/(α − m) على الرسائل التي يرسلها أو يستقبلها. وتتلاشى المجاميع تمامًا إذا وفقط إذا استُقبلت كل رسالة أُرسلت، أي المجموعة المتعددة نفسها على الجانبين. وأي رسالة مزوّرة واحدة تترك باقيًا تكشفه α العشوائية باحتمال ساحق. ويستخدم ناقل الذاكرة قريبًا لها هو فحص تبديل المجموعات المتعددة.
وينشر كل جدول القيمة النهائية لمجموعه التراكمي، أي طرفيّته (terminal)، ويفحص المُتحقِّق أن الطرفيات تجمع إلى صفر. وحتى مجموعة القيود 6 كانت الطرفية دالة يمكن فحصها لمحتويات جدولها. أما منذ مجموعة القيود 7 فيحمل كل جدول خمسة أعمدة إضافية، ويرسل في صفه الأول قيمة عشوائية واحدة ويستقبل قيمة جاره على ناقل BLIND. فتتحرك كل طرفية بعنصر منتظم من حقل التحدي، ويبقى المجموع صفرًا.
وزوجا ناقلي البصمة والقراءة منفصلان عمدًا. فـ LogUp يوازن لكل رسالة وليس لكل مستهلك، ولذلك فإن ناقلًا واحدًا مشتركًا كان سيتيح للمُثبِت إسقاط كلمة من البصمة بينما تنجح قراءة تلك الكلمة.
4. ربط الأثر بالعالم الخارجي
تخرج من الأثر خمس وثلاثون قيمة عامة، كلها في جدول CPU:
| القيم | ما هي |
|---|---|
| 1 | نقطة الدخول pc |
| 1 | الطبقة (tier) |
| 8 | كلمات المخرجات (وهي في الحزمة الملخّص D) |
| 8 | hc، ملخّص البرنامج، ويُحسب داخل الأثر من كلمات جدول البرنامج |
| 8 | H_IN، ملخّص المدخلات الخاصة، محسوبًا على المدخلات وفي أولها ملح جديد من 128 بت |
| 8 | H_PUB، ملخّص المقطع العام، بلا ملح |
| 1 | GAS_LIMIT، سقف الغاز الذي يعلنه المُثبِت (مجموعة القيود 8) |
ولأن hc يُحسب داخل الدارة، لا يمكن لإثبات عن برنامج مختلف أن يمرّ على أنه إثبات عن ضيف الحزمة. فالمُتحقِّق لا يرى أي كلمة تعليمات؛ بل يقارن hc بالملخّص الذي يتوقعه. ولأن H_IN مملَّح، فإنه يربط المدخلات الخاصة دون أن يكشفها، حتى لمن يستطيع تخمينها. ولأن H_PUB غير مملَّح، يستطيع المُتحقِّق إعادة حسابه من كلمات في حوزته أصلًا. وهذه الكلمات في الحزمة هي ربط المعاملة، أي blake3("rand-tx-bind-1" ‖ chain_id ‖ tx) محسوبًا على المعاملة بعد تفريغ بايتات إثباتها، فلا يصحّ إثبات نُسخ إلى معاملة تختلف في الوجهة أو المذكرة أو الغلاف أو معرّف السلسلة. وفي السلسلة 20 (genesis binding_domain: 1) يصبح blake3("rand-tx-bind-2" ‖ genesis_hash ‖ chain_id ‖ tx)، فلا يصحّ الإثبات على سلسلة أخرى حتى لو تطابق معرّف السلسلة؛ والكلمات مدخل عام، فلم يتغيّر شيء في الدارة. ويُنشأ إثبات التفويض على الكلمات الثماني نفسها. ويُربط إثبات الاستدعاء في السلسلة 20 بالطريقة نفسها: مقطعه هو الكلمات العامة للبرنامج المحدَّدة وقت النشر يتبعها ثماني كلمات من ربط الاستدعاء (rand-call-bind-2، وrand-call-bind-1 قبل السلسلة 20)، فلا يصحّ إثبات استدعاء نُسخ تحت حزمة رسوم أخرى.
الغاز هو مقياس الآلة لثِقل التشغيل، وهو داخل الدارة منذ مجموعة القيود 8. يحمل جدول CPU عمودًا اسمه GAS يبدأ من 1 ويزيد 1 مع كل صف، و3 لصف امتصاص Poseidon2، و64 لصف SHA256، و192 لصف KECCAK. وعلى صف HALT تُثبت الدارة أن GAS ≤ GAS_LIMIT عبر أربعة أطراف مفحوصة النطاق من الفرق لا تُفتح أبدًا: فالحد عام، أما الغاز الذي أنفقه التشغيل فليس كذلك. ويرفض المُتحقِّق، قبل أن يبني المفتاح، أي حد يتجاوز ما قد يحتاجه ترويسة الإثبات نفسها:
gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ / 32) + 63·(2ˢ / 64)
حيث k وs هما الارتفاعان اللوغاريتميان المعلنان لجدولَي keccak وsha256، ويكون كل حد صفرًا إذا غاب جدوله. سقف ترويسة الطبقة 10 الخالية من التجزئة هو 1,279، وسقف ترويسة الطبقة 14 هو 20,479. وتسعّر السلسلة الاستدعاء بحدّه المعلن (الرسوم).
5. تحويل القواعد إلى متطابقات كثيرات حدود
كل عمود قائمة من الأعداد في حقل Goldilocks، أي أعداد صحيحة بترديد p = 2⁶⁴ − 2³² + 1. وقائمة من n عددًا هي نفسها كثيرة حدود من درجة أقل من n: تلك التي تمرّ بهذه الأعداد حين توضع على زمرة جزئية {1, ω, ω², …} من الحقل. ولدى Goldilocks زمر جزئية من كل حجم يساوي قوة للعدد 2 حتى 2³²، فيكون التحويل تحويل فورييه سريعًا.
وعندئذ تصبح قاعدة من قواعد الآلة معادلة بين كثيرات حدود الأعمدة. فعبارة «يتقدّم عدّاد البرنامج أربعًا ما لم تكن التعليمة قفزة» تصبح، بصورة تخطيطية:
is_jump(x)·(pc(ωx) − target(x)) + (1 − is_jump(x))·(pc(ωx) − pc(x) − 4) = 0
لكل x في الزمرة الجزئية، حيث تعني pc(ωx) «الصف التالي». ومجموعة هذه القواعد الكاملة لجدول ما هي AIR (التمثيل الوسيط الجبري). وتبلغ قواعد CPU وALU الدرجة 8، وتبقى قواعد غيرها بين 2 و4.
وكثيرة الحدود التي تساوي صفرًا عند كل نقطة من الزمرة الجزئية تقبل القسمة على Z(x) = xⁿ − 1. يجمع المُثبِت كل القواعد، بأوزان عشوائية، في كثيرة حدود واحدة C(x) ويقسمها على Z(x). فإن صحّت كل قاعدة في كل صف، كان خارج القسمة كثيرة حدود حقيقية منخفضة الدرجة. وإن أخفقت قاعدة واحدة في صف واحد، فلا يكون كذلك. وهكذا يتحوّل السؤال «هل صحّت كل خطوة من تشغيل بطول 16,000 دورة؟» إلى «هل هذه الدالة قريبة من كثيرة حدود منخفضة الدرجة؟»، وهذا سؤال يمكن الجواب عنه بالنظر في عدد قليل من القيم المختارة عشوائيًا.
6. الالتزام قبل أن تُعرف الأسئلة
يجب على المُثبِت أن يثبّت كثيرات حدوده قبل أن يرى الأسئلة العشوائية للمُتحقِّق، وإلا لفصّل إجاباته على مقاسها. فهو يقيّم كل عمود على نطاق أكبر بثماني مرات من الجدول (معامل التوسيع 8، ومعدل الشيفرة 1/8) وينشر جذر Merkle فوق التقييمات. وهذا التكرار شيفرة Reed–Solomon: فكثيرة الحدود المنخفضة الدرجة تتحدد بأي ثُمن من قيمها، فمن يغيّر صفًا واحدًا من الأثر عليه أن يغيّر معظم الكلمة الشيفرية الممتدة كي يبقى متسقًا، وستقع الفحوص العشوائية على موضع التلف.
وتستخدم أشجار Merkle دالة Poseidon2 فوق Goldilocks: العرض 8، وصندوق S هو x⁷، و4 جولات كاملة و22 جولة جزئية و4 جولات كاملة، وملخصات من أربعة عناصر حقلية (نحو 256 بت). وتُجزَّأ كل ورقة مع أربعة عناصر من العشوائية الجديدة، فلا يكشف الجذر شيئًا عن القيم التي تحته. والتبديل نفسه هو التزام الملاحظة ودالة تجزئة المُبطِل داخل الآلة. وثوابت جولاته الـ 86 جدول حرفي في الشيفرة المصدرية للعقدة (poseidon2_constants.rs)، فلا تستطيع ترقية أي مكتبة تغييرها.
7. الفحص العشوائي بـ FRI
يملك المُتحقِّق الآن جذور Merkle. وهو يربط أولًا خارج القسمة بالأثر: يختار نقطة عشوائية z خارج نطاق الجدول، ويطلب قيمة كل عمود وقيمة خارج القسمة عندها، ويتحقق مباشرة من أن C(z) = Q(z)·Z(z). ويبقى التأكد من أن الدوال الملتزَم بها كثيرات حدود منخفضة الدرجة فعلًا، وهذا ما تختبره FRI.
تعمل FRI بالطيّ. تُقسَّم كثيرة الحدود إلى جزأين زوجي وفردي، f(x) = fₑ(x²) + x·fₒ(x²). ولقيمة عشوائية β من المُتحقِّق، تكون f′(y) = fₑ(y) + β·fₒ(y) ذات نصف الدرجة على نطاق نصف الحجم. يلتزم المُثبِت بـ f′، ويتلقى β جديدة، ويطوي مرة أخرى (تطوي Rand حتى ثمانية في كل جولة) إلى أن يبقى ثابت يرسله علنًا. ثم يختار الاستعلام موضعًا عشوائيًا واحدًا في الطبقة الأولى ويتتبّعه نزولًا، متحققًا في كل طبقة من أن القيم المفتوحة في الأعلى تنطوي على القيمة التي تحتها. والدالة التي كانت بعيدة عن كونها منخفضة الدرجة لا تستطيع أن تُبقي كل هذه الوعود متسقة.
تجري Rand 80 استعلامًا. وبموجب مبرهنة فجوات القرب المبرهَنة، يترك كل استعلام للمحتال فرصة لا تزيد على 9/16، فتعطي 80 استعلامًا (9/16)⁸⁰ ≈ 2⁻⁶⁶·⁴. وبموجب حدسية ethSTARK التي تعتمد عليها STARKs المنشورة، يترك كل استعلام 1/8، وهي أصغر بكثير.
8. إزالة المحادثة
وُصف كل ما سبق على أنه حوار، لكن المعاملة يجب أن تكون رسالة واحدة يستطيع أي مدقِّق (validator) فحصها بمفرده. وتزيل تحويلة Fiat–Shamir المُتحقِّق: يحسب المُثبِت كل تحدٍّ «عشوائي» على أنه تجزئة لكل ما التُزم به حتى الآن، بإسفنجة مزدوجة من Poseidon2. وتغيير أي التزام يغيّر كل تحدٍّ يليه، فلا يستطيع المُثبِت اختيار التزامات تناسب التحديات.
وهناك إجراءان إضافيان يسدّان الثغرات المتبقية:
- حقل التمديد. حقل من
2⁶⁴عنصرًا أصغر من أن تُسحب منه التحديات: فالمحتال يحتاج إلى أن يقع التحدي على إحدى القيم السيئة القليلة لكثيرة حدود، وينجح باحتمال قدره نحوN/2⁶⁴مهما بلغ عدد الاستعلامات. لذلك يبقى الأثر في Goldilocks، لكن كل تحدٍّ يُسحب من تمديده التربيعي، أي نحو2¹²⁸عنصرًا. - الطحن (grinding). قبل اشتقاق مواضع الاستعلامات، يجب على المُثبِت أن يجد قيمة nonce تبدأ تجزئتها مع النص المسجَّل بـ 20 بتًا صفريًا، أي نحو مليون تجزئة. ويدفع المُثبِت الأمين هذه الكلفة مرة واحدة. أما المحتال الذي يعيد سحب مواضع الاستعلامات حتى تخطئ تزويره فيدفعها في كل محاولة. وهذا هو إثبات العمل الوحيد في Rand كلها، ولا علاقة له بالإجماع.
وبالجمع: 66.4 بتًا من الاستعلامات و20 من الطحن تعطي 86 بتًا مبرهَنة، ونحو 100 بت بموجب الحدسية، حيث يكون حدّ حقل التمديد هو القيد.
9. من أين تأتي المعرفة الصفرية
STARK ليست معرفة صفرية بذاتها: فالقيم المفتوحة عند مواضع الاستعلام هي قيم لكثيرات حدود الأثر، وهي تسرّب المعلومات إن لم يُحتَط. وتضيف إعدادات Rand أربع طبقات:
- كل كثيرة حدود ملتزَم بها تُعشوَن (تخلط FRI المُخفِية في Plonky3 أربع كلمات شيفرية عشوائية)، فيمكن إعادة إنتاج القيم الـ 80 المفتوحة، في التوزيع، على يد من لم يرَ الأثر قط؛
- كل ورقة Merkle مملَّحة، فلا تكشف الجذور ومسارات التوثيق شيئًا؛
- تُسحب عشوائية المُثبِت جديدةً من نظام التشغيل لكل إثبات، فيبدو إثباتان للدفعة نفسها غير مترابطين؛
- كل جدول معلَن له
2⁷صفوف على الأقل، وكل طرف LogUp نهائي معمّى (مجموعة القيود 7). يفتح الإثبات كل جدول عند 80 نقطة استعلام ونقطتين خارج النطاق، ويخلط الالتزام المُخفي من الصفوف العشوائية بقدر عدد صفوف الجدول، فلا يُخفى الجدول إلا ما دام فيه 82 صفًا على الأقل. و128 هي أصغر قوة للعدد 2 تحقق ذلك.
الطبقة 4 تصحيح. فقد استعادت مراجعة 2026-09-27 المدخلات الخاصة للاستدعاءات التي تضم 31 كلمة إدخال أو أقل انطلاقًا من براهين الإنتاج، وقرأت أعداد الجلب (fetch) لبرنامج ما من جدول البرنامج القصير، وميّزت خانات الإدخال الحقيقية في الحزمة من الخانات الوهمية بالاستناد إلى نهاياتها الطرفية غير المعمّاة. هذه البراهين هي تلك التي أُنشئت على السلسلة 15 وما قبلها. وقد أغلقت السلسلة 16 (مجموعة القيود 7، مع ضيف الحزمة الخالي من التفرعات) الثغرات الثلاث جميعها، وتحافظ السلاسل من 18 إلى 20 على هذه الإصلاحات.
الضمان المقدَّم هو المعرفة الصفرية الإحصائية في نموذج الأوراكل العشوائي. أما ما لا يخفيه البرهان فهو محدد بدقة: مستواه (tier)، وارتفاعات جداوله المعلنة، وقيمه العامة، ومنها حد الغاز. فارتفاع keccak أو sha256 المعلن البالغ 0 يعني أن التشغيل لم يستخدم تلك الدالة التجزئية قط، ولذلك يبيّن ترويسة برهان الاستدعاء ما إذا كان الاستدعاء قد أجرى عملية تجزئة، وما فوق الحد الأدنى، كم مرة فعل ذلك بدقة تصل إلى عامل اثنين. أما في الحزمة فالقيم العامة والحقول هي: المرساة (anchor)، وأربعة مبطِلات (nullifiers)، وأربعة التزامات (commitments)، والرسم، وعمليات الحرق، وأصل الحرق، والوقت، والتزام التفويض، وربط المعاملة. والأصل الذي تنقله عملية التحويل مدخل خاص، لذا تبدو دفعة zUSD ودفعة RAND متطابقتين على السلسلة. ودالة hc حتمية، فيستطيع أي شخص أن يعرف أن البرهان يخص ضيف الحزمة (وهذا شأن كل برهان ينقل قيمة)، أو أن يعرف، في حالة الاستدعاء، أي برنامج منشور جرى تشغيله.
10. ما الذي يتحقق منه المدقِّق
يشغّل Machine::verify_public(hc, words, proof) الفحوص التالية بادئًا بالأرخص:
- أن يكون مستوى البرهان وارتفاعات جداوله المعلنة ضمن الحدود (
check_declared_heights) قبل تحديد أي حجم انطلاقًا منها، فتكلّف الترويسة المشوَّهة بضع مقارنات لا بناء مفتاح يستغرق عدة ثوانٍ؛ ويجب أن تعلن الحزمة المستوى 14 من دون أي جدول تجزئة، وأن يعلن برهان التفويض المستوى 10؛ - أن تكون هناك 35 قيمة عامة، كل منها عنصر حقل بصيغته القانونية، مع
pv[HC] = hc، وpv[TIER]مساويًا لمستوى البرهان، وكل كلمة إخراج أقل من2³²، وGAS_LIMITلا يتجاوزgas_maxالوارد في الترويسة؛ - أن يتحقق STARK الدفعي مقابل مفتاح تحقق مخزَّن مؤقتًا لكل شكل
(tier, program, input, keccak, sha256, public heights)، لا لكل برنامج؛ - أن يساوي
H_PUBملخّصwords، وهو في الحزمة ربط المعاملة الذي حسبته العقدة بنفسها.
وقبل ذلك كله تفحص العقدة كل برهان يُقدَّم أو يُنشر عبر الشائعات (gossip) للتأكد من أن ترميزه قانوني: يجب أن يكون جدول طيّ FRI فيه هو الجدول الذي ينتجه مُثبِت أمين للدرجات التي تعلنها الترويسة. ومنذ fullnode v0.6.8 (التدقيق v6، ZKV-4) يرفض الفحص أولًا أي درجة معلنة تتجاوز 25 بتًا (MAX_HONEST_DEGREE_BITS، أي سقف جدول الذاكرة 2²⁴ مضافًا إليه مضاعفة المعرفة الصفرية، وهو أقصى ما يحمله برهان مقبول). وقبل وضع هذا الحد، كانت الترويسة التي تعلن درجة ضخمة تدفع العقدة إلى إعادة اشتقاق جدول طيّ هائل في حلقة أحداثها، فتبقى مشغولة طوال المدة التي يشاؤها المرسِل؛ وكان المُتحقِّق سيرفض البرهان على أي حال، فالحد لا يغيّر إلا كلفة الرفض. أما الآن فإن حدوث panic داخل الفحص يؤدي إلى رفض تلك المعاملة وحدها بدلًا من إيقاف العقدة.
بعد ذلك يعيد دفتر الأستاذ حساب ملخّص الحزمة D مع bad = 0 ويقارنه بكلمات الإخراج الثماني في البرهان. وعلى السلسلة 20 يشترط أيضًا أن يكون GAS_LIMIT في برهان الحزمة مساويًا تمامًا لقيمة bundle_gas_limit في genesis، أي 20,479، وأن يكون في برهان التفويض 1,279، بحيث تعلن كل عمليات التحويل القيم نفسها. ومع مفتاح مُسخَّن يستغرق الفحص كاملًا نحو 16 ms.
المعاملات، ولماذا اختيرت
كل رقم أدناه جزء من الإجماع: يتحقق منه المُتحقِّق، ولا يمرّ أي برهان أُنشئ بقيمة أخرى، وتغيير أي منها يعني مجموعة قيود جديدة وانقسامًا صلبًا (hard fork) (مجموعات القيود). ويحدَّد ملف FRI بملف genesis (fri_profile: production على السلسلة 20)، لا بالبرهان أبدًا.
| المعامل | القيمة | سبب هذه القيمة |
|---|---|---|
| الحقل الأساسي | Goldilocks, p = 2⁶⁴ − 2³² + 1 | عدد أولي من 64 بتًا تتطلب حساباته بضع تعليمات آلة فحسب، وله زمر جزئية ضربية من كل حجم يساوي قوة للعدد 2 حتى 2³²، فيصبح تحويل العمود إلى كثير حدود تحويل فورييه سريعًا. يقيم الأثر (trace) هنا لأنه الحقل الأرخص كلفة. |
| حقل التحديات | امتداد تربيعي F_p² (≈ 2¹²⁸) | لو سُحبت التحديات من Goldilocks نفسه لبقي حدّ أدنى للسلامة (soundness) يقارب N/2⁶⁴ لا يخفضه أي عدد من الاستعلامات. يدفع الامتداد هذا الحد إلى 2⁻¹⁰⁰ على الأكثر للآثار التي تصل إلى 2²⁵ صفًا. التحديات وحدها هي التي تتحمل كلفة الحقل الأكبر، أما الأثر فلا. والامتداد التكعيبي (≈ 2¹⁹²) هو الهامش المسمّى إن لزمت يومًا درجة أمان 128 بتًا. |
| معامل التوسيع (blow-up) | 8 (المعدل ρ = 1/8، log_blowup = 3) | سببان معًا. جدولا CPU وALU فيهما قواعد من الدرجة 8، ومع راية المعرفة الصفرية تبلغ درجة الحاصل 9، وهي أقصى ما يتسع له معامل توسيع 8؛ وأي قاعدة أعلى درجة تفرض معامل توسيع 16. ثم إن قدرة كل استعلام على كشف الغشاش تعتمد على المعدل: (1 + ρ)/2 = 9/16 لكل استعلام في الحالة المثبتة، وρ = 1/8 في الحالة المفترضة. أما معامل توسيع أكبر فيشتري استعلامات أقل، لكن بثمن توسيع منخفض الدرجة أكبر بالنسبة نفسها، على المُثبِت (prover) أن يحسبه ويحتفظ به في الذاكرة. |
| استعلامات FRI | 80 | العدد الذي يُبقي السلامة المثبتة عند نحو 86 بتًا. خُفّض مرة إلى 27 (مجموعتا القيود 3 و4)، وهو ما حقق هدفًا مفترضًا بمقدار 100 بت (3·27 + 20 = 101) لكنه لم يترك سوى نحو 42 بتًا مثبتًا؛ وأعاد تدقيق المعرفة الصفرية بتاريخ 2026-09-12 (النتيجة ZM1) العدد إلى 80. الاستعلامات هي معظم حجم الإثبات، ولهذا يبلغ حجم إثبات الحزمة ≈ 1.50 MB. وتحقيق 100 بت مثبتة يتطلب 97 استعلامًا. |
| التجريش (grinding) | 20 بتًا، في مرحلة الاستعلام | قبل سحب مواضع الاستعلام يجب على المُثبِت أن يجد nonce تبدأ بصمته بـ 20 بتًا صفريًا: نحو مليون دالة تجزئة، يدفعها المُثبِت الأمين مرة واحدة، ويدفعها الغشاش عند كل إعادة لسحب المواضع. يضيف 20 بتًا بكلفة زهيدة؛ أما زيادة التجريش (34 بتًا تعطي أيضًا 100 بت مثبتة) فتبطّئ كل إثبات أمين. ولا تجريش في مرحلة الالتزام. |
| ترتيب الطيّ (folding arity) | حتى 8 في كل جولة (max_log_arity = 3) | الطيّ بثمانية بدل اثنين يعطي ثلث عدد طبقات FRI، فيفتح كل استعلام مسارات Merkle أقل ويصغر الإثبات، مقابل عمل أكبر قليلًا لكل طبقة. |
| كثير الحدود النهائي | ثابت (log_final_poly_len = 0) | يطوي FRI حتى النهاية ويرسل قيمة واحدة مكشوفة، وهذا أبسط فحص ختامي للمُتحقِّق (verifier). |
| دالة التجزئة (داخل الدارة، وMerkle، والنص الإثباتي transcript) | Poseidon2، العرض 8، صندوق الاستبدال x⁷، 4 + 22 + 4 جولات، المعدل 4، السعة 4 | دالة تجزئة صُمّمت لتكون رخيصة بوصفها حسابًا في الحقل، فتكلّف التجزئة داخل الإثبات (الالتزامات، وnullifiers، ومسارات Merkle، وبصمة البرنامج) صفوفًا لا آلاف العمليات على البتات. ويجب ألا يشترك أس صندوق الاستبدال مع p − 1 = 2³²·3·5·17·257·65537 في أي عامل، و7 هو أصغر أس يحقق ذلك. العرض 8 بمعدل 4 يعطي بصمة من أربعة عناصر، نحو 256 بتًا. وثوابت الجولات الـ 86 جدول حرفي في العقدة (poseidon2_constants.rs). |
| عرض البصمة | 4 عناصر حقلية (≈ 256 بتًا) | كافٍ لأدوار الصورة الأصلية (preimage) (nullifiers، والالتزامات: 128 بتًا أمام Grover). وهو غير كافٍ لمقاومة تصادم كمّية بدرجة 128 بتًا في النص الإثباتي (2²⁵⁶/³ ≈ 2⁸⁵)؛ إذ يطلب تصميم الورقة البيضاء دالة تجزئة بطول 384 بتًا للنص الإثباتي، ولم تتبنَّها السلسلة العاملة بعد. |
| الإخفاء | 4 كلمات رمزية عشوائية في FRI؛ و4 عناصر ملح لكل ورقة Merkle؛ وعشوائية جديدة من نظام التشغيل لكل إثبات | الحد الأدنى الذي يجعل المواضع الـ 80 المفتوحة ومسارات Merkle قابلة للمحاكاة من القيم العامة وحدها، فيكون الإثبات صفري المعرفة إحصائيًا. وتبدو إثباتات الدفعة نفسها المنفَّذة مرتين غير مترابطة. |
| حدّ الجدول الأدنى | 2⁷ صفًا لكل جدول مُعلَن (مجموعة القيود 7) | يفتح الإثبات 80 نقطة استعلام ونقطتين خارج النطاق من كل جدول، أي 82 في المجموع، ويُعمّى الجدول ذو h صفًا بـ h صفًا عشوائيًا. 64 صفًا قليلة، و128 هي القوة التالية للعدد 2. وأي عدد استعلامات يتجاوز 126 يتطلب 2⁸. |
| التعمية الطرفية (terminal blind) | قيمة واحدة من F_p² لكل جدول، على ناقل BLIND (مجموعة القيود 7) | تُحسب النهاية الطرفية في LogUp من الأثر الخام، فهي من دون تعمية التزام غير مملّح بجدولها. والتعمية عنصر من الحقل الممتد لأن إحداثيًا واحدًا من الحقل الأساسي يحصر الإزاحة في مجموعة رقيقة يمكن اختبار شاهد مرشح (witness) عليها. |
| أوزان الغاز | 1 للصف؛ 3 لصف امتصاص Poseidon2؛ 64 لصف SHA256؛ 192 لصف KECCAK (مجموعة القيود 8) | يحسب كل وزن الصفوف التي يسحبها الصف إلى جداول أخرى: صف الامتصاص تبديلًا واحدًا من 32 صفًا، وصف SHA256 64 صفًا من 466 عمودًا، وصف KECCAK 32 صفًا من 2,612 عمودًا و100 صف ذاكرة. |
| أقصى درجة للقيود | 8 | تحددها قيمة معامل التوسيع أعلاه؛ وتُثبَّت درجة كل جدول باختبار، حتى لا تتجاوزها قاعدة جديدة بصمت. |
| المستويات (tiers) | 10، 12، 14، 16، 18، 20 | تُحدَّد أحجام الجداول بست درجات ثابتة، كل منها أربعة أمثال سابقتها، ولا يكشف الإثبات إلا مستواه: سلّة خشنة مُبطَّنة تدل على طول التشغيل لا على عدد دوراته. كل حزمة من المستوى 14 وكل إثبات تفويض (auth) من المستوى 10؛ وتُحدَّد الاستدعاءات بالمستوى 14 منذ fullnode v0.5.6. |
| أقصى أثر | 2²⁴ صفًا (2²⁵ مع بت المعرفة الصفرية) | الحجم الذي أُثبت من أجله حد الحقل الممتد؛ ويُرفض الأثر الأطول بدل أن يُثبت بادعاء سلامة لا يصح. |
| القيم العامة | 35 عنصرًا حقليًا | pc الدخول، والمستوى، وثماني كلمات للمخرجات، وثلاث بصمات من ثماني كلمات هي hc وH_IN وH_PUB، وحد الغاز المُعلَن: أصغر بيان يسمّي البرنامج والمدخل الخاص والمدخل العام وحدًّا للعمل. وكانت 34 حتى مجموعة القيود 7. |
| سقف حجم الإثبات | 2 MiB افتراضيًا؛ 4 MiB على السلسلة 20 (max_proof_bytes) | 2 MiB فوق كل إثبات تنتجه الضيوف (guests) المنشورة من دون جدول تجزئة، وتحت إثبات يحمل keccak (≈ 3.3 MB)؛ وتضبط نشأة السلسلة 20 القيمة 4,194,304 لتقبل الاثنين، كما فعلت السلاسل من 17 إلى 19. وضبطت السلاسل من 13 إلى 16 القيمة 8 MiB. ويُفحص السقف قبل فك ترميز الإثبات. |
ما تؤول إليه هذه الأرقام: سلامة مثبتة بمقدار 86 بتًا، ونحو 100 بت وفق افتراض ethSTARK (حيث الحقل الممتد، لا الاستعلامات، هو الحد)، ونحو 85 بتًا أمام خصم كمّي، يحددها طول دالة تجزئة النص الإثباتي البالغ 256 بتًا. وتورد الورقة البيضاء رقمي السلامة جنبًا إلى جنب عن قصد: فكل FRI-STARK منشور يعتمد على الافتراض في بتاته الأخيرة، وينبغي للقارئ الذي يقدّر حجم نشر ما أن يعرف أي افتراض يحمل أي بتات.
إثباتات عن إثباتات
بحجم نحو 1.50 MB يُعدّ إثبات الحزمة ثقيلًا، ويرافقه إثبات التفويض البالغ 1.37 MB، وتخزّنها كلها كل عقدة. والعلاج المبني هو التعاود (recursion): إثبات بيانه «تحققتُ من هذه الإثباتات». تحمل العقدة الكاملة (fullnode) آلة ثانية أصغر لهذا الغرض، هي آلة التعاود الافتراضية (recursion VM)، وكلمتها الأصلية عنصر من Goldilocks، وتعليماتها هي عمليات المُتحقِّق نفسها (حساب الحقل، وPoseidon2، وخطوات Merkle وFRI). ويعيد برنامجها التجميعي التحقق الكامل من N من إثباتات الحزم وينشر بصمة على قيمها العامة، مربوطة بالسلسلة والمُجمِّع (aggregator) وnonce. وقد كلّف التحقق من إثبات حزمة واحد داخله 1,968,619 صفًا في مجموعة القيود 6 و2,044,506 في مجموعة القيود 7؛ ولم يُعَد قياسه في المجموعة 8. وهو منفَّذ لكنه مُعطَّل بنشأة السلسلة في كل السلاسل حتى الآن، ومنها السلسلة 20. ولا يتحقق البرنامج التجميعي إلا من إثباتات الحزم، فيبقى إثبات التفويض في سلسلة ذات تفويض منفصل. كما أن إثباتاته الإنتاجية تحتاج ذاكرة أكبر مما خُطِّط له: وقد بلغت في القياس تحت مجموعة القيود 7 133 GB فأكثر (قريبًا: تجميع الكتل).
ما لا يدّعيه المخطط
- هامش المقاومة الكمّية نحو 85 بتّاً، لا 128. يكلّف البحث الكمّي عن تصادم في دالة تجزئة ذات
nبتّاً نحو2ⁿ/³. يقضي تصميم الورقة البيضاء بتجزئة نصّ البرهان بطول 384 بتّاً؛ أمّا السلسلة العاملة فتستخدم ملخّص Poseidon2 بطول 256 بتّاً. وتوسيعه يعني مجموعة قيود جديدة وانقسامًا صلبًا (hard fork). - 100 بتّ تخمين؛ و86 بتّاً مثبتة. رفع الرقم المثبت إلى 100 سيكلّف 97 استعلامًا، أي نحو خُمس إضافي من بيانات الاستعلامات في كل برهان.
- ثوابت Poseidon2 سحبٌ مبذور (seeded)، وليست صيغة منشورة. لا يُعرف أي هجوم على ذلك؛ لكنّ اعتماد مجموعة قياسية سيغيّر كل تجزئة على السلسلة.
- بعض العبارات يُتحقَّق منها بجانب البرهان لا داخله. يقارن المتحقّق (verifier) القيمة العامة
TIERببنية البرهان لكنّ الدائرة لا تقيّدها (التدقيق ZL1)، ويستند انضباط التلوّث (taint) في الضيف (guest) الخاص بالحزمة إلى حجّة بنيوية وأداة ضيف مُضعَف وتشويش طفروي (mutation fuzzing) لا إلى تحقّق آلي. - ترويسة البرهان غير مخفيّة. المستوى (tier) وارتفاعا جدولَي التجزئة ظاهرة علنًا، ومن ثمّ يكشف برهان الاستدعاء ما إذا كان الاستدعاء قد استخدم
KECCAKأوSHA256. ويوجد خيار للمُثبِت (prover) يملأ الجدولين معًا ولا تفعّله أي محفظة: فهو ينقل برهان المستوى 10 من نحو 1.3 MB إلى نحو 3.6 MB. - استدعاء النظام
POSEIDON2ليس متباينًا (injective) على المدخلات متغيّرة الطول. يبقى كما هو لأن كل التزام ومُبطِل (nullifier) وجذر وملخّص برنامج يعتمد عليه. ولا يستخدم أي ضيف مُوزَّعPOSEIDON2_LENحتى الآن. - الإثبات بطيء ويتولاه المُرسِل، أو مُثبِت يقترن به المُرسِل: نحو 100 s و5.7 GB على معالج حاسوب محمول للحزمة الواحدة. شُغّلت نوى GPU على وحدة GPU (من نوع NVIDIA H100) لأول مرة في 2026-09-28 وطابقت المرجع على CPU؛ وما زالت المحفظة تُثبِت على CPU.
مصادر أخرى للقراءة
- يستعرض قسم الورقة البيضاء «The Zero-Knowledge Scheme: How a Rand Proof Works» (
randprotocol.tex) المكوّنات السبعة نفسها مع مبرهنات السلامة. - تحدّد ورقة التنفيذ (
randprotocol_implementation.tex) مجموعة التعليمات واستدعاءات النظام، وضيف الحزمة، والإسفنجة (sponge) والملخّصات، وكل جدول وناقل (bus)، وإعدادات الآلة، وآلة التعاقب (recursion VM). - الشيفرة:
crates/randprotocol-zkvm/src/في fullnode —emulator.rsوtables/وmachine.rsوgas.rsوguests.rsوhash.rsوposeidon2_constants.rs.