Program apa pun yang berupa kode mesin RV32IM dapat di-deploy, apa pun alat yang menghasilkannya. Halaman ini menunjukkan dua jalur: C melalui rand-guest build --lang c, dan program yang Anda tulis dalam assembly lalu Anda kemas sendiri. Keduanya berakhir pada kontainer image yang sama dengan yang dihasilkan Deploy a Rust program, dan keduanya di-deploy dengan rand program deploy. Siapkan terlebih dahulu checkout circuits dan rand-guest, seperti pada langkah 1 halaman tersebut. Untuk chain 18 hingga 20, checkout berada pada commit aeacf31, yang di-pin oleh fullnode v0.6.7 maupun v0.6.8. Keluaran di bawah ini dihasilkan pada 2026-09-19 pada 7ef3220 dan belum dijalankan ulang. Direktori rand-guest, termasuk guest.h dan rt.c, sama pada kedua commit tersebut.
C via clang
Yang Anda perlukan
| alat | versi | cara |
|---|---|---|
| clang | 23.1.1, dengan backend RISC-V | brew install llvm di macOS; clang bawaan sistem Apple tidak memiliki backend RISC-V. Atau atur $CLANG ke biner 23.1.1 |
rust-lld | dari Rust 1.98.1 | rustup +1.98.1 component add llvm-tools |
rand-guest menolak clang mana pun yang versinya bukan 23.1.1. RAND_GUEST_CLANG_UNPINNED=1 membangun dengan versi lain, disertai peringatan bahwa hc tidak akan cocok dengan image yang dipublikasikan.
Menulis guest
Direktori guest C hanya berisi berkas .c (dan .ld opsional). rand-guest menyediakan guest.h, _start, dan runtime kecil. Jangan menambahkan guest.h atau start.S sendiri: build --lang c menolak direktori yang memiliki salah satunya.
Berikut guests-compiled/c-fib/fib.c yang telah di-commit:
#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 mendeklarasikan rand_read_input, rand_read_public, rand_write_output, rand_poseidon2, rand_keccak, rand_sha256_compress, dan rand_halt. Header ini tidak memiliki pembungkus untuk syscall 7, yaitu POSEIDON2_LEN dengan batas panjang yang dijalankan mesin sejak constraint set 7, dan rand-guest check pada aeacf31 menolak ecall dengan a7 = 7 (zkp-circuits issue #1). Guest C atau assembly untuk chain 20 karena itu menggunakan syscall 0 hingga 6.
Build, jalankan, periksa
$ 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, jawaban yang sama dengan guest Rust fib.
build --lang c mengompilasi setiap berkas .c dalam direktori (berurutan), ditambah start.S dan rt.c bawaan, dengan:
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
lalu menautkannya dengan rust-lld -flavor gnu --gc-sections -T <ld> dari toolchain Rust yang di-pin. --gc-sections hanya mempertahankan apa yang dipanggil guest.
Runtime C
-ffreestanding -fno-builtin mencegah clang mengenali fungsi pustaka, tetapi tidak mencegahnya menghasilkan panggilan ke fungsi tersebut. Penyalinan struct menjadi memcpy, inisialisasi nol yang besar menjadi memset, dan pembagian 64-bit dengan nilai saat run-time menjadi panggilan helper, karena RV32IM hanya membagi nilai 32-bit. rt.c mendefinisikan delapan fungsi berikut:
| fungsi | kegunaan |
|---|---|
memcpy, memmove, memset, memcmp | menyalin, mengisi, membandingkan |
__udivdi3, __umoddi3, __divdi3, __moddi3 | / dan % 64-bit |
Fungsi-fungsi ini berupa perulangan per byte dan pembagian shift-subtract. Kinerjanya lambat: dalam perulangan yang sering dijalankan, salin dan bagi dalam word 32-bit.
Program yang ditulis tangan
Anda juga dapat menulis kode mesin sendiri: rakit, tautkan dengan linker script yang sama, lalu kemas ELF-nya. Program ini menjumlahkan dua input privat dan mempublikasikan hasilnya.
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
Tetapkan setiap nomor syscall dengan li a7, n tepat sebelum ecall-nya. Dengan begitu, checker dapat memverifikasi nomornya.
Rakit dengan clang yang di-pin, tautkan dengan rust-lld dan guest-sdk/guest.ld, lalu kemas. Jalankan dari dalam checkout 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 mengambil .text ELF sebagai segmen teks dan setiap section PROGBITS teralokasi lainnya sebagai satu segmen data. .bss tidak disertakan, karena RAM dimulai dari nol.
Mengemas kontainer secara manual
Kontainer image terdiri dari enam word header, lalu word-word itu sendiri (The RISC-V ISA). Anda dapat menulisnya tanpa toolchain apa pun. Skrip Python ini menulis 80 byte yang sama dengan yang ditulis pack di atas:
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 mencetak hc 42986ac1…14bd yang sama, dan cmp add-hand.bin add.bin tidak menemukan perbedaan. Selalu jalankan rand-guest check pada image yang dikemas manual: node menolak setiap word yang tidak dapat didekode, tetapi hanya pada saat deploy.
Wallet juga men-deploy dua bentuk yang lebih lama: berkas .json { "base_pc", "words" }, dan .bin mentah berisi word little-endian yang dimuat pada basis 0. Kontainer adalah satu-satunya bentuk yang memiliki segmen data.
Deploy dan panggil
Setiap jalur di atas berakhir dengan cara yang sama. Dengan wallet rand dari fullnode v0.3 atau lebih baru (pada chain 20, chain yang aktif, v0.6.8 atau lebih baru: wallet v0.6.7 tidak dapat mengirim di sana, karena chain 20 mengikat setiap proof dan tanda tangan pada genesis hash-nya; lihat Deploy a Rust program):
rand program deploy add.bin
rand call <program id> --input 40 --input 2
Biaya deploy adalah 0.001 RAND + 0.0001 RAND × words: 0,0024 RAND untuk 14 word add.bin dan 0,0035 RAND untuk 25 word c-fib.bin. Keduanya di bawah 4.096 word, sehingga muat dalam batas chain mana pun: bawaan 4.096 dan 65.535 pada chain 20. Panggilan di-prove secara lokal pada tier 10. Pada chain 15 biayanya 0,002 RAND. Pada chain 20 biayanya tidak lagi 0.001 + call_fee(tier): batas bawahnya adalah BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉, rand call mencetak gas: <exact> (declaring <limit>, tier <t>) dan biayanya sebelum melakukan proving, dan empat panggilan yang diukur pada chain 18 masing-masing berbiaya 0,0057 hingga 0,0075 RAND (Example programs). Setiap deploy dan panggilan pada chain 20 juga membawa dua proof sisi bundle, otorisasi pengeluaran tier 10 dan bundle tier 14. Deploy a Rust program menjelaskan apa yang dilakukan setiap perintah.
Agar pihak lain dapat memeriksa apa yang Anda deploy, publikasikan source dan commit circuits Anda (aeacf31 untuk chain 18 hingga 20). Dengan clang 23.1.1 dan Rust 1.98.1, rand-guest build menghasilkan hc yang sama pada mesin mana pun.