Rand Protocol
مستندات
برنامه‌های RISC-V

استقرار یک برنامه RISC-V

دو مسیر برای رسیدن به یک ایمیج قابل استقرار بدون Rust - برنامه‌ای به زبان C که rand-guest با clang پین‌شده می‌سازد، و برنامه‌ای دست‌نویس که اسمبل و بسته‌بندی می‌شود، یا بایت‌به‌بایت در کانتینر ایمیج بسته‌بندی می‌شود.

هر برنامه‌ای که کد ماشین RV32IM باشد قابل استقرار است، فارغ از اینکه چه چیزی آن را تولید کرده باشد. این صفحه دو مسیر را نشان می‌دهد: C از طریق rand-guest build --lang c، و برنامه‌ای که خودتان به زبان اسمبلی می‌نویسید و خودتان بسته‌بندی می‌کنید. هر دو به همان کانتینر تصویری ختم می‌شوند که استقرار یک برنامه Rust تولید می‌کند، و هر دو با rand program deploy مستقر می‌شوند. ابتدا چک‌اوت circuits و rand-guest را راه‌اندازی کنید، مانند گام ۱ در آن صفحه. برای زنجیره‌های 18 تا 20 این چک‌اوت روی کامیت aeacf31 است که fullnode نسخه‌های v0.6.7 و v0.6.8 هر دو به آن پین شده‌اند. خروجی‌های زیر در 2026-09-19 روی 7ef3220 تولید شده‌اند و دوباره اجرا نشده‌اند. پوشه rand-guest، با guest.h و rt.c، در هر دو کامیت یکسان است.

C با clang

آنچه نیاز دارید

ابزارنسخهروش
clang23.1.1، با بک‌اند RISC-Vbrew install llvm در macOS؛ clang سیستمی Apple بک‌اند RISC-V ندارد. یا $CLANG را روی یک باینری 23.1.1 تنظیم کنید
rust-lldاز Rust 1.98.1rustup +1.98.1 component add llvm-tools

rand-guest هر clang را که نسخه‌اش 23.1.1 نباشد رد می‌کند. RAND_GUEST_CLANG_UNPINNED=1 با نسخه‌ای دیگر build می‌کند، همراه با هشداری که می‌گوید hc با تصویرهای منتشرشده یکسان نخواهد بود.

نوشتن guest

پوشه guest به زبان C فقط فایل‌های .c (و یک .ld اختیاری) دارد. rand-guest فایل‌های guest.h، _start و یک runtime کوچک را فراهم می‌کند. 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 را اعلام می‌کند. برای فراخوانی سیستمی 7 هیچ wrapper ندارد، یعنی POSEIDON2_LEN با کران طول که ماشین از مجموعه محدودیت 7 به بعد اجرا می‌کند، و rand-guest check در aeacf31 هر ecall با a7 = 7 را رد می‌کند (zkp-circuits issue #1). بنابراین guest نوشته‌شده به C یا اسمبلی برای زنجیره 20 از فراخوانی‌های سیستمی 0 تا 6 استفاده می‌کند.

ساخت، اجرا، بررسی

$ 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، همان پاسخ guest fib نوشته‌شده به Rust.

build --lang c همه فایل‌های .c پوشه را (به ترتیب مرتب‌شده)، به‌علاوه start.S و rt.c همراه ابزار، با این گزینه‌ها کامپایل می‌کند:

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

و آن‌ها را با rust-lld -flavor gnu --gc-sections -T <ld> از toolchain پین‌شده Rust پیوند می‌دهد. --gc-sections فقط آنچه را guest فراخوانی می‌کند نگه می‌دارد.

runtime زبان C

-ffreestanding -fno-builtin clang را از شناسایی توابع کتابخانه‌ای بازمی‌دارد، اما از تولید فراخوانی به آن‌ها بازنمی‌دارد. کپی یک struct به memcpy تبدیل می‌شود، مقداردهی اولیه صفر بزرگ به memset، و تقسیم 64 بیتی بر مقداری که در زمان اجرا معلوم می‌شود به فراخوانی یک تابع کمکی، زیرا RV32IM فقط مقادیر 32 بیتی را تقسیم می‌کند. rt.c این هشت تابع را تعریف می‌کند:

تابعکاربرد
memcpy، memmove، memset، memcmpکپی، پر کردن، مقایسه
__udivdi3، __umoddi3، __divdi3، __moddi3/ و % 64 بیتی

این‌ها حلقه‌های بایتی و تقسیم شیفت-تفریق‌اند. کند هستند: در یک حلقه پرتکرار، کپی و تقسیم را به‌صورت کلمه‌های 32 بیتی انجام دهید.

برنامه‌ای که با دست نوشته می‌شود

می‌توانید کد ماشین را خودتان هم بنویسید: آن را اسمبل کنید، با همان اسکریپت پیوندده (linker script) پیوند دهید و ELF را بسته‌بندی کنید. این برنامه دو ورودی خصوصی را جمع می‌کند و حاصل‌جمع را منتشر می‌کند.

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

شماره هر فراخوانی سیستمی را با li a7, n درست پیش از ecall آن تنظیم کنید. در این صورت checker می‌تواند آن شماره را تأیید کند.

با clang پین‌شده اسمبل کنید، با rust-lld و guest-sdk/guest.ld پیوند دهید، سپس بسته‌بندی کنید. از داخل چک‌اوت circuits اجرا کنید:

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 بخش .text فایل ELF را به‌عنوان سگمنت متن و هر بخش PROGBITS تخصیص‌یافته دیگر را به‌عنوان یک سگمنت داده برمی‌دارد. .bss کنار گذاشته می‌شود، زیرا RAM از صفر شروع می‌شود.

بسته‌بندی دستی کانتینر

کانتینر تصویر شش کلمه سرآیند است و سپس خود کلمه‌ها (معماری مجموعه دستورهای RISC-V). می‌توانید بدون هیچ toolchain ای یکی بنویسید. این اسکریپت Python همان 80 بایتی را می‌نویسد که 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 هیچ تفاوتی نمی‌یابد. همیشه روی تصویر بسته‌بندی‌شده با دست rand-guest check را اجرا کنید: node هر کلمه‌ای را که رمزگشایی نشود رد می‌کند، اما فقط در زمان استقرار.

کیف پول دو شکل قدیمی‌تر را هم مستقر می‌کند: فایل .json به شکل { "base_pc", "words" }، و یک .bin خام از کلمه‌های little-endian که روی پایه 0 بارگذاری می‌شود. کانتینر تنها شکلی است که سگمنت داده دارد.

استقرار و فراخوانی

همه مسیرهای بالا یکسان به پایان می‌رسند. با کیف پول rand از fullnode نسخه v0.3 یا جدیدتر (در زنجیره 20، زنجیره زنده، نسخه v0.6.8 یا جدیدتر: کیف پول v0.6.7 نمی‌تواند در آن ارسال کند، زیرا زنجیره 20 هر اثبات و امضا را به genesis hash خود پیوند می‌دهد؛ استقرار یک برنامه Rust را ببینید):

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

کارمزد استقرار 0.001 RAND + 0.0001 RAND × words است: 0.0024 RAND برای 14 کلمه add.bin و 0.0035 RAND برای 25 کلمه c-fib.bin. هر دو کمتر از 4,096 کلمه‌اند، بنابراین در سقف هر زنجیره‌ای جا می‌شوند: پیش‌فرض 4,096 و سقف زنجیره 20 یعنی 65,535. فراخوانی به‌صورت محلی در tier 10 اثبات می‌شود. در زنجیره 15 هزینه آن 0.002 RAND بود. در زنجیره 20 کارمزد دیگر 0.001 + call_fee(tier) نیست: کف آن BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉ است، rand call پیش از اثبات gas: <exact> (declaring <limit>, tier <t>) و کارمزد را چاپ می‌کند، و چهار فراخوانی که در زنجیره 18 اندازه‌گیری شد هرکدام 0.0057 تا 0.0075 RAND هزینه داشت (برنامه‌های نمونه). هر استقرار و فراخوانی در زنجیره 20 همچنین دو اثبات سمت bundle دارد: یک مجوز خرج در tier 10 و bundle در tier 14. استقرار یک برنامه Rust توضیح می‌دهد هر فرمان چه می‌کند.

برای اینکه دیگران بتوانند آنچه را مستقر کرده‌اید بررسی کنند، سورس و کامیت circuits خود را منتشر کنید (aeacf31 برای زنجیره‌های 18 تا 20). با clang 23.1.1 و Rust 1.98.1، rand-guest build روی هر ماشینی همان hc را می‌دهد.

آخرین بار با کامیت c0878c3f از fullnode (زنجیرهٔ 21) در تاریخ 2026-10-12، و 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 بررسی شد. هرجا کد و این صفحه با هم اختلاف دارند، حق با کد است؛ به ما اطلاع دهید.