Rand Protocol
दस्तावेज़ीकरण
मार्गदर्शिकाएँ

प्रोग्राम

गोपनीय प्रोग्राम मॉडल - zkVM में सिद्ध किया गया एक RISC-V गेस्ट, जिसे एक बार डिप्लॉय किया जाता है और निजी इनपुट के साथ कॉल किया जाता है - साथ में build, deploy, call, invoke और receipt कमांड, चेन 20 पर RPL-2 प्रोग्राम स्टेट, और उन चीज़ों की एक ईमानदार सूची जो अभी संभव नहीं हैं।

इस पृष्ठ का उपयोग किसी प्रोग्राम को चेन पर रखने और उसे कॉल करने के लिए करें: प्रोग्राम क्या है, उसे कैसे बनाया जाता है, एक कॉल क्या प्रकाशित करती है और क्या छिपाती है, रसीद को वापस कैसे पढ़ा जाता है, और वे सीमाएँ जिनसे आपका सामना होगा। हर प्रोग्राम सार्वजनिक कोड है; हर कॉल एक प्रमाण है। उदाहरण प्रोग्राम में पूर्ण अनुप्रयोग हैं, और randprotocol/example-programs में chain 20 पर डिप्लॉय किए गए प्रोग्रामों का स्रोत है, जिनमें RPL-2 वाले प्रोग्राम भी शामिल हैं (नीचे)।

प्रोग्राम क्या है

प्रोग्राम Rand zkVM के लिए RV32IM मशीन कोड है: { base_pc: u32, words: Vec<u32> }, जो अधिकतम चेन की प्रोग्राम सीमा तक हो सकता है (chain 20 पर 65,535 words, जैसा कि chain 13 के बाद से हर चेन पर है; max_program_words के बिना वाले genesis पर, जैसे chain 12, 4,096), base_pc 4 का गुणज हो, और हर word किसी निर्देश के रूप में डिकोड हो (जो word डिकोड नहीं होता उसे डिप्लॉय के समय ही अस्वीकार कर दिया जाता है, रन टाइम पर कभी नहीं)। chain 20, chain 19 की तरह, ऐसे प्रोग्राम को भी डिप्लॉय करने से मना करती है जिसे कोई कॉल समा न सके: सार्वजनिक खंड न होने पर 8,180 से अधिक words, और सार्वजनिक खंड होने पर इससे कम (नीचे)। यह सामग्री-आधारित पते वाला और अपरिवर्तनीय है:

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 वाला प्रोग्राम कुछ निजी इनपुट पर चला, उसने घोषित गैस सीमा से अधिक खर्च नहीं किया, और आठ आउटपुट words प्रकाशित किए। नोड प्रमाण को सत्यापित करता है, एक रसीद दर्ज करता है, और इसके अलावा कुछ नहीं होता: आउटपुट डेटा हैं, प्रभाव नहीं। कॉल RAND को स्थानांतरित नहीं कर सकती; कॉल करने वाला उस बंडल में मूल्य स्थानांतरित करता है जो कॉल का भुगतान करता है, और प्रोग्राम के आउटपुट का वही अर्थ होता है जिस पर प्रोग्राम और उसका कॉलर सहमत हों। chain 20 पर Invoke इसका अपवाद है: उसका प्रमाण एक घोषित स्टेट संक्रमण की पुष्टि करता है, जिसे लेजर फिर लागू करता है (प्रोग्राम स्टेट (RPL-2))।

guest क्या कर सकता है:

समूहनिर्देश
upper / जंपLUI AUIPC JAL JALR
ब्रांचBEQ BNE BLT BGE BLTU BGEU
लोड / स्टोरLW SW LB LH LBU LHU SB SH
ALUADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ADD SUB SLL SLT SLTU XOR SRL SRA OR AND
RV32MMUL MULH MULHU MULHSU DIV DIVU REM REMU
सिस्टमECALL; कभी नहीं: FENCE, CSR, EBREAK, A या C एक्सटेंशन

Syscall, a7 में संख्या के अनुसार:

#syscallप्रभाव
0HALTरन समाप्त करता है
1WRITE_OUTPUT slot wordसार्वजनिक आउटपुट slot < 8 को स्थिर करता है; हर स्लॉट अधिकतम एक बार, न लिखे गए स्लॉट शून्य होते हैं
2READ_INPUT idxनिजी इनपुट word idx लौटाता है, जो salted कमिटमेंट H_IN से बँधा होता है
3POSEIDON2 ptr nPoseidon2 sponge से n ≤ 4096 words को उसी स्थान पर हैश करता है
4KECCAK ptrउसी स्थान पर एक Keccak-f[1600] permutation; प्रमाण में keccak तालिका जुड़वाता है
5SHA256 ptrउसी स्थान पर एक SHA-256 compression; प्रमाण में sha256 तालिका जुड़वाता है
6READ_PUBLIC idxसार्वजनिक खंड का word idx लौटाता है; chain 20 पर सार्वजनिक खंड डिप्लॉय के समय तय होता है (rand program deploy --public); max_program_public_words के बिना वाले genesis पर यह खाली होता है और यह कुछ नहीं पढ़ता
7POSEIDON2_LEN ptr nPOSEIDON2, जिसमें संदेश की लंबाई sponge में बाँधी जाती है, ताकि [a] और [a, 0] के हैश भिन्न हों (constraint set 7, chain 16 और उसके बाद)। circuits aeacf31 पर rand-guest check अब भी इसे अस्वीकार करता है (अगला खंड)

मेमोरी: 32 रजिस्टर, जिनमें x0 शून्य है; RAM जो शून्य से शुरू होती है; हर पता 2³⁰ से नीचे; संकलित guests के लिए 64 KiB स्टैक। rand-guest से बना प्रोग्राम अपना केवल-पठनीय और आरंभिक डेटा image container में रखता है, और प्रोग्राम का हिस्सा एक loader prologue प्रोग्राम का अपना कोड चलने से पहले वह डेटा लिखता है (The RISC-V ISA)। नोट कमिटमेंट, nullifier और Merkle जाँचें syscall नहीं हैं, बल्कि POSEIDON2 पर आधारित लाइब्रेरी रूटीन हैं।

chain 18 (constraint set 8) से हर रन को गैस में मापा जाता है। एक सामान्य निर्देश और syscall 0, 1, 2 और 6 में से प्रत्येक की लागत 1 गैस है, n words पर POSEIDON2 की लागत 3 + 3 × ⌈n / 4⌉ है, एक KECCAK permutation की लागत 192 और एक SHA256 compression की लागत 64 है। उदाहरण प्रोग्राम में तालिका है और यह भी कि एक कॉल क्या घोषित करती है।

प्रोग्राम बनाना

तीन तरीके, सभी RV32IM words बनाते हैं:

अंतर्निहित असेंबलर। वॉलेट के साथ हाथ से असेंबल किए गए पाँच guests आते हैं, और वह उन्हें { "base_pc", "words" } JSON फ़ाइल में असेंबल करता है:

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 से संकलित प्रोग्राम। circuits रिपॉज़िटरी का rand-guest एक no_std Rust crate (riscv32im-unknown-none-elf, Rust 1.98.1) या एक C डायरेक्टरी (clang 23.1.1) को image container में बनाता है, हर word को मशीन के डिकोडर से जाँचता है, और hc तथा प्रोग्राम id प्रिंट करता है। बिल्ड hermetic है, इसलिए एक ही स्रोत किसी भी मशीन पर एक ही hc देता है। chain 18 से 20 के लिए toolchain को circuits commit aeacf31 (branch feat/v067-circuits) पर बनाएँ, वही commit जिसे fullnode v0.6.7 और v0.6.8 दोनों पिन करते हैं। उस commit पर rand-guest check केवल syscall 0 से 6 सूचीबद्ध करता है, इसलिए जो guest guest_sdk::poseidon2_len (syscall 7) कॉल करता है उसे बिल्ड के समय अस्वीकार कर दिया जाता है, हालाँकि मशीन उसे चलाती है (zkp-circuits issue #1)। चरण-दर-चरण: Rust प्रोग्राम डिप्लॉय करें और RISC-V प्रोग्राम डिप्लॉय करें।

अनूदित प्रोग्राम। sbpf2rv एक Solana sBPF ELF का, और evm2rv EVM runtime bytecode का, ऐसे प्रोग्रामों में अनुवाद करते हैं जिनके आउटपुट Rand के interpreter guests के आउटपुट से word-दर-word मेल खाते हैं: Solana transpiler और Ethereum transpiler।

वॉलेट में स्वयं कोई कंपाइलर नहीं है। rand program deploy एक .json, little-endian words की कच्ची .bin, या rand-guest द्वारा लिखा गया image container स्वीकार करता है (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 करने के लिए fullnode v0.6.8 या उसके बाद का rand वॉलेट चाहिए। पुराना वॉलेट वहाँ कुछ भी नहीं भेज सकता: chain 20 का genesis binding_domain: 1 सेट करता है, इसलिए हर प्रमाण binding और हर हस्ताक्षरित संदेश में genesis hash शामिल होता है, जबकि v0.6.7 वॉलेट की bindings में केवल chain id होती है। सार्वजनिक endpoint https://rpc.randprotocol.org वॉलेट के sync, डिप्लॉय और कॉल को सेवा देता है (सेटअप)।

वॉलेट पहले प्रोग्राम id, words की संख्या और hc प्रिंट करता है, और rand_estimateFee से नोड से शुल्क पूछता है, जो किसी भी प्रमाण-निर्माण से पहले ही चेन की सीमा से बड़े प्रोग्राम को अस्वीकार कर देता है। फिर वह एक शून्य-मूल्य self-transfer बंडल बनाता है (वह एक 4-in-4-out hidden-asset बंडल जो हर लेनदेन में होता है; शुल्क उसके RAND स्लॉट से निकलता है) जो अपने ही नोटों से 0.001 RAND + 100,000 units × words का भुगतान करता है (सार्वजनिक खंड के words कोड words के रूप में गिने जाते हैं), Action::Deploy { base_pc, words, public } जोड़ता है, लेनदेन की binding के विरुद्ध बंडल का प्रमाण बनाता है और सबमिट करता है। chain 17 से ये दो प्रमाण हैं, और वॉलेट प्रत्येक के लिए एक पंक्ति प्रिंट करता है: tier-10 व्यय प्राधिकरण (कुछ सेकंड, लगभग 1.37 MB) और tier-14 बंडल (लैपटॉप पर लगभग दो मिनट, लगभग 1.5 MB)। chain 17 से पहले केवल बंडल प्रमाण होता था, chain 15 पर लगभग 100 s और लगभग 1.42 MB। डिप्लॉय शुल्क में गैस या बाइट का कोई घटक नहीं है: गैस केवल कॉल का मूल्य तय करती है। नोड जाँचता है कि हर word डिकोड होता है, hc की गणना करता है, रिकॉर्ड संग्रहीत करता है, और पृष्ठभूमि में verifier key बनाना शुरू करता है। कोड सार्वजनिक है; उसका भुगतान किसने किया, यह नहीं — rand_getProgram में कोई deployer फ़ील्ड नहीं है। एक ही words को दो बार डिप्लॉय करना idempotent है।

→ {"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>

वॉलेट नोड से कोड और सार्वजनिक खंड प्राप्त करता है और जाँचता है कि उनका हैश id के बराबर है। वह कॉल को emulator में चलाता है और कुछ भी प्रमाणित करने से पहले गैस और शुल्क प्रिंट करता है:

gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)

इसके बाद वह इनपुट transcript को सील करता है (नीचे), चेन की FRI प्रोफ़ाइल के तहत इनपुट को निजी vector मानकर कॉल का प्रमाण स्थानीय रूप से बनाता है, शुल्क चुकाने वाले बंडल का प्रमाण बनाता है, सबमिट करता है, प्रतीक्षा करता है, और रसीद प्रिंट करता है। chain 20 पर कॉल प्रमाण अपने ही लेनदेन से बँधा होता है (एक binding जिसमें genesis hash शामिल है) और बंडल दो प्रमाण है, इसलिए एक कॉल तीन प्रमाण है: कॉल, tier-10 व्यय प्राधिकरण और tier-14 बंडल। ऊपर की दो पंक्तियाँ 2026-09-30 को chain 18 पर गुमनाम मतदान की कॉल से हैं (गुमनाम मतदान)।

शुल्क। chain 20 पर न्यूनतम शुल्क BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉ है, जहाँ BUNDLE_BASE 0.001 RAND है, बाइट्स से आशय कॉल प्रमाण और इनपुट लिफ़ाफ़े के बाइट्स से है, और कीमतें 100 और 800 इकाइयों से शुरू हुईं (एक इकाई 10⁻⁹ RAND है) तथा ब्लॉक कितना भरा है, उसके अनुसार बदलती हैं। chain 20 इन्हें 10,000 और 80,000 इकाइयों पर सीमित करती है, और इसके लोड में केवल वे बाइट्स गिने जाते हैं जो बाइट कीमत चुकाते हैं (कॉल के प्रमाण और लिफ़ाफ़े) (byte_load: "paying"); chains 18 और 19 पर कीमतों की कोई ऊपरी सीमा नहीं थी और हर लेनदेन के बाइट्स बाइट कीमत को प्रभावित करते थे। लेजर की स्थिर 0.001 RAND + call_fee(tier) अनुसूची, जो chain 15 का नियम थी, वहाँ लागू नहीं होती। वॉलेट प्रमाण बनने से पहले ही शुल्क तय कर देता है, इसलिए वह प्रमाण का मूल्य चेन की 4 MiB सीमा पर आँकता है और अतिरिक्त गुंजाइश के लिए दो मूल्य-चरण जोड़ता है। chain 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 बैकएंड चुनता है (हार्डवेयर पर अपरीक्षित, और लिफ़ाफ़ा सील करने के साथ असंगत)। chain 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 फ़ील्ड नहीं है, और घोषित गैस सीमा रसीद में नहीं होती। एक्सप्लोरर किसी call के लिए यही ऑब्जेक्ट GET /transactions/:hash पर receipt के रूप में दिखाता है, GET /programs/:id पर किसी प्रोग्राम की कॉलें सूचीबद्ध करता है, और GET /transactions?kind=call&program=<id> से फ़िल्टर करता है।

इनपुट लिफ़ाफ़े: किसी कॉल को बाद में प्रकट करना

डिफ़ॉल्ट रूप से rand call कॉल का (salt, inputs) ट्रांसक्रिप्ट एक कॉल-इनपुट लिफ़ाफ़े में सील करके प्रकाशित करता है, ताकि इनपुट बाद में दूसरे प्रमाण के बिना दिखाए जा सकें:

भागबाइट्सयह क्या है
body12 + 16 + 4·n + 16एक नई प्रति-कॉल कुंजी K_call के अंतर्गत salt ‖ inputs
to_sender60कॉलर की आउटगोइंग व्यूइंग कुंजी के लिए रैप की गई K_call
kem_ct1,088 या 0लेखा-परीक्षक के लिए एक ML-KEM-768 एनकैप्सुलेशन, लेखा-परीक्षक न होने पर रिक्त
to_auditor60 या 0उस एनकैप्सुलेशन के रहस्य के अंतर्गत रैप की गई K_call

बॉडी का संबद्ध डेटा H_IN है, इसलिए किसी दूसरी कॉल पर उठाकर रखा गया लिफ़ाफ़ा किसी के लिए नहीं खुलता। चेन केवल आकार जाँचती है (अधिकतम max_call_envelope_bytes: chain 20 पर 65,536 बाइट्स, उस फ़ील्ड के बिना वाले जेनेसिस पर 18,432) और इसे 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 के अंतर्गत, उनके बाद आठ-शब्दों वाला कॉल बाइंडिंग (fullnode issue #116)। v0.6.7 में open-call केवल सार्वजनिक खंड के बिना डिप्लॉय किए गए प्रोग्राम के लिए काम करता था; सार्वजनिक खंड वाले प्रोग्राम के लिए वह इनपुट प्रिंट करता था और फिर re-running the program on these inputs failed: PublicIndex(0) के साथ रुक जाता था, और दोनों में से कोई भी जाँच चलाए बिना शून्येतर कोड के साथ बाहर निकल जाता था।

प्रोग्राम स्टेट (RPL-2)

chain 20 RPL-2 वाली पहली चेन है, जिसे उसके जेनेसिस का program_state खंड सक्रिय करता है। किसी प्रोग्राम को तीन ऐसी चीज़ें मिलती हैं जो पहले उसके पास नहीं थीं (fullnode docs/program-state.md, v0.6.8 पर crates/randprotocol-core/src/ledger/program_state.rs):

सेलप्रति प्रोग्राम, 8-शब्द कुंजी से 8-शब्द मान तक का एक मानचित्र। अनुपस्थित सेल आठ शून्यों के रूप में पढ़ा जाता है; आठ शून्य लिखने से वह हट जाता है। स्टेट रूट में प्रतिबद्ध
एक वॉल्टप्रति एसेट एक सार्वजनिक शेष-राशि (0 RAND है)। मूल्य किसी invoke के बंडल के माध्यम से आता है और चेन द्वारा गणना किए गए नोटों के रूप में बाहर जाता है
अपना एक टोकनrand token create --program <id> से पंजीकृत टोकन (मिंट प्राधिकार Program(id), कोई प्रारंभिक आपूर्ति नहीं) केवल उसी प्रोग्राम के invokes द्वारा मिंट और बर्न किया जाता है

सेल और वॉल्ट की शेष-राशियाँ सार्वजनिक हैं, किसी टोकन की आपूर्ति की तरह। इनमें कुछ भी नोट नहीं है।

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_asset का burn_a जमा किया गया (inflow deposit) या नष्ट किया गया (inflow burn, केवल प्रोग्राम का अपना टोकन) टोकन है। जो बाहर जाता है, वह प्रति भुगतान चेन द्वारा गणना किया गया एक नोट है, पहले pays फिर mints, जिसमें राशि और प्राप्तकर्ता का pk सार्वजनिक होते हैं, जैसे किसी मिंट में होते हैं। लेजर संक्रमण तभी लागू करता है जब प्रोग्राम मौजूद हो, संरचना वैध हो, reads का हर सेल अब भी घोषित मान रखता हो, वॉल्ट (इस लेनदेन की अपनी जमा के बाद) हर pay को कवर करता हो, हर mint प्रोग्राम के अपने टोकन का हो, और प्रमाण सत्यापित हों। जो read अब मेल नहीं खाता, उसे StaleRead के रूप में अस्वीकार किया जाता है; वॉलेट दोबारा पढ़ता है और दोबारा प्रमाण बनाता है।

invoke किसी कॉल से कैसे भिन्न है।

Call (RPL-1)Invoke (RPL-2)
वह सार्वजनिक खंड जिस पर प्रमाण बनता हैpublic ‖ call_bindingpublic ‖ call_binding ‖ context, शब्दों के रूप में संक्रमण
सफल होने पर चेन क्या करती हैरसीद संग्रहीत करती है; आउटपुट डेटा हैंरसीद संग्रहीत करती है और संक्रमण लागू करती है: सेल लिखती है, वॉल्ट में मूल्य स्थानांतरित करती है, भुगतान-नोट जोड़ती है
मूल्यकेवल बंडल का अपना स्थानांतरणबंडल के बर्न के माध्यम से वॉल्ट में जमा; उसमें से भुगतान और मिंट
स्टेट पर निर्भरतानहींप्रमाण निर्भर नहीं करता (खंड लेनदेन से बनता है), लेकिन read जाँच करती है, प्रवेश के समय और पूल में
शुल्ककॉल का न्यूनतम शुल्ककॉल का न्यूनतम शुल्क और साथ में बनाए गए प्रति सेल cell_fee

कोई प्रोग्राम context शब्द i को READ_PUBLIC(public_len + 8 + i) से पढ़ता है। शब्द 0 संस्करण (1) है, शब्द 1 से 4 क्रमशः reads, writes, pays और mints की संख्याएँ, फिर burn_r, inflow का प्रकार, burn_asset और burn_a, फिर प्रत्येक read और write कुंजी और मान के रूप में (प्रत्येक 8 शब्द), फिर प्रत्येक भुगतान एसेट और राशि के रूप में। प्राप्तकर्ता context में नहीं होते: राशियाँ प्रोग्राम तय करता है, किसे भुगतान होगा यह बाइंडिंग तय करती है। प्रोग्राम को उसे दिखाए गए हर context शब्द को बाधित करना चाहिए, और उसमें panic करने वाला कोई पथ नहीं होना चाहिए, क्योंकि guest-sdk का panic handler निष्पादन रोक देता है और रुका हुआ निष्पादन भी प्रमाणित किया जा सकने वाला निष्पादन है।

chain 20 की सीमाएँ (rand_getLimits.program_state):

सीमाchain 20
cell_fee10,000,000 इकाइयाँ, 0.01 RAND, प्रति बनाए गए सेल (ऐसा सेल जिसे शून्येतर लिखा गया हो जहाँ पहले कुछ संग्रहीत नहीं था); किसी सेल को दोबारा लिखना या हटाना निःशुल्क है
max_reads8 सेल
max_writes8 सेल
max_payouts4 नोट, pays और mints मिलाकर

खंड का नियम प्रायः अधिक कड़ा होता है: पूरा खंड उस सार्वजनिक तालिका में समाना चाहिए जिस पर उसी प्रोग्राम की किसी कॉल का प्रमाण बनता है, यानी बिना सार्वजनिक इनपुट वाले प्रोग्राम के लिए 127 शब्द। इससे 119 context शब्द बचते हैं, उदाहरण के लिए तीन reads, तीन writes और चार भुगतान।

कमांड और RPC (wallet 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 प्रविष्टि को सेल के वर्तमान मान से जाँचता है, और पुराने (stale) read पर, चाहे प्रमाण बनाने से पहले हो या समावेशन के समय, कोड 3 के साथ बाहर निकलता है, ताकि स्क्रिप्ट दोबारा पढ़कर फिर प्रयास करे। "kind": "invoke" के साथ rand_estimateFee शुल्क बताता है। संक्रमण फ़ाइल का प्रारूप fullnode के docs/cli.md में है। chain 20 पर डिप्लॉय किए गए पूर्ण RPL-2 प्रोग्राम (amm, stableswap, orderbook, stablecoin, lending, perp, crowdfund, stoploss), और RPL-1 प्रोग्राम (sealed-auction, ballot, eligibility, private-join, credit-score), randprotocol/example-programs में हैं।

क्या सार्वजनिक है और क्या छिपा है

सार्वजनिकछिपा हुआ
प्रोग्राम id और उसका कोड; tier; घोषित gas सीमा; आठ आउटपुट; H_IN; bundle का शुल्क और सार्वजनिक फ़ील्ड; envelope का आकार; कॉल ने Keccak का उपयोग किया या SHA-256 का, और लगभग कितनी बार (proof header प्रत्येक hash तालिका की ऊँचाई घोषित करता है)निजी इनपुट; रजिस्टर, मेमोरी, ब्रांच; वास्तविक cycle संख्या और सटीक gas; H_IN salt; इसे किसने कॉल किया और भुगतान किससे किया

एक ही रन के दो प्रूफ़ अलग-अलग बाइट होते हैं। chain 15 और उससे पहले यह पर्याप्त नहीं था: 31 या उससे कम निजी words वाली कॉल ऐसी तालिका प्रूव करती थी जो छिपाने के लिए बहुत छोटी थी, और उसके इनपुट प्रूफ़ से पुनः प्राप्त किए जा सकते थे। v0.6 से प्रूवर ने उन तालिकाओं को न्यूनतम 128 पंक्तियों पर निर्धारित कर दिया है, और constraint set 7 (chain 16 और उसके बाद) उन प्रति-तालिका योगों को ब्लाइंड करता है जो तब भी लीक होते थे। hc बाध्यकारी (binding) है, पर छिपाने वाला (hiding) नहीं: जो कोई संभावित प्रोग्रामों को गिन सकता है, वह पुष्टि कर सकता है कि कौन-सा डिप्लॉय किया गया था।

अभी क्या संभव नहीं है

जैसा fullnode इसे बताता है:

  • कोई cross-program कॉल नहीं, और RPL-2 के बाहर कोई state नहीं। एक Call एक रन पर एक प्रूफ़ है; कॉलर जो वापस देता है उसके अतिरिक्त कुछ भी एक कॉल से दूसरी कॉल तक नहीं जाता। chain 20 पर एक Invoke प्रोग्राम के अपने cells और vault को पढ़ता और लिखता है (ऊपर); chain 20 से पहले की किसी chain पर प्रोग्राम state नहीं है, और एक invoke किसी दूसरे प्रोग्राम के cells को छू नहीं सकता।
  • vault के बाहर प्रोग्राम-संचालित कोई ट्रांसफ़र नहीं। Effect kind 1, यानी किसी खाते को प्रोग्राम द्वारा भुगतान किया गया ट्रांसफ़र, खातों के साथ हटा दिया गया था, और एक Call के आउटपुट कथन (statements) होते हैं। chain 20 पर एक Invoke प्रोग्राम के अपने vault से भुगतान करता है, अधिकतम चार नोट, जिनकी राशियाँ और प्राप्तकर्ता सार्वजनिक होते हैं।
  • वॉलेट में कोई कंपाइलर नहीं। प्रोग्राम या तो अंतर्निहित assembled guests होते हैं, या circuits रिपॉज़िटरी में rand-guest से बनाए गए images।
  • सार्वजनिक इनपुट डिप्लॉय के समय निर्धारित होता है, प्रति कॉल नहीं। Constraint set 6 ने READ_PUBLIC और H_PUB जोड़े। chain 20 पर एक डिप्लॉय में अधिकतम 32,768 सार्वजनिक words हो सकते हैं, प्रोग्राम id उन्हें बाँधता है, और हर कॉल का प्रूफ़ उनके digest के विरुद्ध जाँचा जाता है। प्रति-कॉल कोई सार्वजनिक words नहीं होते।
  • Hash syscalls बड़े प्रूफ़ बनाते हैं। Keccak वाला प्रूफ़ लगभग 3.2 MB का होता है, जो डिफ़ॉल्ट 2 MiB प्रूफ़ सीमा से सीधे अधिक है; sha256 वाला प्रूफ़ लगभग 400 KB जोड़ता है। chain 20 की सीमा 4,194,304 बाइट है (4 MiB, जैसा chains 17 से 19 पर; chains 13 से 16 पर यह 8 MiB थी), जो दोनों को स्वीकार करती है: chain 18 पर Keccak तालिका वाली tier-12 कॉल 3.35 MB मापी गई और SHA-256 तालिका वाली tier-14 कॉल 1.91 MB (उदाहरण प्रोग्राम)। एक कॉल 128 Keccak-f permutations और 128 SHA-256 compressions तक भी सीमित है (नीचे देखें), और प्रूफ़ के प्रत्येक KiB पर बाइट मूल्य के अनुसार शुल्क लगता है।
  • EVM और Solana प्रोग्राम chain 20 पर डिप्लॉय नहीं होते, और उनकी कॉलें call-tier सीमा से ऊपर हैं। Interpreter guests (evm.bin, 18,009 words; sbpf.bin, 8,317) और अनूदित images (ERC-20, 11,686 words; SPL Token, 65,096) डिफ़ॉल्ट 4,096-word सीमा से ऊपर हैं और उस 65,535 से नीचे हैं जो chain 13 ने निर्धारित की और उसके बाद की हर chain ने बनाए रखी। दोनों अनूदित images 2026-09-19 को chain 13 पर डिप्लॉय किए गए थे, SPL Token अपने ELF को सार्वजनिक इनपुट के रूप में लेकर। Chains 14 और 15 नए सिरे से शुरू की गईं, और उनमें से किसी पर भी कोई image पुनः डिप्लॉय नहीं किया गया। chain 20, chain 19 की तरह, इन चारों में से किसी को भी डिप्लॉय करने से मना करती है: इसके hardening_v6 नियम के अंतर्गत ऐसा प्रोग्राम जिसे कोई tier-14 कॉल समा न सके, यानी सार्वजनिक segment के बिना 8,180 से अधिक words वाला और सार्वजनिक segment के साथ इससे कम वाला, डिप्लॉय के समय अस्वीकार कर दिया जाता है (ProgramUncallable), ताकि ऐसे प्रोग्राम के लिए डिप्लॉय शुल्क न चुकाया जाए जिसे कोई कॉल नहीं कर सकता। एक ERC-20 कॉल tier 16 या 18 पर प्रूव होती है: tier-16 approve को 2026-09-19 को chain 13 पर 3,412,405-बाइट के production प्रूफ़ के साथ कॉल किया गया था (Ethereum transpiler), और chain 20 tier 14 से ऊपर की किसी भी कॉल को अस्वीकार करती है (अगला बिंदु), इसलिए आज वहाँ कोई ERC-20 कॉल स्वीकार्य नहीं है। SPL Token कॉलें tier 20 की होती हैं, जिसके लिए लगभग 330 GB चाहिए; कोई SPL कॉल प्रूफ़ मौजूद नहीं है (proving मेमोरी)। EVM के block-context opcodes trap करते हैं, और कोई on-chain EVM state नहीं है।
  • एक कॉल tier 14 या उससे नीचे प्रूव होती है। fullnode v0.5.6 (2026-09-25) से tier 14 से ऊपर का कॉल प्रूफ़ अस्वीकार कर दिया जाता है (CallTierTooHigh), और 128 से अधिक Keccak-f permutations या 128 से अधिक SHA-256 compressions वाला प्रूफ़ भी। chain 13 का tier-16 ERC-20 approve chain 20 पर अस्वीकार कर दिया जाएगा, जैसे chains 15, 18 और 19 पर किया जाता, और इसी तरह हर tier-18 ERC-20 या tier-20 SPL Token कॉल भी; ये अब भी off chain प्रूव होती हैं (बजट)।
  • GPU proving कभी हार्डवेयर पर नहीं चली है। --cuda के लिए --features cuda build और committed PTX चाहिए; fleet पर इनमें से कोई भी मौजूद नहीं है। chain 20 पर कॉल प्रूफ़ CPU backend पर बनता है, build चाहे जो भी हो।
  • hc छिपाने वाला (hiding) नहीं है, जैसा ऊपर बताया गया है, और एक witness अनुरोध अब भी node को बता देता है कि वॉलेट कौन-सा leaf खर्च करता है।

2026-10-12 को fullnode कमिट c0878c3f (चेन 21) के विरुद्ध अंतिम बार सत्यापित। जहाँ कोड और यह पृष्ठ असहमत हों, वहाँ कोड सही है; हमें बताएँ।