Rand Protocol
دستاویزات
RISC-V پروگرام

RISC-V پروگرام ڈپلائے کریں

Rust کے بغیر ڈپلائے کے قابل امیج تک پہنچنے کے دو راستے: ایک C پروگرام جسے rand-guest مقررہ clang کے ساتھ بناتا ہے، اور ایک ہاتھ سے لکھا گیا پروگرام جسے اسمبل کر کے پیک کیا جاتا ہے، یا بائٹ بہ بائٹ امیج کنٹینر میں پیک کیا جاتا ہے۔

کوئی بھی پروگرام جو RV32IM مشینی کوڈ ہو، deploy کیا جا سکتا ہے، خواہ اسے کسی بھی چیز نے تیار کیا ہو۔ یہ صفحہ دو راستے دکھاتا ہے: C بذریعہ rand-guest build --lang c، اور ایک ایسا پروگرام جو آپ assembly میں لکھیں اور خود pack کریں۔ دونوں اسی image container پر ختم ہوتے ہیں جو Rust پروگرام deploy کریں تیار کرتا ہے، اور دونوں rand program deploy سے deploy ہوتے ہیں۔ پہلے circuits checkout اور rand-guest تیار کریں، جیسا کہ اس صفحے کے مرحلہ 1 میں ہے۔ chains 18 تا 20 کے لیے checkout کمٹ aeacf31 پر ہے، جسے fullnode v0.6.7 اور v0.6.8 دونوں pin کرتے ہیں۔ نیچے دیے گئے نتائج 2026-09-19 کو 7ef3220 پر حاصل کیے گئے تھے اور دوبارہ نہیں چلائے گئے۔ rand-guest ڈائریکٹری، بشمول guest.h اور rt.c، دونوں کمٹس پر یکساں ہے۔

clang کے ذریعے C

آپ کو کیا درکار ہے

ٹولورژنطریقہ
clang23.1.1، RISC-V backend کے ساتھmacOS پر brew install llvm؛ Apple کے سسٹم clang میں RISC-V backend نہیں ہے۔ یا $CLANG کو 23.1.1 کی کسی binary پر سیٹ کریں
rust-lldRust 1.98.1 سےrustup +1.98.1 component add llvm-tools

rand-guest ہر اس clang کو مسترد کر دیتا ہے جس کا ورژن 23.1.1 نہ ہو۔ RAND_GUEST_CLANG_UNPINNED=1 کسی دوسرے ورژن سے build کرتا ہے، اس انتباہ کے ساتھ کہ hc شائع شدہ images سے مطابقت نہیں رکھے گا۔

guest لکھیں

C guest کی ڈائریکٹری میں صرف .c فائلیں ہوتی ہیں (اور اختیاری طور پر ایک .ld)۔ guest.h، _start اور ایک مختصر runtime rand-guest فراہم کرتا ہے۔ اپنی guest.h یا start.S شامل نہ کریں: build --lang c ایسی ڈائریکٹری کو مسترد کر دیتا ہے جس میں ان میں سے کوئی بھی ہو۔

یہ کمٹ شدہ guests-compiled/c-fib/fib.c ہے:

#include "guest.h"

static uint32_t fib(uint32_t n) {
    uint32_t a = 0, b = 1;
    for (uint32_t i = 0; i < n; i++) {
        uint32_t c = a + b;
        a = b;
        b = c;
    }
    return a;
}

void main(void) {
    rand_write_output(0, fib(rand_read_input(0)));
    rand_halt();
}

guest.h میں rand_read_input، rand_read_public، rand_write_output، rand_poseidon2، rand_keccak، rand_sha256_compress اور rand_halt کا اعلان ہے۔ اس میں syscall 7 کے لیے کوئی wrapper نہیں، یعنی طول کی حد والا POSEIDON2_LEN جو مشین constraint set 7 سے چلا رہی ہے، اور aeacf31 پر rand-guest check ایسے ecall کو مسترد کر دیتا ہے جس میں a7 = 7 ہو (zkp-circuits issue #1)۔ لہٰذا chain 20 کے لیے C یا assembly guest صرف syscalls 0 تا 6 استعمال کرتا ہے۔

build، run، inspect

$ rand-guest build --lang c guests-compiled/c-fib --out c-fib.bin
25 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK
wrote c-fib.bin and its .sha256 (25 words, hc 76e723cb4c218f9a7f657f39b9f95ee2353d8fbeb601aaea394afa29928f3aee, program id c213cf5c94679cab4585ed1e81b61736a7d2fe5605dc7884e4acec4144665999)

$ rand-guest run c-fib.bin --input 20 --tier 10
out[0] = 6765
out[1] = 0
out[2] = 0
out[3] = 0
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 0
cycles 116
tier 10
tier 10: fits (cycles 126 of 1023, Poseidon2 permutations 10 of 128)

fib(20) = 6765، وہی جواب جو Rust کے fib guest کا ہے۔

build --lang c ڈائریکٹری کی ہر .c فائل کو (ترتیب وار)، ساتھ میں شامل start.S اور rt.c سمیت، ان فلیگز کے ساتھ compile کرتا ہے:

clang --no-default-config --target=riscv32-unknown-none-elf -march=rv32im -mabi=ilp32 -mno-relax
      -nostdlib -ffreestanding -fno-builtin -ffunction-sections -fdata-sections -Os
      -ffile-prefix-map=<checkout>=/rand-circuits -c

اور انہیں pin شدہ Rust toolchain کے rust-lld -flavor gnu --gc-sections -T <ld> سے link کرتا ہے۔ --gc-sections صرف وہی رکھتا ہے جسے guest کال کرتا ہے۔

C runtime

-ffreestanding -fno-builtin clang کو لائبریری فنکشنز پہچاننے سے روکتے ہیں، مگر ان کی کالز پیدا کرنے سے نہیں۔ struct کی نقل memcpy بن جاتی ہے، بڑا صفر initialiser memset بن جاتا ہے، اور run-time قدر سے 64-bit تقسیم ایک helper کال بن جاتی ہے، کیونکہ RV32IM صرف 32-bit قدروں کو تقسیم کرتا ہے۔ rt.c یہ آٹھ فنکشنز متعین کرتا ہے:

فنکشناستعمال
memcpy، memmove، memset، memcmpنقل، بھرائی، موازنہ
__udivdi3، __umoddi3، __divdi3، __moddi364-bit / اور %

یہ byte loops اور shift-subtract تقسیم ہیں۔ یہ سست ہیں: کسی hot loop میں نقل اور تقسیم 32-bit words میں کریں۔

ہاتھ سے لکھا گیا پروگرام

آپ مشینی کوڈ خود بھی لکھ سکتے ہیں: اسے assemble کریں، اسی linker script سے link کریں، اور ELF کو pack کریں۔ یہ پروگرام دو نجی inputs جمع کرتا ہے اور حاصلِ جمع شائع کرتا ہے۔

add.S:

# Reads private inputs 0 and 1, writes their sum to output slot 0, halts.
    .section .text._start
    .globl _start
_start:
    li   a7, 2          # READ_INPUT
    li   a0, 0
    ecall               # a0 = input 0
    mv   t0, a0
    li   a7, 2          # READ_INPUT
    li   a0, 1
    ecall               # a0 = input 1
    add  t1, t0, a0
    li   a7, 1          # WRITE_OUTPUT slot, word
    li   a0, 0
    mv   a1, t1
    ecall
    li   a7, 0          # HALT
    ecall

ہر syscall نمبر اس کے ecall سے عین پہلے li a7, n کے ذریعے سیٹ کریں۔ اس طرح checker نمبر کی تصدیق کر سکتا ہے۔

pin شدہ clang سے assemble کریں، rust-lld اور guest-sdk/guest.ld سے link کریں، پھر pack کریں۔ circuits checkout کے اندر سے چلائیں:

CLANG=/opt/homebrew/opt/llvm/bin/clang
LLD=$(rustc +1.98.1 --print sysroot)/lib/rustlib/$(rustc +1.98.1 -vV | sed -n 's/host: //p')/bin/rust-lld
$CLANG --no-default-config --target=riscv32-unknown-none-elf -march=rv32im -mabi=ilp32 -mno-relax -c add.S -o add.o
$LLD -flavor gnu -T guest-sdk/guest.ld add.o -o add.elf
$ rand-guest pack add.elf --out add.bin
wrote add.bin (80 bytes) and add.bin.sha256

$ rand-guest check add.bin
14 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK

$ rand-guest run add.bin --input 40 --input 2
out[0] = 42
out[1] = 0
out[2] = 0
out[3] = 0
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 0
cycles 14
tier 10

$ rand-guest info add.bin
form: image container
text 14 words at 0x1000; data 0 words (0 non-zero) at 0x0; prologue 0 words; program 14 words from base_pc 0x1000
hc 42986ac1f93396d7de75f6757149523e7f1e9c5999d748af9b1915ada8ee14bd
program id 3b34c9d16adbd81a4849148675a914d10e5da0bd5b6aa64967e5f8a5fc75d941
14 words against a cap of 4096: fits

pack ELF کے .text کو text segment کے طور پر لیتا ہے اور ہر دوسرے allocated PROGBITS section کو ایک data segment کے طور پر۔ .bss چھوڑ دیا جاتا ہے، کیونکہ RAM صفر سے شروع ہوتی ہے۔

container کو ہاتھ سے pack کرنا

image container چھ header words اور پھر خود words پر مشتمل ہوتا ہے (RISC-V ISA)۔ آپ اسے کسی toolchain کے بغیر لکھ سکتے ہیں۔ یہ Python اسکرپٹ وہی 80 bytes لکھتا ہے جو اوپر pack نے لکھے تھے:

import struct

text = [
    0x00200893,  # li   a7, 2      READ_INPUT
    0x00000513,  # li   a0, 0
    0x00000073,  # ecall           a0 = input 0
    0x00050293,  # mv   t0, a0
    0x00200893,  # li   a7, 2      READ_INPUT
    0x00100513,  # li   a0, 1
    0x00000073,  # ecall           a0 = input 1
    0x00a28333,  # add  t1, t0, a0
    0x00100893,  # li   a7, 1      WRITE_OUTPUT
    0x00000513,  # li   a0, 0      slot 0
    0x00030593,  # mv   a1, t1     word
    0x00000073,  # ecall
    0x00000893,  # li   a7, 0      HALT
    0x00000073,  # ecall
]
#          magic ("RAND")  version  text_base  n_text     data_base  n_data
header = [0x444E4152,      1,       0x1000,    len(text), 0,         0]
with open("add-hand.bin", "wb") as f:
    f.write(struct.pack(f"<{len(header) + len(text)}I", *header, *text))

rand-guest info add-hand.bin وہی hc 42986ac1…14bd پرنٹ کرتا ہے، اور cmp add-hand.bin add.bin کوئی فرق نہیں پاتا۔ ہاتھ سے pack کی گئی image پر ہمیشہ rand-guest check چلائیں: node ہر اس word کو مسترد کر دیتا ہے جو decode نہ ہو، مگر صرف deploy کے وقت۔

wallet دو پرانی شکلیں بھی deploy کرتا ہے: ایک .json فائل { "base_pc", "words" }، اور little-endian words کی خام .bin جو base 0 پر لوڈ ہوتی ہے۔ container واحد شکل ہے جس میں data segment ہوتا ہے۔

deploy اور call

اوپر کا ہر راستہ ایک ہی طرح ختم ہوتا ہے۔ fullnode v0.3 یا بعد کے rand wallet کے ساتھ (chain 20 پر، جو live chain ہے، v0.6.8 یا بعد کا: v0.6.7 کا wallet وہاں بھیج نہیں سکتا، کیونکہ chain 20 ہر proof اور دستخط کو اپنے genesis hash سے باندھتی ہے؛ دیکھیں Rust پروگرام deploy کریں):

rand program deploy add.bin
rand call <program id> --input 40 --input 2

deploy fee 0.001 RAND + 0.0001 RAND × words ہے: add.bin کے 14 words کے لیے 0.0024 RAND اور c-fib.bin کے 25 کے لیے 0.0035 RAND۔ دونوں 4,096 words سے کم ہیں، اس لیے کسی بھی chain کی حد میں آتے ہیں: پہلے سے طے شدہ 4,096 اور chain 20 کی 65,535۔ call مقامی طور پر tier 10 پر prove ہوتی ہے۔ chain 15 پر اس کی لاگت 0.002 RAND تھی۔ chain 20 پر fee اب 0.001 + call_fee(tier) نہیں رہی: کم از کم حد BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉ ہے، rand call prove کرنے سے پہلے gas: <exact> (declaring <limit>, tier <t>) اور fee پرنٹ کرتا ہے، اور chain 18 پر ناپی گئی چار calls میں سے ہر ایک کی لاگت 0.0057 سے 0.0075 RAND رہی (مثالی پروگرام)۔ chain 20 پر ہر deploy اور call کے ساتھ bundle کی جانب کے دو proofs بھی ہوتے ہیں، ایک tier-10 spend authorisation اور tier-14 bundle۔ Rust پروگرام deploy کریں بیان کرتا ہے کہ ہر کمانڈ کیا کرتی ہے۔

دوسروں کو یہ جانچنے کا موقع دینے کے لیے کہ آپ نے کیا deploy کیا، سورس اور اپنا circuits کمٹ شائع کریں (chains 18 تا 20 کے لیے aeacf31)۔ clang 23.1.1 اور Rust 1.98.1 کے ساتھ، rand-guest build کسی بھی مشین پر وہی hc دیتا ہے۔

آخری بار 2026-10-12 کو fullnode کمٹ c0878c3f (چین 21)، اور circuits `7ef3220` (`rand-guest/README.md`, `guests-compiled/c-fib`, `guest-sdk/guest.ld`); every `rand-guest` output on this page was produced on 2026-09-19, circuits `aeacf31` (`rand-guest/guest.h`, `rand-guest/src/check.rs`; `git diff 7ef3220 aeacf31 -- guest-sdk rand-guest`), read on 2026-09-30; no output on this page was re-run at that commit, fullnode tag v0.6.8 `c9c9bd3c` (`docs/deploy.md` on `binding_domain`, `.github/workflows/ci.yml` circuits pin) and `rand_getLimits` on chain 20, read on 2026-10-01; nothing on this page was deployed or called on chain 20, fullnode tag v0.8.0 `c0878c3f` (`CHANGELOG.md`, `docs/zkvm.md` §8a) and `rand_getLimits` on chain 21, read on 2026-10-12; nothing on this page was deployed or called on chain 21 کے مقابل تصدیق کی گئی۔ جہاں کوڈ اور یہ صفحہ ایک دوسرے سے مختلف ہوں، وہاں کوڈ درست ہے؛ ہمیں مطلع کریں۔