Rand Protocol
Dokumentasi
Program RISC-V

ISA RISC-V

Apa arti RV32IM pada Rand - instruksi yang dibuktikan oleh zkVM, apa yang ditolak oleh checker, delapan syscall, memori, kontainer image tempat program dikirim, dan batas ukuran program.

Baca halaman ini sebelum Anda menulis program untuk Rand. Halaman ini menjelaskan mesin tempat kode Anda berjalan, apa yang ditolak oleh toolchain, bagaimana program berkomunikasi dengan dunia luar, dan berkas apa yang Anda deploy. Halaman berikutnya memakainya: Deploy program Rust, Deploy program RISC-V, dan kedua transpiler.

Mesin

Program Rand berformat RV32IM: set bilangan bulat dasar RISC-V 32-bit ditambah ekstensi perkalian dan pembagian. Program berjalan di dalam zkVM Rand, yang membuktikan setiap eksekusi dengan batch STARK Plonky3. Verifier tidak pernah melihat satu pun instruksi. Verifier memeriksa bukti terhadap hc, digest Poseidon2 dalam sirkuit dari program tersebut.

kelompokinstruksi
upper / jumpsLUI AUIPC JAL JALR
branchesBEQ BNE BLT BGE BLTU BGEU
loads / storesLW 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
sistemhanya ECALL

JALR mengosongkan bit 0 dari targetnya, sebagaimana ditetapkan RV32I, dan encoding-nya harus memiliki funct3 = 0. Kedua aturan ini hadir bersama constraint set 7 (chain 16); sebelumnya target ganjil tidak dapat di-fetch dan eksekusinya tidak dapat dibuktikan. Target yang genap tetapi bukan kelipatan empat tetap tidak dapat di-fetch.

Tidak ada floating point, tidak ada atomics (ekstensi A), tidak ada instruksi terkompresi (ekstensi C), dan tidak ada register 64-bit. Pembagian 64-bit dalam C menjadi pemanggilan ke helper perangkat lunak; lihat jalur C.

Apa yang ditolak

rand-guest check menjalankan setiap word teks melalui decoder milik mesin itu sendiri. Program yang lolos tidak dapat gagal di dalam sirkuit karena alasan encoding, nomor syscall, atau tata letak. Pemeriksa menolak:

temuanarti
Compressedencoding RVC 16-bit; mesin hanya men-decode word 32-bit
Undecodableopcode, funct, atau jumlah pergeseran yang tidak diimplementasikan oleh mesin
FenceFENCE atau FENCE.I; mesin tidak memiliki pengurutan memori
Csrinstruksi CSR apa pun; mesin tidak memiliki CSR
EbreakEBREAK; trap tidak dimodelkan
Syscallsebuah ecall yang a7-nya diatur oleh li tepat sebelumnya dan tidak menunjuk syscall mana pun
Capjumlah word melebihi --max-words, dihitung sebagaimana loader menghitungnya
Layoutbasis teks yang tidak rata pada batas word

Program empat word dengan fence, ebreak, dan ecall bernomor 9, sebagaimana dilaporkan oleh rand-guest check (kode keluar 1):

$ rand-guest check bad.elf
note: the loader refuses this image (Decode { index: 0, word: 267386895, err: Opcode(15) }), so its data prologue could not be counted; the word count below is the text alone
0x00001000  0x0ff0000f  Fence: FENCE/FENCE.I: the machine has no memory ordering instructions
0x00001004  0x00100073  Ebreak: EBREAK: traps are not modelled
0x0000100c  0x00000073  Syscall: ecall with a7 = 9, which is no syscall the machine implements
4 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
REJECTED

Sebuah ecall yang a7-nya tidak dapat dilihat pemeriksa (diatur oleh selain li, atau setelah sebuah branch) dihitung sebagai unresolved_ecalls, bukan ditolak. Mesin tetap melakukan trap pada nomor yang salah saat run time.

Pemeriksa tidak memeriksa nilai pointer, panjang, atau indeks input, stack 64 KiB terhadap RAM, maupun perataan alamat yang dihitung saat run time. Akses yang tidak rata merupakan pelanggaran constraint, sehingga toolchain dikompilasi dengan -unaligned-scalar-mem. rand-guest run melaporkan setiap trap saat run time.

Syscall

ECALL membaca nomor syscall dari a7, argumen pertama dari a0, dan argumen kedua dari a1. Sebuah nilai dikembalikan di a0.

#syscallefekgasRust (guest-sdk)C (guest.h)
0HALTmengakhiri eksekusi1halt()rand_halt()
1WRITE_OUTPUT slot wordmenetapkan output publik slot < 8, paling banyak sekali per slot; slot yang belum ditulis bernilai nol1write_output(slot, word)rand_write_output
2READ_INPUT idxmengembalikan word input privat idx, terikat pada komitmen bergaram H_IN1read_input(idx)rand_read_input
3POSEIDON2 ptr nmeng-hash n ≤ 4096 word pada alamat word ptr dan menulis digest 8 word di atas ptr..ptr+8; tanpa padding3 + 3·⌈n/4⌉unsafe poseidon2(ptr, n)rand_poseidon2
4KECCAK ptrsatu permutasi Keccak-f[1600] di tempat pada 50 word192keccak(state), keccak256(msg)rand_keccak
5SHA256 ptrsatu kompresi SHA-256 di tempat pada 24 word64sha256_compress(buf), sha256(msg)rand_sha256_compress
6READ_PUBLIC idxmengembalikan word segmen publik idx, terikat pada H_PUB tanpa garam1read_public(idx)rand_read_public
7POSEIDON2_LEN ptr nPOSEIDON2 dengan panjang n terikat ke dalam sponge, minimal satu permutasi (constraint set 7)3 + 3·max(⌈n/4⌉, 1)poseidon2_len(buf, n)tidak ada

Setiap instruksi lainnya berbiaya 1 gas: gas dihitung satu per baris cpu, dan hanya syscall hash yang berbobot lebih (constraint set 8, sejak chain 18). Satu pemanggilan Poseidon2 terdiri dari baris ecall-nya, dua baris write-back, dan ⌈n/4⌉ baris absorb dengan 3 gas masing-masing.

POSEIDON2 tidak melakukan padding. Sponge menimpa lane pertamanya dengan setiap potongan empat word, sehingga [a], [a, 0], [a, 0, 0], dan [a, 0, 0, 0] menghasilkan satu digest yang sama. Program yang meng-hash data dengan panjang yang dapat bervariasi harus menyertakan panjang di dalam pesan, meng-hash encoding dengan panjang tetap, atau memanggil POSEIDON2_LEN, yang menanamkan n ke dalam kapasitas sponge dan menghasilkan digest yang berbeda untuk word yang sama. Di Rust SDK, poseidon2 adalah unsafe fn atas raw pointer dan poseidon2_len adalah pemanggilan aman atas slice; keccak dan sha256_compress menerima &mut [u32; 50] dan &mut [u32; 24].

Satu celah toolchain. Pada circuits aeacf31, mesin, emulator, dan Rust SDK mengimplementasikan syscall 7, tetapi rand-guest check tidak: daftar syscall-nya berhenti di 6, sehingga ia melaporkan ecall dengan a7 = 7 sebagai temuan Syscall dan rand-guest build keluar dengan kode bukan nol pada guest yang memanggil poseidon2_len (circuits issue #1). guest.h juga tidak memiliki wrapper untuknya. Sampai hal ini diperbaiki, guest yang dibangun dengan tool bawaan memakai POSEIDON2 pada pesan dengan panjang tetap.

Sebuah bukti mempublikasikan persis ini: hc, H_IN, H_PUB, tier, batas gas yang dideklarasikan, dan delapan word output. Dua batasan berlaku di chain saat ini:

  • Segmen publik ditetapkan saat deploy. Di chain 20, rand program deploy --public menetapkan word publik sebuah program (paling banyak max_program_public_words, 32.768), dan setiap pemanggilan diperiksa terhadapnya. Tidak ada word publik per pemanggilan milik program itu sendiri. Chain 20, seperti chain 19, menambahkan delapan word ke segmen setiap pemanggilan setelah word tersebut, yaitu call binding yang mengikat bukti pada transaksinya (di chain 20 ia juga mencakup genesis hash); guest membaca word miliknya sendiri pada indeks yang sama seperti sebelumnya. Invoke RPL-2 menambahkan transisi yang dideklarasikan setelah binding, yang dibaca program mulai dari public_len + 8 (Program). Pada chain tanpa field batas, seperti chain 12, segmen publik kosong dan READ_PUBLIC tidak memiliki apa pun untuk dibaca.
  • Word input privat sebuah pemanggilan dibatasi oleh sealed envelope. max_call_envelope_bytes chain 20 sebesar 65.536 memungkinkan 16.071 word; chain tanpa field tersebut memungkinkan 4.295.

Pemanggilan KECCAK atau SHA256 menambahkan sebuah tabel ke bukti, dan header bukti kemudian menunjukkan bahwa pemanggilan tersebut melakukan hash. Bukti yang membawa keccak berukuran sekitar 3,3 MB: melebihi batas bukti bawaan 2 MiB, di bawah 4 MiB milik chain 20, dan satu pemanggilan boleh memakai paling banyak 128 permutasi Keccak-f dan 128 kompresi SHA-256 (Program).

Memori

  • 32 register; x0 bernilai nol.
  • RAM dimulai dari nol. Setiap alamat berada di bawah 2³⁰.
  • Skrip linker bawaan, guest-sdk/guest.ld, menempatkan program pada 0x1000, lalu .rodata, .data, dan .bss, kemudian stack 64 KiB pada __stack_top.
  • Program dengan segmen data memerlukan ruang di bawah teksnya untuk prolog loader (bagian berikutnya). Berkas .ld-nya sendiri menaikkan ORIGIN ke 0x10000, seperti yang dilakukan evm.ld dan sbpf.ld.

Kontainer image

rand-guest build dan rand-guest pack menulis satu format berkas, yaitu kontainer image. Semua angka berupa word 32-bit little-endian:

[0x444e4152 ("RAND"), 1, text_base, n_text, data_base, n_data] text… data…

Loader mengubah kontainer menjadi program { base_pc, words }:

  1. Loader menulis pasangan li/sw untuk setiap word data yang bukan nol. Word nol dilewati, karena RAM sudah terbaca nol.
  2. Loader menempatkan prolog itu tepat di bawah text_base, sehingga base_pc = text_base − 4 × prologue words. Prolog berlanjut langsung ke _start milik program itu sendiri.
  3. hc dan id program dihitung atas seluruh words, termasuk prolog. Jadi hc mengikat data sekaligus kode.

Kontainer tanpa data tidak menghasilkan prolog, serta program dan hc yang sama dengan biner datar tanpa header pada basis yang sama.

rand program deploy di fullnode v0.3 dan yang lebih baru mengenali kontainer dari word pertamanya. 0x444e4152 tidak pernah ter-decode sebagai instruksi, sehingga .bin mentah tidak mungkin tertukar dengan kontainer. Berkas yang diawali magic tetapi cacat merupakan kesalahan, bukan fallback.

Dua identitas

namaapa itusiapa yang memakainya
hcdigest Poseidon2 sirkuit atas (base_pc, len, words)setiap bukti dari sebuah panggilan diverifikasi terhadapnya; rand_getProgram menampilkannya sebagai code_hash
program idblake3("rand-program" ‖ base_pc ‖ words); deploy dengan --public turut mengikat public words, di bawah tag rand-program-2kunci tempat program yang telah di-deploy disimpan dan dipanggil

rand-guest build dan rand-guest info mencetak keduanya. hc bersifat mengikat tetapi tidak menyembunyikan: siapa pun yang memiliki program kandidat dapat mengonfirmasinya.

Batas program

Sebuah deploy boleh memuat paling banyak sejumlah batas program milik rantai tersebut, dalam satuan word.

rantaibatas
genesis tanpa max_program_words (rantai 12)4,096
genesis dengan max_program_wordsnilai tersebut, 1..=65535
batas keras zkVM, pada rantai mana pun65,535

Batas ini merupakan bagian dari hash genesis, sehingga tidak dapat diubah pada rantai yang sedang berjalan. rand-guest memeriksa terhadap --max-words (bawaan 4,096). rand program deploy menanyakannya kepada node melalui rand_estimateFee dan menolak program yang melebihi batas sebelum membuktikan apa pun.

Rantai 20, rantai yang sedang aktif, menaikkan batas tersebut. Hash genesis-nya adalah 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135 dan max_program_words-nya 65,535. Rantai ini mempertahankan batasan yang diperkenalkan rantai 13 (rantai v0.4), kecuali batas bukti, yang dipangkas setengah oleh rantai 17 hingga 20. Batasan lainnya:

batasanrantai 20
max_proof_bytes4,194,304 (4 MiB); 8 MiB pada rantai 13 hingga 16
max_block_bytes20,971,520 (20 MiB)
max_call_envelope_bytes65,536
max_program_public_words32,768

Pada rantai 20, seperti pada rantai 19, batas word bukanlah batasan yang mengikat. Genesis-nya menetapkan hardening_v6, yang menyebabkan sebuah deploy ditolak (ProgramUncallable) bila tidak ada panggilan yang mampu membuktikan program tersebut. Sebuah panggilan dibatasi pada tingkat 14, yang tabel Poseidon2-nya memuat 2,048 permutasi. Digest program memakai satu permutasi per empat word, baris salt masukan satu, dan delapan word pengikat panggilan dua, sehingga tersisa 2,045: 8,180 word untuk program tanpa masukan publik, dan lebih sedikit bila ada masukan publik (8,168 dengan sembilan public words). Pool setiap node menolak deploy semacam itu pada rantai mana pun.

Agregasi blok dimatikan pada rantai 20: genesis-nya tidak memiliki bagian aggregation. Rantai 20 aktif sejak 2026-10-01 06:17 UTC pada fullnode v0.6.8, sebuah hard fork yang dipotong dari state rantai 19 pada ketinggian 7,258 (rantai 19 berhenti pada 7,700); rantai 19 (aktif sejak 03:43 UTC pada hari yang sama) sendiri merupakan re-genesis dari rantai 18 pada ketinggian 141,935. Program tidak terbawa melewati pemotongan, sehingga tidak ada program yang di-deploy pada rantai sebelumnya yang ada di rantai 20. Rantai 20 juga merupakan rantai pertama dengan state program RPL-2 (Program). Rantai ini berjalan di jaringan melalui node bootstrap berikut:

/ip4/164.90.239.200/tcp/30303/p2p/12D3KooWEQEbUwZRgqhDPXhW7vNBmcRcnhUDGUADe81VFdv3ALYe
/ip4/165.245.173.74/tcp/30303/p2p/12D3KooWJyc5oDHggAr89e9QrRFBy9Tg8TxXh18yzrb1SM6KKLYw
/ip4/188.166.235.187/tcp/30303/p2p/12D3KooWSyX84P1DAbWByLHpRBMLRWD53r3Jj4aqVt6tHoF8B2ZQ
imagewordmuat dalam 4,096muat dalam 65,535di-deploy pada rantai 20 (8,180 dapat dipanggil)
fib (Rust)26yayaya
c-fib (C)25yayaya
ERC-20, diterjemahkan (tahap dua)11,686tidakyatidak
evm.bin, interpreter EVM18,009tidakyatidak
sbpf.bin, interpreter sBPF8,317tidakyatidak
SPL Token, diterjemahkan65,096tidakya, dengan sisa 439tidak

Tingkat

Tingkat t sebuah bukti membatasi eksekusi pada 2ᵗ − 1 baris: siklus ditambah baris digest (satu per empat program word, satu per empat input word, dan digest masukan publik). Tingkat juga menetapkan anggaran Poseidon2 yang terpisah. rand-guest run --tier t melaporkan keduanya:

tier 10: fits (cycles 146 of 1023, Poseidon2 permutations 10 of 128)

Tingkat bersifat publik, demikian pula batas gas yang dideklarasikan bukti. Pada rantai 20, biaya panggilan mengikuti batas tersebut dan ukuran bukti, bukan tingkat: setidaknya 0.001 RAND + gas_price × gas_limit + byte_price × ⌈bytes / 1024⌉, pada harga rantai saat ini, yang bermula pada 100 dan 800 unit 10⁻⁹ RAND, bergerak mengikuti keterisian blok, dan pada rantai 20 berhenti pada 10,000 dan 80,000 unit (Biaya). Sebuah batas tidak boleh melebihi plafon header, (2ᵗ − 1) + 2ᵗ⁻² ditambah 191 per blok Keccak dan 63 per blok SHA-256 yang dideklarasikan bukti: 1,279 pada tingkat 10 dan 20,479 pada tingkat 14 tanpa tabel hash. rand-guest run melaporkan siklus dan permutasi, bukan gas. rand call menjalankan program terlebih dahulu dan mencetak gas yang dipakainya serta batas yang dideklarasikannya, secara bawaan gas yang tepat dibulatkan ke atas ke kelipatan 2ᵗ⁻² berikutnya, sebelum membuktikan:

gas: 5636 (declaring 6144, tier 12)

Baris tersebut berasal dari sebuah panggilan pada rantai 18 pada 2026-09-30 (Pemungutan suara anonim). Pada rantai tanpa bagian gas dalam genesis, yakni rantai 15 dan sebelumnya, biayanya merupakan jadwal bertingkat: 0.001 + (0.001 + 0.0001 × (t − 10) / 2) RAND. Memori yang dibutuhkan sebuah bukti naik tajam seiring tingkat; lihat Perangkat keras validator dan aggregator.

Sejak fullnode v0.5.6 (2026-09-25), sebuah panggilan boleh mendeklarasikan paling tinggi tingkat 14: node menolak bukti panggilan bertingkat lebih tinggi dengan CallTierTooHigh sebelum membangun verifier key, dan blok yang memuatnya tidak valid. Anggaran tingkat 14 adalah 16,383 baris dan 2,048 permutasi Poseidon2, dan digest program itu sendiri menghabiskan sekitar satu permutasi per empat word, sehingga program di atas 8,180 word tidak dapat dipanggil, dan rantai 20, seperti rantai 19, menolak deploy-nya. Menulis program dalam Rust atau C menguraikan anggaran tersebut.

Terakhir diverifikasi terhadap commit fullnode c0878c3f (chain 21) pada 2026-10-12, dan circuits `aeacf31` (`rand-guest/README.md`, `rand-guest/src/check.rs`, `research/docs/01-isa.md`, `guest-sdk/`), fullnode tag v0.6.8 `c9c9bd3c` (`docs/program-state.md`, `ledger/program_state.rs`, `deploy/cut-chain20-genesis.sh`), chain 20's genesis and `rand_getLimits`, read on 2026-10-01, fullnode tag v0.8.0 `c0878c3f` (`docs/zkvm.md` §8a, constraint set 9), `deploy/genesis-chain21.json` and `rand_getLimits` on chain 21, read on 2026-10-12. Jika kode dan halaman ini tidak sesuai, kodelah yang benar; beri tahu kami.