استخدم هذه الصفحة لنشر برنامج على السلسلة واستدعائه: ما البرنامج، وكيف تبنيه، وما الذي ينشره الاستدعاء وما الذي يُخفيه، وكيف تقرأ الإيصال، والحدود التي ستصادفها. كل برنامج شيفرة عامة، وكل استدعاء إثبات. تضم Example programs تطبيقات كاملة، وتضم randprotocol/example-programs مصدر البرامج المنشورة على chain 20، ومنها برامج RPL-2 (أدناه).
ما البرنامج
البرنامج شيفرة آلة RV32IM لـ zkVM الخاصة بـ Rand: { base_pc: u32, words: Vec<u32> }، بحد أقصى هو سقف البرنامج في السلسلة (65,535 كلمة على chain 20، كما في كل سلسلة منذ chain 13؛ و4,096 في genesis بلا max_program_words، مثل chain 12)، على أن يكون base_pc من مضاعفات 4، وأن تُفكَّك كل كلمة إلى تعليمة (الكلمة التي لا تُفكَّك تُرفض عند النشر، لا عند التشغيل). وترفض chain 20، مثل chain 19، نشر برنامج لا يمكن لأي استدعاء أن يحتويه: أكثر من 8,180 كلمة بلا مقطع عام، أو أقل منها مع مقطع عام (أدناه). وهو معنوَن بمحتواه وغير قابل للتغيير:
program_id = blake3("rand-program" || base_pc || words) // the id, with no public input
program_id = blake3("rand-program-2", base_pc || u32_le(len(words)) || words || u32_le(len(public)) || public) // deployed with --public
code_hash = hc = Poseidon2 digest of (base_pc, len, words) // what calls are verified against
الاستدعاء هو إثبات STARK بأن البرنامج ذا الملخّص hc شُغّل على مدخلات خاصة، ولم يستهلك أكثر من حدّ غاز مُعلَن، ونشر ثماني كلمات مخرجات. تتحقق العقدة من الإثبات، وتسجّل إيصالًا، ولا يحدث شيء آخر: المخرجات بيانات، لا آثار. لا يستطيع الاستدعاء تحريك RAND؛ فالمستدعي هو من ينقل القيمة في الحزمة التي تدفع ثمن الاستدعاء، وتعني مخرجات البرنامج ما يتفق عليه البرنامج ومستدعيه. وعلى chain 20 يشكّل Invoke استثناءً: فإثباته يضمن انتقال حالة مُعلَنًا، ثم يطبّقه السجل (Program state (RPL-2)).
ما يستطيع الضيف فعله:
| المجموعة | التعليمات |
|---|---|
| العليا / القفزات | LUI AUIPC JAL JALR |
| الفروع | BEQ BNE BLT BGE BLTU BGEU |
| التحميل / التخزين | LW SW LB LH LBU LHU SB SH |
| ALU | ADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ADD SUB SLL SLT SLTU XOR SRL SRA OR AND |
| RV32M | MUL MULH MULHU MULHSU DIV DIVU REM REMU |
| النظام | ECALL؛ ولا FENCE ولا CSR ولا EBREAK ولا امتدادات A أو C أبدًا |
استدعاءات النظام، بحسب الرقم في a7:
| # | استدعاء النظام | الأثر |
|---|---|---|
| 0 | HALT | ينهي التشغيل |
| 1 | WRITE_OUTPUT slot word | يثبّت مخرجًا عامًا في الخانة slot < 8؛ كل خانة مرة واحدة على الأكثر، والخانات غير المكتوبة أصفار |
| 2 | READ_INPUT idx | يعيد كلمة المدخل الخاص idx، المرتبطة بالالتزام المملَّح H_IN |
| 3 | POSEIDON2 ptr n | يهشّ n ≤ 4096 كلمة في مكانها بإسفنجة Poseidon2 |
| 4 | KECCAK ptr | تبديل Keccak-f[1600] واحد في مكانه؛ ويجعل الإثبات يحمل جدول keccak |
| 5 | SHA256 ptr | ضغطة SHA-256 واحدة في مكانها؛ وتجعل الإثبات يحمل جدول sha256 |
| 6 | READ_PUBLIC idx | يعيد كلمة المقطع العام idx؛ وعلى chain 20 يُثبَّت المقطع العام عند النشر (rand program deploy --public)؛ وفي genesis بلا max_program_public_words يكون فارغًا ولا يقرأ هذا الاستدعاء شيئًا |
| 7 | POSEIDON2_LEN ptr n | POSEIDON2 مع ربط طول الرسالة بالإسفنجة، فيختلف تهشيم [a] عن [a, 0] (مجموعة القيود 7، chain 16 وما بعدها). ولا يزال rand-guest check عند circuits aeacf31 يرفضه (القسم التالي) |
الذاكرة: 32 سجلًّا يكون فيها x0 صفرًا؛ وذاكرة وصول عشوائي تبدأ من الصفر؛ وكل عنوان أدنى من 2³⁰؛ ومكدّس بحجم 64 KiB للضيوف المترجَمة. يحمل البرنامج المبني بـ rand-guest بياناته للقراءة فقط والمهيَّأة في حاوية الصورة، ومعها مقدّمة محمِّل هي جزء من البرنامج تكتب تلك البيانات قبل أن تعمل شيفرة البرنامج نفسها (The RISC-V ISA). أما التزامات الملاحظات والمُبطِلات وفحوص Merkle فليست استدعاءات نظام، بل روتينات مكتبة فوق POSEIDON2.
منذ chain 18 (مجموعة القيود 8) يُقاس كل تشغيل بالغاز. التعليمة العادية والاستدعاءات 0 و1 و2 و6 تكلّف 1 غاز لكل منها، وPOSEIDON2 على n كلمة يكلّف 3 + 3 × ⌈n / 4⌉، وتبديل KECCAK واحد يكلّف 192، وضغطة SHA256 واحدة تكلّف 64. تضم Example programs الجدول وما يعلنه الاستدعاء.
بناء برنامج
ثلاث طرق، تنتج كلها كلمات RV32IM:
المجمِّع المدمج. تأتي المحفظة بخمسة ضيوف مجمَّعة يدويًا، وتجمّعها إلى ملف JSON بصيغة { "base_pc", "words" }:
rand program build --guest private_payment --arg 1000 --out pp.json # prints the program id
rand program build --guest balance_check --arg 1000 --out bc.json
# guests: fib, memcpy, bubble_sort, balance_check, private_payment, public_echo, rpl2_counter (v0.6.8); --arg is repeatable
البرامج المترجَمة، بـ rand-guest. يبني rand-guest في مستودع circuits حزمة Rust من نوع no_std (riscv32im-unknown-none-elf، Rust 1.98.1) أو دليل C (clang 23.1.1) إلى حاوية صورة، ويفحص كل كلمة بمفكِّك الآلة، ويطبع hc ومعرّف البرنامج. البناء مغلق، فيعطي المصدر نفسه hc نفسه على أي جهاز. لسلاسل 18 إلى 20 ابنِ سلسلة الأدوات عند إيداع circuits رقم aeacf31 (الفرع feat/v067-circuits)، وهو الإيداع الذي تثبّته fullnode v0.6.7 وv0.6.8 كلتاهما. وعند ذلك الإيداع يسرد rand-guest check الاستدعاءات من 0 إلى 6 فقط، فيُرفض عند البناء ضيف يستدعي guest_sdk::poseidon2_len (الاستدعاء 7) مع أن الآلة تشغّله (zkp-circuits issue #1). الخطوات بالتفصيل: Deploy a Rust program وDeploy a RISC-V program.
البرامج المنقولة. يترجم sbpf2rv ملف Solana sBPF ELF، ويترجم evm2rv شيفرة EVM التنفيذية، إلى برامج تساوي مخرجاتها مخرجات ضيوف المفسِّر في Rand كلمة بكلمة: The Solana transpiler وThe Ethereum transpiler.
وليس في المحفظة نفسها مترجِم. يقبل rand program deploy ملف .json، أو ملف .bin خامًا من كلمات little-endian، أو حاوية الصورة التي يكتبها rand-guest (fullnode v0.3 وما بعدها).
النشر
rand program deploy pp.json # a bundle pays the floor; waits for the commit; prints the id
rand program show <id> # id, base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest
الـ testnet هي chain 20، ويحتاج النشر أو الاستدعاء أو Invoke عليها إلى المحفظة rand من fullnode v0.6.8 أو أحدث. ولا تستطيع محفظة أقدم الإرسال هناك إطلاقًا: فـ genesis الخاصة بـ chain 20 تضبط binding_domain: 1، فيتضمن كل ربط للإثبات وكل رسالة موقَّعة تجزئة genesis، بينما تحمل روابط محفظة v0.6.7 معرّف السلسلة وحده. تخدم نقطة النهاية العامة https://rpc.randprotocol.org مزامنة المحفظة وعمليات النشر والاستدعاء (الإعداد).
تطبع المحفظة أولًا معرّف البرنامج وعدد الكلمات وhc، وتسأل العقدة عن الرسوم بـ rand_estimateFee، الذي يرفض البرنامج الذي يتجاوز سقف السلسلة قبل أي إثبات. ثم تبني حزمة تحويل ذاتي بقيمة صفر (حزمة الأصول المخفية الواحدة بأربعة مداخل وأربعة مخارج التي تحملها كل معاملة؛ وتُخصم الرسوم من خانات RAND فيها) تدفع 0.001 RAND + 100,000 units × words من ملاحظاتها نفسها (وتُحتسب كلمات المقطع العام ضمن كلمات الشيفرة)، وترفق بها Action::Deploy { base_pc, words, public }، وتُثبت الحزمة مقابل ربط المعاملة وترسلها. منذ chain 17 يعني ذلك إثباتين، وتطبع المحفظة سطرًا لكل منهما: تفويض إنفاق من المستوى 10 (ثوانٍ، نحو 1.37 MB) وحزمة المستوى 14 (نحو دقيقتين على حاسوب محمول، نحو 1.5 MB). وقبل chain 17 كان إثبات الحزمة وحده، نحو 100 s ونحو 1.42 MB على chain 15. ولا يتضمن رسم النشر حدّ غاز ولا حدّ بايتات: فالغاز يسعّر الاستدعاءات فقط. تتحقق العقدة من أن كل كلمة تُفكَّك، وتحسب hc، وتخزّن السجل، وتبدأ بناء مفتاح المُتحقِّق في الخلفية. الشيفرة عامة؛ أما من دفع ثمنها فلا — فليس في rand_getProgram حقل deployer. ونشر الكلمات نفسها مرتين لا يغيّر شيئًا.
→ {"method":"rand_getProgram","params":["675adeea…0065"]}
← { "id": "675adeea…0065", "base_pc": 0, "words_len": 42, "code_hash": "…", "deployed_at": 10,
"public_words_len": 0, "public_digest": null }
→ {"method":"rand_getProgramCode","params":["675adeea…0065"]}
← { "base_pc": 0, "words": [ … ] }
الاستدعاء
rand call <program id> --input 400 --input 250 --input 300 --input 75
rand receipt <tx hash>
تجلب المحفظة الشيفرة والمقطع العام من العقدة وتتحقق من أنهما يُهشَّان إلى المعرّف. وتشغّل الاستدعاء في المحاكي وتطبع الغاز والرسوم قبل أن تُثبت أي شيء:
gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)
ثم تختم سجل المدخلات (أدناه)، وتُثبت الاستدعاء محليًا وفق ملف FRI الخاص بالسلسلة مع المدخلات بوصفها المتجه الخاص، وتُثبت الحزمة التي تدفع الرسوم، وتُرسل، وتنتظر، وتطبع الإيصال. وعلى chain 20 يُربط إثبات الاستدعاء بمعاملته الخاصة (ربط يتضمن تجزئة genesis) وتكون الحزمة إثباتين، فيصير الاستدعاء ثلاثة إثباتات: الاستدعاء، وتفويض الإنفاق من المستوى 10، وحزمة المستوى 14. السطران أعلاه من استدعاء التصويت المجهول على chain 18 بتاريخ 2026-09-30 (An anonymous vote).
الرسوم. في السلسلة 20 يكون الحد الأدنى BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉، حيث BUNDLE_BASE يساوي 0.001 RAND، والبايتات هي إثبات الاستدعاء مع غلاف المدخلات، وبدأت الأسعار عند 100 و800 وحدة (الوحدة 10⁻⁹ RAND) وتتحرك مع امتلاء الكتل. تضع السلسلة 20 سقفًا لها عند 10,000 و80,000 وحدة، ولا يُحتسب في حِملها إلا البايتات التي تدفع سعر البايت (إثباتات الاستدعاءات وأغلفتها) (byte_load: "paying")؛ أما في السلسلتين 18 و19 فلم يكن للأسعار سقف، وكانت بايتات كل معاملة تحرّك سعر البايت. أما جدول دفتر الأستاذ الثابت 0.001 RAND + call_fee(tier)، وهو القاعدة في السلسلة 15، فلا ينطبق هناك. تحدد المحفظة الرسوم قبل أن يوجد الإثبات، لذا تسعّر الإثبات عند سقف السلسلة البالغ 4 MiB وتضيف هامشًا بخطوتين سعريتين. كلّفت أربعة استدعاءات مقيسة على السلسلة 18 ما بين 0.0057 و0.0075 RAND لكل منها (الحساب). الأمر rand fee call <tier> --gas <limit> --bytes <B> يسأل العقدة عن الحد الأدنى نفسه. يقرأ private_payment أربعة أرصدة خاصة، وإذا بلغ مجموعها عتبته على الأقل نشر الفائض: المخرجات [1, 0, 25, 0, 0, 0, 0, 0]. ترى السلسلة الإثبات والمستوى وهذه الكلمات الثماني؛ ولا ترى الأرصدة أبدًا.
الخيارات: --tier T يفرض مستوى؛ --gas-limit <N|max> يعلن حدًا دقيقًا للغاز أو سقف المستوى بدلًا من الافتراضي، ويُقرَّب الغاز الدقيق إلى أعلى مضاعف لـ 2^(tier − 2)؛ --fee <RAND> يحدد الرسوم؛ --expect-public <FILE> يرفض قبل توليد الإثبات ما لم يكن المقطع العام للبرنامج هو كلمات ذلك الملف؛ --auditor <rand1…> يختم السجل أيضًا لمدقق مسمّى؛ --print-call-key يطبع مفتاح الاستدعاء؛ --no-envelope لا ينشر أي سجل؛ --cuda يختار واجهة GPU الخلفية (لم تُجرَّب على العتاد، ولا تتوافق مع ختم غلاف). ترفض السلسلة 20 الخيار --cuda في الاستدعاء رفضًا قاطعًا: إثبات الاستدعاء المرتبط بمعاملته يُنتَج على واجهة CPU الخلفية.
الإيصالات
يُخزَّن إيصال لكل استدعاء مثبَّت، ويُعاد حسابه حين تتزامن العقدة أو تتحقق من سلسلتها:
→ {"method":"rand_getReceipt","params":["b082…09b0"]}
← { "tx": "b082…09b0", "program": "675adeea…0065", "tier": 14,
"outputs": [1, 0, 25, 0, 0, 0, 0, 0], "height": 105, "index": 0, "h_in": "9c0e…7f", "h_pub": null }
h_in هو التزام الإثبات المملَّح بالمدخلات الخاصة؛ وهو بمفرده لا يكشف شيئًا. h_pub هو ملخّص المقطع العام للبرنامج المحدد وقت النشر، أي public_digest للبرنامج، أو null للبرنامج المنشور دون مقطع عام. لا يوجد حقل effect، والحد المعلن للغاز ليس في الإيصال. يعرض المستكشف الكائن نفسه باسم receipt على GET /transactions/:hash لمعاملة call، ويسرد استدعاءات البرنامج على GET /programs/:id، ويرشّح GET /transactions?kind=call&program=<id>.
أغلفة المدخلات: الكشف عن استدعاء لاحقًا
افتراضيًا ينشر rand call سجل (salt, inputs) للاستدعاء مختومًا في غلاف مدخلات الاستدعاء، بحيث يمكن إظهار المدخلات لاحقًا دون إثبات ثانٍ:
| الجزء | البايتات | ما هو |
|---|---|---|
body | 12 + 16 + 4·n + 16 | salt ‖ inputs تحت مفتاح جديد لكل استدعاء K_call |
to_sender | 60 | K_call ملفوفًا بمفتاح العرض الصادر للمستدعي |
kem_ct | 1,088 أو 0 | تغليف ML-KEM-768 موجّه إلى المدقق، وهو فارغ عند عدم وجود مدقق |
to_auditor | 60 أو 0 | K_call ملفوفًا تحت سر ذلك التغليف |
البيانات المرافقة للجزء body هي H_IN، فالغلاف المنقول إلى استدعاء آخر لا يُفتح لأحد. تتحقق السلسلة من الحجم فقط (على الأكثر max_call_envelope_bytes: 65,536 بايت في السلسلة 20، و18,432 في genesis بلا هذا الحقل) وتقدّمه كما هو عبر rand_getCallEnvelope(tx). ثلاثة مفاتيح تفتحه ولا غير: مفتاح عرض المستدعي (كل استدعاء أجرته تلك المحفظة)، ومفتاح الاستدعاء (ذلك الاستدعاء وحده)، ومفتاح عرض المدقق (ذلك الاستدعاء وحده).
rand open-call <txhash> # as the caller
rand open-call <txhash> --as-auditor # as the auditor
rand open-call <txhash> --call-key <hex> # with the per-call key alone
يفتح open-call السجل، ويتحقق من أن input_digest(salt, inputs) == H_IN، ويعيد تشغيل البرنامج على المدخلات المستعادة عبر المحاكي، ويقارن المخرجات بمخرجات الإيصال — وينتهي برمز غير صفري إن فشل أيٌّ من الفحصين، فلا يخلط سكربت بين تحذير وإقرار. الخيار --no-envelope لا رجعة فيه: متى ضاع الملح لم يعد بوسع أحد، ولا المستدعي نفسه، فتح ذلك الاستدعاء.
منذ v0.6.8 تجري إعادة التشغيل على المقطع العام نفسه الذي صُنع عليه الإثبات: كلمات البرنامج العامة المحددة وقت النشر، وتحت hardening_v6 ربط الاستدعاء ذو الكلمات الثماني بعدها (المسألة رقم 116 في fullnode). في v0.6.7 كان open-call يعمل فقط للبرنامج المنشور دون مقطع عام؛ أما للبرنامج الذي له مقطع فكان يطبع المدخلات ثم يتوقف مع re-running the program on these inputs failed: PublicIndex(0)، وينتهي برمز غير صفري دون تشغيل أيٍّ من الفحصين.
حالة البرنامج (RPL-2)
السلسلة 20 هي أول سلسلة فيها RPL-2، وتُفعَّل بقسم program_state في genesis الخاص بها. يحصل البرنامج على ثلاثة أشياء لم تكن له (fullnode docs/program-state.md، وcrates/randprotocol-core/src/ledger/program_state.rs في v0.6.8):
| الخلايا | خريطة من مفتاح من 8 كلمات إلى قيمة من 8 كلمات، لكل برنامج. الخلية الغائبة تُقرأ ثماني أصفار؛ وكتابة ثمانية أصفار تزيلها. مثبَّتة في جذر الحالة |
| خزنة | رصيد عام لكل أصل (0 هو RAND). تدخل القيمة عبر حزمة invoke وتخرج مذكرات تحسبها السلسلة |
| رمز خاص به | الرمز المسجَّل بـ rand token create --program <id> (سلطة السك Program(id)، دون عرض أولي) لا يُسك ولا يُحرق إلا عبر استدعاءات invoke لذلك البرنامج |
الخلايا وأرصدة الخزنة عامة، كعرض الرمز. وليس فيها شيء من المذكرات.
Invoke (Action 33) استدعاء يعلن أيضًا انتقالًا، ويُظهر إثبات استدعائه أن البرنامج يقبل هذا الانتقال بالضبط:
Invoke { program, proof, input_envelope, transition }
Transition { reads: [{ key, value }] // cells read, with the values read; keys strictly ascending
writes: [{ key, value }] // cells written; keys strictly ascending
inflow: none | deposit | burn
pays: [{ asset, amount, recipient, r, envelope }] // notes out of the vault
mints: [{ asset, amount, recipient, r, envelope }] } // new units of the program's own token
الداخل هو ما تحرقه الحزمة: burn_r هو RAND يُودَع في الخزنة؛ وburn_a من burn_asset رمز يُودَع (تدفق deposit) أو يُتلَف (تدفق burn، لرمز البرنامج نفسه فقط). والخارج مذكرة واحدة تحسبها السلسلة لكل دفعة، الدفعات أولًا ثم السكّ، مع كون المبلغ وpk المستلم عامَّين، كما في السكّ. لا يطبّق دفتر الأستاذ الانتقال إلا إذا كان البرنامج موجودًا، والشكل مشروعًا، وكل خلية في reads لا تزال تحمل القيمة المعلنة، والخزنة (بعد إيداع هذه المعاملة نفسها) تغطي كل دفعة، وكل سكّ هو لرمز البرنامج نفسه، والإثباتات سليمة. القراءة التي لم تعد مطابقة تُرفض بوصف StaleRead؛ وتعيد المحفظة القراءة والإثبات.
كيف يختلف invoke عن call.
Call (RPL-1) | Invoke (RPL-2) | |
|---|---|---|
| المقطع العام الذي يُصنع الإثبات عليه | public ‖ call_binding | public ‖ call_binding ‖ context، أي الانتقال في صورة كلمات |
| ما تفعله السلسلة عند النجاح | تخزّن إيصالًا؛ والمخرجات بيانات | تخزّن الإيصال وتطبّق الانتقال: تكتب الخلايا، وتحرّك الخزنة، وتُلحق مذكرات الدفعات |
| القيمة | تحويل الحزمة نفسه فقط | إيداعات في الخزنة عبر حروق الحزمة؛ وتخرج منها الدفعات والسكّ |
| الاعتماد على الحالة | لا | الإثبات لا يعتمد (فالمقطع يُبنى من المعاملة)، لكن فحص القراءة يعتمد، عند القبول وفي المجمّع |
| الرسوم | حد الاستدعاء الأدنى | حد الاستدعاء الأدنى مضافًا إليه cell_fee لكل خلية تُنشأ |
يقرأ البرنامج كلمة السياق i بـ READ_PUBLIC(public_len + 8 + i). الكلمة 0 هي الإصدار (1)، والكلمات من 1 إلى 4 أعداد القراءات والكتابات والدفعات وعمليات السكّ، ثم burn_r ونوع التدفق وburn_asset وburn_a، ثم كل قراءة وكتابة مفتاحًا وقيمة (8 كلمات لكل منهما)، ثم كل دفعة أصلًا ومبلغًا. المستلمون ليسوا في السياق: البرنامج يقرر المبالغ، والربط يحدد من يُدفع له. على البرنامج أن يقيّد كل كلمة سياق تُعرض عليه، وألا يكون فيه مسار يُحدث panic، لأن معالج panic في guest-sdk يوقف التنفيذ، والتنفيذ الموقوف تنفيذ يمكن إثباته.
حدود السلسلة 20 (rand_getLimits.program_state):
| الحد | السلسلة 20 |
|---|---|
cell_fee | 10,000,000 وحدة، أي 0.01 RAND، لكل خلية تُنشأ (خلية تُكتب بقيمة غير صفرية حيث لم يكن مخزَّنًا شيء)؛ وإعادة كتابة خلية أو حذفها مجانية |
max_reads | 8 خلايا |
max_writes | 8 خلايا |
max_payouts | 4 مذكرات، الدفعات وعمليات السكّ معًا |
قاعدة المقطع عادةً أشد: يجب أن يتسع المقطع كله لجدول المدخلات العامة الذي يُصنع عليه استدعاء للبرنامج نفسه، أي 127 كلمة لبرنامج بلا مدخلات عامة. يترك ذلك 119 كلمة سياق، مثلًا ثلاث قراءات وثلاث كتابات وأربع دفعات.
الأوامر وRPC (المحفظة v0.6.8):
rand program build --guest rpl2_counter --out counter.json # one cell, incremented by one per invoke
rand program invoke <id> --transition step.json [--input N | --inputs-file F.json]
rand program state <id> [--cell <64 hex>] # rand_getProgramCell / rand_getProgramCells
rand program vault <id> # rand_getProgramVault
يفحص program invoke كل مدخل في reads مقابل الخلية الحية قبل أن يُنتج الإثبات، وينتهي بالرمز 3 عند قراءة قديمة، قبل الإثبات أو عند التضمين، فيعيد السكربت القراءة ويحاول ثانية. يسعّر rand_estimateFee مع "kind": "invoke" الرسوم. صيغة ملف الانتقال في docs/cli.md لدى fullnode. البرامج الكاملة من RPL-2 المنشورة على السلسلة 20 (amm وstableswap وorderbook وstablecoin وlending وperp وcrowdfund وstoploss)، وبرامج RPL-1 (sealed-auction وballot وeligibility وprivate-join وcredit-score)، في randprotocol/example-programs.
ما هو علني وما هو مخفي
| علني | مخفي |
|---|---|
معرّف البرنامج وشيفرته؛ الطبقة (tier)؛ حد الغاز المعلن؛ المخرجات الثمانية؛ H_IN؛ الرسوم والحقول العلنية للحزمة؛ حجم المغلّف؛ هل استخدم الاستدعاء Keccak أم SHA-256، وبأي تكرار تقريبًا (تعلن ترويسة الإثبات ارتفاع كل جدول تجزئة) | المدخلات الخاصة؛ السجلات والذاكرة والتفرعات؛ العدد الفعلي للدورات والغاز الدقيق؛ ملح H_IN؛ من أجرى الاستدعاء وبماذا دفع |
يختلف الإثباتان لتشغيل واحد في البايتات. لم يكن ذلك كافيًا في السلسلة 15 وما قبلها: فالاستدعاء ذو 31 كلمة خاصة أو أقل كان يثبت جدولًا أصغر من أن يخفي شيئًا، وكان يمكن استرجاع مدخلاته من الإثبات. ومنذ الإصدار v0.6 يرفع المُثبِت (prover) حدًا أدنى لهذه الجداول قدره 128 صفًا، وتعمّي مجموعة القيود 7 (السلسلة 16 وما بعدها) مجاميع الجداول التي كانت لا تزال تسرّب. hc ملزِم لكنه غير مُخفٍ: فكل من يستطيع إحصاء البرامج المرشحة يستطيع التأكد من أيها نُشر.
ما ليس ممكنًا بعد
كما تصرّح به العقدة الكاملة (fullnode):
- لا استدعاءات بين البرامج، ولا حالة خارج RPL-2. الاستدعاء
Callإثبات واحد على تشغيل واحد؛ ولا ينتقل شيء من استدعاء إلى آخر إلا ما يعيده المستدعي بنفسه. في السلسلة 20 يقرأInvokeخلايا البرنامج وخزنته ويكتب فيها (أعلاه)؛ ولا حالة برنامج في أي سلسلة قبل 20، ولا يستطيع الاستدعاءInvokeلمس خلايا برنامج آخر. - لا تحويلات يقودها البرنامج خارج الخزنة. نوع الأثر 1، أي التحويل الذي يدفعه البرنامج إلى حساب، حُذف مع الحسابات، ومخرجات
Callبيانات تقريرية. في السلسلة 20 يدفعInvokeمن خزنة البرنامج نفسه، بما لا يزيد على أربع ملاحظات، وتكون المبالغ والمستلمون علنيين. - لا مترجم داخل المحفظة. البرامج هي الضيوف المجمَّعة المدمجة، أو صور تُبنى باستخدام
rand-guestفي مستودع الدارات (circuits). - المدخل العلني يُثبَّت عند النشر لا عند كل استدعاء. أضافت مجموعة القيود 6 كلًّا من
READ_PUBLICوH_PUB. في السلسلة 20 يجوز أن يحمل النشر حتى 32,768 كلمة علنية، ويربطها معرّف البرنامج، ويُتحقَّق من إثبات كل استدعاء مقابل خلاصتها. ولا كلمات علنية لكل استدعاء. - استدعاءات التجزئة تنتج إثباتات كبيرة. يبلغ حجم الإثبات الحامل لـ keccak نحو 3.2 MB، أي أكثر من سقف الإثبات الافتراضي 2 MiB بوضوح؛ ويضيف الإثبات الحامل لـ sha256 نحو 400 KB. سقف السلسلة 20 هو 4,194,304 بايت (4 MiB، كما في السلسلتين 17 إلى 19؛ وكان 8 MiB في السلاسل 13 إلى 16)، وهو يتسع للحالتين: ففي السلسلة 18 بلغ استدعاء من الطبقة 12 مع جدول Keccak 3.35 MB، واستدعاء من الطبقة 14 مع جدول SHA-256 1.91 MB (Example programs). ويُقيَّد الاستدعاء كذلك بـ 128 تبديلة Keccak-f و128 عملية ضغط SHA-256 (أدناه)، ويُحتسب كل KiB من الإثبات بسعر البايت.
- برامج EVM وSolana لا تُنشر في السلسلة 20، واستدعاءاتها فوق سقف طبقة الاستدعاء. ضيوف المفسّر (
evm.bin، 18,009 كلمة؛ وsbpf.bin، 8,317) والصور المترجمة (ERC-20، 11,686 كلمة؛ وSPL Token، 65,096) أكبر من السقف الافتراضي البالغ 4,096 كلمة وأقل من 65,535 التي حددتها السلسلة 13 وأبقت عليها كل سلسلة بعدها. نُشرت الصورتان المترجمتان في السلسلة 13 بتاريخ 2026-09-19، وSPL Token مع ملف ELF الخاص بها مدخلًا علنيًا. أما السلسلتان 14 و15 فقد أُنشئتا من جديد، ولم تُعد أي من الصورتين إلى النشر على أيٍّ منهما. السلسلة 20، كالسلسلة 19، ترفض نشر أي من الأربعة: فبموجب قاعدتهاhardening_v6يُرفض عند النشر (ProgramUncallable) كل برنامج لا يستطيع أي استدعاء من الطبقة 14 أن يحتويه، أي ما يزيد على 8,180 كلمة بلا مقطع علني، وأقل من ذلك مع مقطع علني، حتى لا تُدفع رسوم نشر لبرنامج لا يستدعيه أحد. يُثبَت استدعاء ERC-20 في الطبقة 16 أو 18: فقد استُدعيapproveمن الطبقة 16 في السلسلة 13 بتاريخ 2026-09-19 بإثبات إنتاجي حجمه 3,412,405 بايت (Ethereum transpiler)، وترفض السلسلة 20 أي استدعاء فوق الطبقة 14 (البند التالي)، فلا يوجد اليوم استدعاء ERC-20 مقبول فيها. واستدعاءات SPL Token من الطبقة 20، وهي تحتاج إلى نحو 330 GB؛ ولا يوجد إثبات استدعاء لـ SPL (proving memory). وتفشل شيفرات سياق الكتلة في EVM بفخ (trap)، ولا توجد حالة EVM على السلسلة. - يُثبَت الاستدعاء في الطبقة 14 أو أدنى. منذ الإصدار v0.5.6 من العقدة الكاملة (2026-09-25) يُرفض إثبات استدعاء فوق الطبقة 14 (
CallTierTooHigh)، وكذلك كل إثبات يتجاوز 128 تبديلة Keccak-f أو 128 عملية ضغط SHA-256. كانapproveالخاص بـ ERC-20 من الطبقة 16 في السلسلة 13 سيُرفض في السلسلة 20، كما كان سيُرفض في السلاسل 15 و18 و19، وكذلك كل استدعاء ERC-20 من الطبقة 18 أو SPL Token من الطبقة 20؛ وتظل هذه تُثبَت خارج السلسلة (the budget). - لم يعمل إثبات GPU على عتاد حقيقي قط. يحتاج
--cudaإلى بناء بالخيار--features cudaوإلى PTX مُلتزَم به؛ ولا يوجد أيٌّ منهما على الأسطول. في السلسلة 20 يُنتَج إثبات الاستدعاء على واجهة CPU أيًّا كان البناء. hcغير مُخفٍ، كما سبق، وما زال طلب الشاهد (witness) يخبر العقدة بأي ورقة تنفقها المحفظة.