Halaman ini merangkumi semua yang anda perlukan untuk meletakkan program anda sendiri di Rand: apa yang boleh menjadi sebuah program, cara mereka bentuk apa yang dibacanya dan diterbitkannya, serta satu contoh lengkap yang ditulis dua kali, sekali dalam Rust dan sekali dalam C. Halaman ini kemudian membincangkan pengujian, bajet pembuktian, penempatan, pemanggilan, dan cara sesiapa sahaja boleh menyemak hasilnya. Setiap output rand-guest di bawah datang daripada larian sebenar dan ditampal seperti yang dicetak. Larian tersebut dibuat pada 2026-09-25 pada commit circuits 7ef3220. Testnet kini ialah chain 20, yang toolchain-nya, seperti chain 18 dan 19, dibina pada aeacf31 (cabang feat/v067-circuits; fullnode v0.6.8 menetapkan commit yang sama), dan output tersebut belum dijalankan semula di sana. Halaman ini menyatakan di mana SDK atau chain berbeza.
Dua tutorial ringkas ialah laluan pantas: Deploy a Rust program dan Deploy a RISC-V program. Halaman ini menganggap anda telah melakukan langkah 1 tutorial Rust: checkout randprotocol/zkp-circuits pada aeacf31, Rust 1.98.1 dengan sasaran riscv32im-unknown-none-elf, binari rand-guest, dan untuk C, clang 23.1.1 serta llvm-tools. The RISC-V ISA ialah rujukan bagi mesin itu sendiri. Example programs menyambung daripada sini dengan program yang lebih besar dan apa yang diubah oleh pengukuran gas chain 18. Halaman ini merangkumi program RPL-1, yang dijalankan oleh Call tanpa keadaan; chain 20 juga mempunyai keadaan program RPL-2, cell dan vault yang diubah oleh Invoke (Programs), dengan contoh lengkap dalam randprotocol/example-programs.
1. Apa yang boleh menjadi sebuah program
Program ialah apa-apa pengiraan yang sesuai dengan bentuk ini:
- Ia membaca perkataan 32-bit sebagai input.
- Ia menerbitkan paling banyak lapan perkataan 32-bit sebagai output.
- Ia deterministik: input yang sama sentiasa memberikan output yang sama.
- Ia berhenti dalam bajet tiernya (bahagian 7).
Dalam had tersebut anda boleh menulis apa sahaja. Anda mendapat gelung, rekursi, struct, tatasusunan, aritmetik 64-bit, crate dan heap jika anda membawa pengalokasi sendiri. Namun mesin ini ialah RV32IM kosong, dan tiada sistem pengendalian di bawahnya:
| anda ada | anda tidak ada |
|---|---|
arahan integer RV32IM, termasuk MUL/DIV/REM | perkakasan titik terapung (float perisian Rust berfungsi, tetapi perlahan) |
sehingga 1 MiB RAM bermula dari 0x1000 dalam skrip linker lalai, dengan stack 64 KiB | heap, melainkan anda membekalkan pengalokasi (bahagian 6) |
| Poseidon2, Keccak-f dan SHA-256 sebagai syscall | fail, rangkaian, jam, kerawakan, thread, std, libc |
crate Rust no_std dan C yang berdiri sendiri | keadaan yang kekal dari satu panggilan ke panggilan seterusnya |
| lapan perkataan output awam | cara untuk memindahkan RAND atau token dari dalam program |
Baris terakhir paling penting semasa anda mereka bentuk program. Output ialah data, bukan kesan. Sebuah panggilan membuktikan bahawa program dengan digest tertentu telah dijalankan pada input persendirian dan menerbitkan lapan perkataan. Chain mengesahkan bukti itu dan menyimpan lapan perkataan tersebut dalam resit. Ia tidak mengubah sebarang baki dan tidak mencetuskan apa-apa. Maksud perkataan-perkataan itu dipersetujui antara program, pemanggilnya, dan sesiapa yang membaca resit. Programs menyenaraikan perkara yang chain belum sokong.
2. Reka bentuk antara muka dahulu
Sebuah program mempunyai tiga saluran. Pilih apa yang diletakkan pada setiap satu sebelum anda menulis sebarang kod.
| saluran | ditetapkan apabila | dibaca dengan | siapa yang melihat | had pada chain 20 |
|---|---|---|---|---|
| input persendirian | setiap panggilan | read_input(i) / rand_read_input(i) | hanya pemanggil, dan sesiapa yang diberi kunci panggilan itu | 16,071 perkataan (had sampul input termeterai) |
| segmen awam | sekali, semasa penempatan | read_public(i) / rand_read_public(i) | semua orang; id program mengikatnya | 32,768 perkataan |
| output | oleh larian | write_output(slot, w) / rand_write_output | semua orang, dalam resit | 8 perkataan |
Input persendirian tidak membuktikan apa-apa tentang dunia dengan sendirinya
Input persendirian diikat pada bukti melalui komitmen bergaram H_IN, jadi ia tidak boleh berubah selepas pembuktian. Tetapi program tidak mempunyai cara untuk mengetahui bahawa input itu benar. Pemanggil boleh menaip sebarang nombor. Untuk menjadikan input persendirian bermakna, kaitkannya dengan sesuatu yang awam di dalam program:
- sebuah komitmen yang diterbitkan lebih awal (dalam segmen awam, atau diberikan kepada verifier di luar saluran) yang mesti dibuka oleh input persendirian, seperti dalam lelongan di bawah;
- sebuah akar Merkle yang di bawahnya sebuah daun persendirian mesti dibuktikan (Poseidon2 ialah syscall untuk ini);
- sebuah digest yang sudah dimiliki verifier, yang dikira semula oleh program dan diterbitkan sebagai output.
Ini ialah kemahiran reka bentuk utama bagi program sulit. Bukti memberitahu verifier apa yang dikira. Pengikatan kepada data awam memberitahunya ke atas apa.
Mengekod data sebagai perkataan
Segala-galanya bergerak sebagai u32:
| data | pengekodan |
|---|---|
| rentetan bait | satu perkataan panjang, kemudian bait empat setiap perkataan, little-endian, perkataan terakhir dipadankan dengan sifar (bentuk yang digunakan dompet untuk input awam ELF) |
u64 atau jumlah | dua perkataan, rendah kemudian tinggi |
| integer bertanda | pelengkap dua dalam satu perkataan |
| hasil yang besar | digest Poseidon2-nya: 8 perkataan, yang memenuhi semua output; atau terbitkan beberapa perkataan ringkasan dan komitmen yang lebih pendek |
Alat baris perintah menerima perkataan input dalam perpuluhan: rand-guest run --input dan rand call --input tidak menerima 0x…. Fail segmen awam untuk rand program deploy --public menerima perpuluhan dan heks 0x.
Bentuk output
- Slot 0 ialah perkataan status. Berikan setiap hasil satu nombor, termasuk kegagalan: pembukaan yang tidak sepadan, terlalu banyak item, input di luar julat. Semakan yang gagal tetap harus berhenti dengan bersih bersama status, supaya pemanggil mendapat bukti yang menyatakan apa yang tidak kena.
- Setiap slot ditulis paling banyak sekali. Penulisan kedua ke slot yang sama ialah trap (
DoubleWrite(0)dalam emulator), dan larian yang trap tidak boleh dibuktikan. Slot yang tidak pernah anda tulis dibaca sebagai sifar. - Dalam Rust, biarkan slot 7 kosong. Guest Rust yang panik menulis
0xdeadbeef(3,735,928,559) ke slot 7 dan berhenti. Output yang ditulisnya sebelum panik kekal di tempatnya, jadi pembaca mesti menyemak slot 7 sebelum mempercayai yang lain (bahagian 6). - Membaca melepasi input yang diberikan ialah trap (
InputIndex(5)dalam emulator). Baca kiraan dahulu dan semaknya.
3. Contoh lengkap: lelongan bida termeterai
Seorang penjual ingin menjalankan lelongan harga kedua tanpa menerbitkan bida yang kalah:
- Setiap pembida memilih bida dan salt rawak 128-bit, dan memberikan penjual sebuah komitmen: Poseidon2 ke atas
("BID1", indeks pembida, bida, salt). - Penjual menempatkan program lelongan dengan harga rizab dan setiap komitmen sebagai segmen awamnya. Id program kini menamakan lelongan ini dan tiada yang lain.
- Setiap pembida menghantar bida dan salt mereka kepada penjual secara persendirian.
- Penjual memanggil program dengan setiap bida dan salt sebagai input persendirian. Program menyemak setiap pembukaan terhadap komitmennya, memilih pemenang, dan menerbitkan
[status, pemenang, harga, bilangan pembida].
Chain mengetahui siapa yang menang dan pada harga berapa. Ia tidak pernah melihat bida yang kalah. Penjual yang mengubah sebarang bida mendapat BAD_OPENING dan bukannya hasil, kerana komitmen telah ditetapkan dalam id program.
Semuanya berada di dalam checkout circuits, kerana rand-guest build mencari guest-sdk dengan melihat ke atas dari direktori guest:
circuits/
guest-sdk/
rand-guest/
my-guests/
commit/ a Rust guest that computes one commitment
auction-core/ the auction rule as a plain no_std library, tested on the host
auction/ the Rust guest
auction-c/ the same guest in C
auction-c-test/ a host test for the C rule, outside the guest's directory
Emulator sebagai kalkulator hash
Pembida memerlukan Poseidon2 ke atas medan Goldilocks, tepat seperti yang dikira oleh mesin. Alat yang paling mudah dan betul ialah guest kecil yang mengiranya, dijalankan pada emulator. Digest ialah lapan perkataan, iaitu tepat bilangan slot output.
my-guests/commit/Cargo.toml:
[package]
name = "commit"
version = "0.1.0"
edition = "2021"
[dependencies]
guest-sdk = { path = "../../guest-sdk" }
[profile.release]
opt-level = 3
lto = true
panic = "abort"
codegen-units = 1
[workspace]
my-guests/commit/src/main.rs:
#![no_std]
#![no_main]
use guest_sdk::{halt, poseidon2, read_input, write_output};
/// Inputs: a bidder's index, bid and four salt words.
/// Outputs: the 8-word commitment Poseidon2("BID1", index, bid, salt[0..4]).
#[no_mangle]
pub extern "C" fn main() -> ! {
let mut buf = [0u32; 8];
buf[0] = 0x4249_4431; // "BID1"
for i in 0..6 {
buf[1 + i] = read_input(i as u32);
}
unsafe { poseidon2(buf.as_mut_ptr(), 7) };
for (slot, word) in buf.iter().enumerate() {
write_output(slot as u32, *word);
}
halt();
}
poseidon2(ptr, n) meng-hash n perkataan di tempat dan menulis digest 8 perkataan di atas ptr..ptr+8, jadi penimbal mesti memuatkan sekurang-kurangnya lapan perkataan walaupun hanya tujuh yang di-hash. Tag "BID1" mengasingkan digest ini daripada sebarang penggunaan Poseidon2 yang lain.
Pada aeacf31 poseidon2 ialah unsafe fn, kerana ia menerima penuding mentah, jadi panggilan itu berada dalam blok unsafe. Pada 7ef3220, tempat output di bawah dihasilkan, ia ialah fungsi selamat dan baris yang sama ditulis tanpa blok itu. Span tidak menambah padding: [a] dan [a, 0] menghasilkan hash yang sama. Ini tidak berbahaya di sini, kerana setiap mesej ialah tujuh perkataan dan bermula dengan tag. Bagi mesej yang panjangnya boleh berubah, SDK pada aeacf31 mempunyai poseidon2_len(buf, n), yang mengikat panjang (syscall 7), tetapi rand-guest check pada commit itu menolak syscall 7, jadi guest sedemikian belum boleh dibina (isu zkp-circuits #1). Letakkan panjang dalam mesej sebaliknya.
$ rand-guest build my-guests/commit --out commit.bin
76 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK
wrote commit.bin and its .sha256 (76 words, hc 41007891b5c393296fdd76539c93d0b06678bc8a0bd52cfc3b6886dcf0381343, program id 11dabdf740989d43599b296c43342bf3ce890e980f5cc558d15ba8ca9e8e9438)
$ rand-guest run commit.bin --input 0 --input 400 --input 2654435769 --input 2135587861 --input 4087136352 --input 1559071514
out[0] = 3502385358
out[1] = 1187922325
out[2] = 2500412136
out[3] = 2452821097
out[4] = 2045657999
out[5] = 1866863173
out[6] = 678782893
out[7] = 3524569252
cycles 77
tier 10
Baris cargo ditinggalkan daripada output binaan di sini dan seterusnya. Amaran unaligned-scalar-mem yang dicetak cargo adalah dijangka (Deploy a Rust program).
Empat pembida, dengan bida dan salt berikut:
| pembida | bidaan | salt (empat perkataan) |
|---|---|---|
| 0 | 400 | 2654435769 2135587861 4087136352 1559071514 |
| 1 | 250 | 461845907 3432918353 3864292196 2246822507 |
| 2 | 900 | 3266489909 668265263 374761393 3550635116 |
| 3 | 300 | 4252018373 3042594569 1759714724 625341585 |
Menjalankan commit.bin sekali bagi setiap pembida, kemudian menulis harga rizab (100), bilangan (4) dan keempat-empat digest mengikut urutan, menghasilkan segmen awam, auction-public.txt, dengan 34 perkataan:
100 4 3502385358 1187922325 2500412136 2452821097 2045657999 1866863173 678782893 3524569252 3413403069 1515911705 3633767467 1218237979 474331206 896634170 2889783686 541804293 3111308722 99534202 1917186958 1017842926 3082732585 4215265588 467906960 2929273893 588931287 1295911076 647129560 1384102451 625140897 4060943258 2970944086 698558155
Dalam lelongan sebenar, setiap pembida menjalankan commit.bin pada mesin sendiri, dan salt datang daripada sumber rawak yang sebenar.
4. Program dalam Rust
Peraturan sebagai pustaka yang boleh diuji
Letakkan logik yang tidak memerlukan syscall dalam crate no_std tersendiri. guest-sdk enggan membina untuk sebarang sasaran selain riscv32, jadi crate guest tidak dapat menjalankan cargo test pada komputer riba anda. Crate pustaka tanpa kebergantungan guest-sdk boleh.
my-guests/auction-core/Cargo.toml:
[package]
name = "auction-core"
version = "0.1.0"
edition = "2021"
[workspace]
my-guests/auction-core/src/lib.rs:
//! The auction's rule, with no syscalls, so it builds and tests on the host.
#![no_std]
#[derive(Debug, PartialEq, Eq)]
pub enum Outcome {
/// The highest bidder wins and pays the second-highest bid, or the reserve if higher.
Sold { winner: u32, price: u32 },
/// No bid reached the reserve.
NoSale,
}
/// A second-price (Vickrey) auction. Ties go to the lower index.
pub fn settle(reserve: u32, bids: &[u32]) -> Outcome {
let mut best: Option<(u32, u32)> = None; // (index, bid)
let mut second = 0u32;
for (i, &bid) in bids.iter().enumerate() {
match best {
Some((_, top)) if bid <= top => second = second.max(bid),
Some((_, top)) => {
second = top;
best = Some((i as u32, bid));
}
None => best = Some((i as u32, bid)),
}
}
match best {
Some((winner, top)) if top >= reserve => Outcome::Sold { winner, price: second.max(reserve) },
_ => Outcome::NoSale,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn highest_bid_pays_the_second() {
assert_eq!(settle(100, &[400, 250, 900, 300]), Outcome::Sold { winner: 2, price: 400 });
}
#[test]
fn the_reserve_sets_a_floor() {
assert_eq!(settle(500, &[400, 250, 900, 300]), Outcome::Sold { winner: 2, price: 500 });
assert_eq!(settle(1000, &[400, 250, 900, 300]), Outcome::NoSale);
}
#[test]
fn a_tie_goes_to_the_lower_index() {
assert_eq!(settle(0, &[700, 700]), Outcome::Sold { winner: 0, price: 700 });
}
#[test]
fn no_bids_is_no_sale() {
assert_eq!(settle(0, &[]), Outcome::NoSale);
}
}
$ cd my-guests/auction-core && cargo +1.98.1 test
running 4 tests
test tests::a_tie_goes_to_the_lower_index ... ok
test tests::highest_bid_pays_the_second ... ok
test tests::no_bids_is_no_sale ... ok
test tests::the_reserve_sets_a_floor ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Guest
my-guests/auction/Cargo.toml bergantung pada kedua-dua crate melalui laluan:
[package]
name = "auction"
version = "0.1.0"
edition = "2021"
[dependencies]
guest-sdk = { path = "../../guest-sdk" }
auction-core = { path = "../auction-core" }
[profile.release]
opt-level = 3
lto = true
panic = "abort"
codegen-units = 1
[workspace]
my-guests/auction/src/main.rs:
#![no_std]
#![no_main]
use auction_core::{settle, Outcome};
use guest_sdk::{halt, poseidon2, read_input, read_public, write_output};
const MAX_BIDDERS: usize = 64;
const TAG: u32 = 0x4249_4431; // "BID1", the same tag the commit guest uses
// Output slot 0: what happened.
const SOLD: u32 = 1;
const NO_SALE: u32 = 2;
const BAD_OPENING: u32 = 3;
const TOO_MANY: u32 = 4;
/// Public (fixed at deploy): reserve, n, then n 8-word commitments.
/// Private (per call): for each bidder, the bid and four salt words.
/// Outputs: [status, winner, price, n, 0, 0, 0, 0]; a panic sets slot 7 to 0xdeadbeef.
#[no_mangle]
pub extern "C" fn main() -> ! {
let reserve = read_public(0);
let n = read_public(1) as usize;
if n > MAX_BIDDERS {
finish(TOO_MANY, 0, 0, n as u32);
}
let mut bids = [0u32; MAX_BIDDERS];
for i in 0..n {
let base = (5 * i) as u32;
let bid = read_input(base);
let mut buf = [TAG, i as u32, bid, read_input(base + 1), read_input(base + 2),
read_input(base + 3), read_input(base + 4), 0];
unsafe { poseidon2(buf.as_mut_ptr(), 7) };
let committed = 2 + 8 * i as u32;
for w in 0..8 {
if buf[w] != read_public(committed + w as u32) {
finish(BAD_OPENING, i as u32, 0, n as u32);
}
}
bids[i] = bid;
}
match settle(reserve, &bids[..n]) {
Outcome::Sold { winner, price } => finish(SOLD, winner, price, n as u32),
Outcome::NoSale => finish(NO_SALE, 0, 0, n as u32),
}
}
fn finish(status: u32, winner: u32, price: u32, n: u32) -> ! {
write_output(0, status);
write_output(1, winner);
write_output(2, price);
write_output(3, n);
halt();
}
Setiap laluan keluar melalui finish, yang menulis setiap slot sekali dan berhenti. Had gelung datang daripada segmen awam, yang ditetapkan semasa penempatan. Oleh itu pemanggil tidak dapat menjadikan program membaca bidaan yang lebih sedikit daripada yang telah dikomit.
Bina dan jalankan
$ rand-guest build my-guests/auction --out auction.bin
258 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK
wrote auction.bin and its .sha256 (258 words, hc aa04e4c451e986f086e9fcff53b213eb52e593317c32a979d5e24991164f050d, program id 148ab0c4b9ad5115caab4a2ab433e3e13b5e8fbecbd9d8a9d6ba20c64f942c73)
rand-guest run menerima segmen awam sebagai bendera --public yang berulang. Dalam shell:
PUB=$(for w in $(cat auction-public.txt); do printf -- "--public %s " "$w"; done)
BIDS="--input 400 --input 2654435769 --input 2135587861 --input 4087136352 --input 1559071514
--input 250 --input 461845907 --input 3432918353 --input 3864292196 --input 2246822507
--input 900 --input 3266489909 --input 668265263 --input 374761393 --input 3550635116
--input 300 --input 4252018373 --input 3042594569 --input 1759714724 --input 625341585"
rand-guest run auction.bin $PUB $BIDS --tier 10
out[0] = 1
out[1] = 2
out[2] = 400
out[3] = 4
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 0
cycles 641
tier 10
tier 10: fits (cycles 721 of 1023, Poseidon2 permutations 88 of 128)
Terjual (1) kepada pembida 2 pada harga 400, iaitu bidaan kedua tertinggi, dengan empat pembida dikira. Dua larian yang sepatutnya gagal:
| perubahan | output 0–3 | maksud |
|---|---|---|
| bidaan 250 milik pembida 1 diganti dengan 260 | 3 1 0 0 | BAD_OPENING pada pembida 1; tiada apa-apa lagi diterbitkan |
| harga rizab 1,000 dan bukannya 100 (segmen awam yang berbeza, maka id program yang berbeza) | 2 0 0 4 | NO_SALE: tiada bidaan mencapai harga rizab |
5. Program yang sama dalam C
Direktori guest C mengandungi fail .c dan header anda sendiri. rand-guest build --lang c membekalkan guest.h, _start dan masa larian kecil (Deploy a RISC-V program). Ia mengompil setiap fail .c dalam direktori, jadi fail ujian hos perlu diletakkan di tempat lain.
my-guests/auction-c/auction.h:
#ifndef AUCTION_H
#define AUCTION_H
#include <stdint.h>
/* Outcome of a second-price auction. sold is 0 when no bid reached the reserve. */
struct outcome {
uint32_t sold;
uint32_t winner;
uint32_t price;
};
struct outcome settle(uint32_t reserve, const uint32_t *bids, uint32_t n);
#endif
my-guests/auction-c/settle.c:
#include "auction.h"
/* A second-price (Vickrey) auction. Ties go to the lower index. */
struct outcome settle(uint32_t reserve, const uint32_t *bids, uint32_t n) {
struct outcome o = {0, 0, 0};
if (n == 0) {
return o;
}
uint32_t winner = 0, top = bids[0], second = 0;
for (uint32_t i = 1; i < n; i++) {
if (bids[i] > top) {
second = top;
top = bids[i];
winner = i;
} else if (bids[i] > second) {
second = bids[i];
}
}
if (top >= reserve) {
o.sold = 1;
o.winner = winner;
o.price = second > reserve ? second : reserve;
}
return o;
}
my-guests/auction-c/main.c:
#include "guest.h"
#include "auction.h"
#define MAX_BIDDERS 64
#define TAG 0x42494431u /* "BID1", the same tag the commit guest uses */
enum { SOLD = 1, NO_SALE = 2, BAD_OPENING = 3, TOO_MANY = 4 };
static uint32_t bids[MAX_BIDDERS];
static void finish(uint32_t status, uint32_t winner, uint32_t price, uint32_t n) {
rand_write_output(0, status);
rand_write_output(1, winner);
rand_write_output(2, price);
rand_write_output(3, n);
rand_halt();
}
/* Public (fixed at deploy): reserve, n, then n 8-word commitments.
* Private (per call): for each bidder, the bid and four salt words.
* Outputs: [status, winner, price, n, 0, 0, 0, 0]. */
void main(void) {
uint32_t reserve = rand_read_public(0);
uint32_t n = rand_read_public(1);
if (n > MAX_BIDDERS) {
finish(TOO_MANY, 0, 0, n);
}
for (uint32_t i = 0; i < n; i++) {
uint32_t base = 5 * i;
uint32_t buf[8] = {TAG, i, rand_read_input(base), rand_read_input(base + 1),
rand_read_input(base + 2), rand_read_input(base + 3),
rand_read_input(base + 4), 0};
bids[i] = buf[2];
rand_poseidon2(buf, 7);
for (uint32_t w = 0; w < 8; w++) {
if (buf[w] != rand_read_public(2 + 8 * i + w)) {
finish(BAD_OPENING, i, 0, n);
}
}
}
struct outcome o = settle(reserve, bids, n);
if (o.sold) {
finish(SOLD, o.winner, o.price, n);
}
finish(NO_SALE, 0, 0, n);
}
bids ialah static yang diawalkan kepada sifar, jadi ia berada dalam .bss. Itu tidak menambah perkataan imej, kerana RAM sememangnya terbaca sifar.
Uji peraturan pada hos
settle.c tidak menggunakan syscall, jadi pengompil biasa anda boleh membinanya. my-guests/auction-c-test/test_settle.c:
#include <assert.h>
#include <stdio.h>
#include "../auction-c/auction.h"
int main(void) {
uint32_t bids[] = {400, 250, 900, 300};
struct outcome o = settle(100, bids, 4);
assert(o.sold && o.winner == 2 && o.price == 400);
o = settle(500, bids, 4);
assert(o.sold && o.price == 500);
o = settle(1000, bids, 4);
assert(!o.sold);
uint32_t tie[] = {700, 700};
o = settle(0, tie, 2);
assert(o.winner == 0 && o.price == 700);
puts("settle: 4 checks passed");
return 0;
}
$ cc -Wall -o test_settle my-guests/auction-c-test/test_settle.c my-guests/auction-c/settle.c && ./test_settle
settle: 4 checks passed
Bina dan jalankan
$ rand-guest build --lang c my-guests/auction-c --out auction-c.bin
200 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
OK
wrote auction-c.bin and its .sha256 (200 words, hc 68605aa8d28a863a4a52a25de07ba159327a0fd0145a9dcd5e5aa6e79ab6bea0, program id 5667c343b8848805a908c9009d426164f39d2f927b2c780529a054f0d66aafc1)
$ rand-guest run auction-c.bin $PUB $BIDS --tier 10
out[0] = 1
out[1] = 2
out[2] = 400
out[3] = 4
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 0
cycles 576
tier 10
tier 10: fits (cycles 641 of 1023, Poseidon2 permutations 73 of 128)
Outputnya sepadan dengan guest Rust. Dengan empat pembida, imej C lebih kecil sebanyak 58 perkataan dan lebih pantas sebanyak 65 kitaran; bahagian 7 menunjukkan imbangan beralih pada 64 pembida. Kedua-duanya ialah program yang berbeza dengan hc dan id program yang berbeza. Tempatkan mana-mana satu yang kod sumbernya anda terbitkan.
6. Nota bahasa
Rust
Crate. #![no_std], #![no_main], dan #[no_mangle] pub extern "C" fn main() -> ! yang berakhir dengan halt(). Profil release menetapkan panic = "abort". Jadual [workspace] kosong mengekalkan crate di luar mana-mana ruang kerja induk. Jangan sertakan .cargo/config.toml dan bendera pengompil anda sendiri: rand-guest build menolak yang pertama dan menetapkan kesemua yang kedua (Deploy a Rust program).
Panik. Pengendali panik guest-sdk menulis 0xdeadbeef ke slot 7 dan berhenti. Larian itu tetap larian yang sah dan boleh dibuktikan. Program ini membaca indeks 999 bagi Vec enam elemen selepas menulis slot 0 dan 1:
$ rand-guest run heap.bin --input 6 --input 1 --input 2 --input 3 --input 4 --input 5 --input 6
out[0] = 4
out[1] = 1080033280
out[2] = 0
out[3] = 0
out[4] = 0
out[5] = 0
out[6] = 0
out[7] = 3735928559
cycles 1326
tier 12
Slot 0 dan 1 memegang nilai sebenar, dan slot 7 menyatakan bahawa larian itu panik. Pemanggil yang mengemukakan larian sedemikian memperoleh resit pada rantaian. Sesiapa yang membaca resit mesti menolak slot 7 = 3,735,928,559, atau program tidak boleh sekali-kali panik. Utamakan get(), aritmetik berperiksa dan kod status yang eksplisit pada setiap laluan.
Heap. Secara lalai tiada pengalokasi. alloc berfungsi sebaik sahaja anda menyediakannya. Pengalokasi bump pada arena bersaiz tetap sesuai untuk program yang dijalankan sekali dan keluar:
extern crate alloc;
use alloc::vec::Vec;
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
/// A bump allocator over a fixed arena: allocation moves a pointer forward, free does nothing.
/// One run is one short-lived process, so nothing needs to be reclaimed.
struct Bump {
arena: UnsafeCell<[u8; ARENA]>,
next: UnsafeCell<usize>,
}
const ARENA: usize = 256 * 1024;
unsafe impl Sync for Bump {}
unsafe impl GlobalAlloc for Bump {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let next = &mut *self.next.get();
let start = (*next + layout.align() - 1) & !(layout.align() - 1);
let end = start + layout.size();
if end > ARENA {
return core::ptr::null_mut();
}
*next = end;
(self.arena.get() as *mut u8).add(start)
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
#[global_allocator]
static HEAP: Bump = Bump { arena: UnsafeCell::new([0; ARENA]), next: UnsafeCell::new(0) };
Arena itu bernilai sifar, jadi ia berada dalam .bss dan tidak menambah perkataan imej. Skrip pemaut lalai memberikan 1 MiB RAM, dan arena, statik anda serta tindanan 64 KiB mesti muat di dalamnya. Dengan pengalokasi ini, Vec, collect() dan iter().sum() semuanya terbina dan lulus check. Namun alloc dan kebergantungannya menambah kod: program ujian ini berjumlah 1,341 perkataan, berbanding 258 bagi lelongan. Bahagian 7 menerangkan mengapa saiz program membebankan bajet pembuktian.
Titik terapung. Sasaran ini tiada arahan float, dan Rust menurunkan f32/f64 kepada rutin perisian. Rutin itu berfungsi: min bagi 9, 2, 7, 4 dan 5 yang dikira dalam f64 kembali sebagai bit 5.4f32. Setiap satu memakan banyak kitaran. Gunakan integer atau titik tetap jika boleh.
Crate. Crate no_std daripada crates.io dibina seperti kebergantungan biasa. CRC-32 crate crc bagi hello, rand, yang dijalankan pada emulator, mencetak 2124296908, nilai yang sama seperti yang diberikan oleh zlib.crc32 Python. Crate yang memerlukan std tidak dapat dibina untuk sasaran ini. rand-guest build tidak menghantar --locked, maka komit Cargo.lock anda: tanpanya, binaan semula beberapa bulan kemudian mungkin menyelesaikan crate yang lebih baharu dan menghasilkan hc yang berbeza.
Perangkap unimp: dapatan Csr daripada kod pustaka. Sesetengah kod core dikompil kepada perkataan unimp (0xc0001073, csrrw x0, cycle, x0) pada laluan yang dianggap LLVM tidak boleh dicapai. Mesin ini tiada CSR, jadi check menolak imej itu:
0x00002250 0xc0001073 Csr: CSR instruction: the machine has no CSRs
2613 words against a cap of 4096 (fits); 0 ecall(s) with a non-static a7
REJECTED
wrote heap.bin (the loader refuses it: no hc)
Punca di sini ialah slice::sort_unstable pada Vec yang panjangnya hanya diketahui semasa larian. sort() yang stabil menghasilkan perkara yang sama, manakala sort_unstable pada tatasusunan tetap 16 elemen lulus. Untuk mencari sumber dapatan sedemikian, nyahhimpun ELF guest dan lihat fungsi di sekitar alamat yang dicetak oleh check:
llvm-objdump -d -C my-guests/heap/target/riscv32im-unknown-none-elf/release/heap | grep -B12 unimp
Kemudian gantikan panggilan itu dengan kod anda sendiri (bagi program ini, isihan sisipan yang ringkas). halt() milik guest-sdk berakhir dengan loop {} dan bukannya janji noreturn atas sebab yang sama.
C
- Titik masuk ialah
void main(void), dan ia berakhir denganrand_halt()._startyang disertakan menetapkan tindanan dan memanggilnya. - Tiada libc. Tiada
printf,mallocatau<string.h>. Masa larian menyediakanmemcpy,memmove,memsetdanmemcmp(clang menjana panggilan kepadanya untuk salinan struct dan pengawalan besar) serta empat pembantu pembahagian 64-bit. Kesemuanya ialah gelung bait, jadi salin dan bahagi dalam unit perkataan pada kod yang kerap dijalankan. Untuk heap, tulis pengalokasi bump pada tatasusunanstatic, seperti dalam Rust. - Tingkah laku tak tertakrif tidak dikesan. Tiada pengendali panik. Penulisan di luar sempadan merosakkan memori secara senyap, dan muatan atau simpanan yang tidak sejajar menyebabkan trap, yang bermakna larian itu tidak dapat dibuktikan. Semak indeks berdasarkan had anda sendiri, seperti
main.cmenyemaknterhadapMAX_BIDDERS. - Peraturan direktori. Jangan tambah
guest.hataustart.Sanda sendiri;build --lang cmenolak kedua-duanya. Letakkan fail ujian di luar direktori guest, kerana setiap fail.cdi dalamnya dikompil ke dalam imej. - Clang yang ditetapkan.
rand-guestmenolak sebarang clang selain 23.1.1.RAND_GUEST_CLANG_UNPINNED=1mengatasi had itu, dengan amaran bahawahcanda tidak akan sepadan dengan binaan orang lain.
7. Adakah ia akan terbukti? Bajet
Pembukti (prover) melapik setiap larian kepada satu tier t. Setiap tier mempunyai dua bajet yang bebas, dan rand-guest run --tier t melaporkan kedua-duanya:
- baris: paling banyak
2ᵗ − 1, yang mengira kitaran yang dilaksanakan ditambah baris digest; - pilih atur Poseidon2: paling banyak
2ᵗ⁻³.
| tier | baris | pilih atur Poseidon2 | siling gas tanpa jadual hash | panggilan pada chain 20 |
|---|---|---|---|---|
| 10 | 1,023 | 128 | 1,279 | diterima |
| 12 | 4,095 | 512 | 5,119 | diterima |
| 14 | 16,383 | 2,048 | 20,479 | diterima |
| 16 | 65,535 | 8,192 | 81,919 | ditolak sejak v0.5.6 |
| 18, 20 | 2¹⁸ − 1, 2²⁰ − 1 | 2¹⁵, 2¹⁷ | 327,679, 1,310,719 | ditolak sejak v0.5.6 |
Yuran mengikut gas dan bait, bukan tier. Pada chain 15, satu panggilan membayar 0.001 + call_fee(tier) yang tetap: 0.002 RAND pada tier 10, 0.0021 pada tier 12 dan 0.0022 pada tier 14. Sejak chain 18, setiap larian diukur. Satu baris biasa ialah 1 gas, satu baris serapan Poseidon2 ialah 3, satu pilih atur Keccak ialah 192 dan satu pemampatan SHA-256 ialah 64. Bukti menerbitkan had gas yang diisytiharkan, litar menguatkuasakan bahawa gas larian itu berada pada atau di bawahnya, dan gas yang tepat kekal persendirian. Siling yang boleh diisytiharkan oleh sesuatu tier tanpa jadual hash ialah (2ᵗ − 1) + 2ᵗ⁻², iaitu lajur di atas. Lantai yuran ialah
floor = BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉
dengan BUNDLE_BASE 0.001 RAND, bait ialah bukti panggilan ditambah sampul input, dan harga yang bermula pada 100 unit setiap gas dan 800 unit setiap KiB (satu unit ialah 10⁻⁹ RAND) serta bergerak sedikit pada setiap blok. Pada chain 20 harga itu tidak boleh melebihi 10,000 dan 80,000 unit, dan hanya bait bukti dan sampul panggilan menggerakkan harga bait; pada chain 18 dan 19 tiada siling. Bahagian 9 menunjukkan apa yang dompet bayar secara lalai, dan Example programs mempunyai empat panggilan yang diukur pada chain 18, pada 0.0057 hingga 0.0075 RAND setiap satu.
Tier 14 ialah tier tertinggi yang boleh digunakan oleh sesuatu panggilan. Sejak fullnode v0.5.6 (2026-09-25), nod menolak mana-mana bukti panggilan di atas tier 14 dengan CallTierTooHigh, sebelum ia membina kunci verifier. Peraturan yang sama menjadikan blok yang membawa panggilan sedemikian tidak sah. Had ini melindungi validator yang mempunyai memori 2–4 GB: kunci verifier tier 20 mengambil masa beberapa minit dan beberapa gigabait untuk dibina. rand call tidak menolak panggilan sedemikian sebelum membuat bukti, walaupun pada chain 20 ia mencetak tier itu terlebih dahulu. Jalankan rand-guest run … --tier 14 sebelum anda membuat panggilan, dan jika larian itu tidak muat, susun semula program tersebut. Sebelum pelaksanaan (deploy), rand program deploy --input <word> … (atau --check-call bagi panggilan tanpa input) menjalankan panggilan yang anda rancangkan melalui emulator dan enggan melaksanakan program melainkan ia berada pada tier 14 atau lebih rendah.
Perkara yang menggunakan bajet
Baris digest dan permutasi merupakan kejutan. Sebelum kod anda dijalankan, mesin meng-hash program, input persendirian dan segmen awam ke dalam hc, H_IN dan H_PUB. Setiap satu memerlukan kira-kira satu permutasi dan satu baris bagi setiap empat perkataan. Selain itu, setiap panggilan POSEIDON2 yang anda buat menelan kos kira-kira satu permutasi bagi setiap empat perkataan yang di-hash. Bagi lelongan pada tier 10:
| item | perkataan | permutasi |
|---|---|---|
program (hc) | 258 | kira-kira 65 |
input persendirian (H_IN) | 20 | kira-kira 5 |
segmen awam (H_PUB) | 34 | kira-kira 9 |
| empat semakan komitmen, 7 perkataan setiap satu | 4 × 7 | 8 |
jumlah, seperti yang dilaporkan oleh run | 88 daripada 128 |
Baris: 641 kitaran ditambah baris digest berjumlah 721 daripada 1,023.
Pada chain 20, seperti pada chain 19, kiraan dompet sendiri sedikit lebih tinggi daripada kiraan rand-guest run. Bukti panggilan di sana terikat pada transaksinya (pada chain 20 pengikatan itu turut merangkumi hash genesis, dengan lapan perkataan yang sama), dan pengikatan tersebut dibawa sebagai lapan perkataan tambahan segmen awam: dua baris digest dan dua permutasi tambahan bagi program yang mempunyai segmen awam, dan satu baris serta satu permutasi tambahan bagi program tanpa segmen awam.
Ini menjadikan saiz program sebahagian daripada bajet. Program ujian heap hanya menjalankan 1,183 kitaran, namun 1,341 perkataannya memerlukan 340 permutasi dan menolaknya ke tier 12. Pada 2,048 permutasi tier 14, program yang jauh melebihi kira-kira 8,000 perkataan langsung tidak boleh dipanggil, walau apa pun yang dilakukannya. Pada chain 20, had tepatnya ialah 8,180 perkataan bagi program tanpa segmen awam dan panggilan tanpa input, dan kurang daripada itu jika terdapat salah satunya. Had program pada chain 20 ialah 65,535 perkataan, sama seperti pada chain 15, tetapi chain 19 dan 20 enggan melaksanakan program yang melebihi had itu (ProgramUncallable), sedangkan chain 15 mengambil yuran pelaksanaan bagi program yang tiada sesiapa dapat memanggilnya. Itulah sebabnya ERC-20 terjemahan chain 13 (11,686 perkataan, dipanggil pada tier 16) tidak boleh dilaksanakan mahupun dipanggil pada chain 20. Pastikan program kecil: opt-level = "s" dalam Rust, -Os (sudah ditetapkan) dalam C, sedikit kebergantungan, dan tiada alloc melainkan anda memerlukannya.
Lelongan ini membesar secara linear: setiap pembida tambahan menambah 5 perkataan input, 8 perkataan awam, 2 permutasi untuk semakan, dan kira-kira seratus kitaran. Dengan 64 pembida (bida rawak, 320 perkataan input, 514 perkataan awam), kedua-dua binaan memilih pemenang dan harga yang sama, dan kedua-duanya memerlukan tier 14:
auction.bin tier 14: fits (cycles 6557 of 16383, Poseidon2 permutations 403 of 2048)
auction-c.bin tier 14: fits (cycles 8571 of 16383, Poseidon2 permutations 388 of 2048)
Permutasi tersebut akan muat dalam 512 milik tier 12, tetapi baris tidak muat dalam 4,095 miliknya. Kitaran, bukan hashing, yang menentukan tier di sini. Pada 64 pembida, binaan Rust lebih pantas. opt-level = 3nya mengatasi -Os tetap binaan C apabila gelung menjadi dominan.
Keccak dan SHA-256
keccak dan sha256_compress masing-masing menelan kos satu baris, tetapi bukti yang menggunakan salah satunya membawa jadual tambahan. Pada aeacf31 kedua-duanya menerima rujukan tatasusunan, keccak(&mut state) ke atas [u32; 50] dan sha256_compress(&mut buf) ke atas [u32; 24], manakala 7ef3220 menerima penuding mentah. Sejak v0.5.6, sesuatu panggilan boleh menggunakan paling banyak 128 permutasi Keccak-f dan paling banyak 128 pemampatan SHA-256. Pada chain 20, satu permutasi Keccak turut menelan kos 192 gas dan satu pemampatan SHA-256 menelan 64.
Jadual itu menjadikan bukti lebih besar, dan chain 20 meletakkan had 4,194,304 bait (4 MiB, seperti pada chain 17 hingga 19; had itu ialah 8 MiB pada chain 13 hingga 16) bagi bukti panggilan. Pada chain 18, panggilan tier 12 dengan jadual Keccak diukur pada 3.35 MB, hampir keseluruhan had itu, dan panggilan tier 14 dengan jadual SHA-256 pada 1.91 MB, berbanding 1.50 MB tanpa kedua-duanya (Example programs). Setiap KiB dikenakan caj pada harga bait. Pada chain 15, hanya bait melebihi 2 MiB + 18,432 percuma yang dikenakan caj, pada 1,000 unit setiap KiB (Fees). Gunakan Poseidon2 melainkan anda mesti memadankan hash luaran.
Memori dan masa untuk membuat bukti
Masa dan memori pembuktian meningkat dengan mendadak mengikut tier. Panggilan tier 10 dibuktikan dalam beberapa saat pada komputer riba. Pada chain 20, setiap pelaksanaan dan setiap panggilan turut membawa dua bukti di sisi bundle: kebenaran perbelanjaan tier 10 (beberapa saat) dan bundle tier 14 yang membayar yuran (kira-kira dua minit pada komputer riba). Empat panggilan yang diukur pada chain 18 masing-masing mengambil masa antara tiga hingga lima setengah minit dan mencapai puncak 6.1 hingga 6.6 GB (Example programs). Pada chain 15, bundle ialah satu bukti tier 14 sekitar 100 s dan sekitar 5.7 GB. Validator and aggregator hardware mengandungi angka yang diukur.
8. Deploy
Testnet ialah chain 20. Pelaksanaan memerlukan dompet rand daripada fullnode v0.6.8 atau lebih baharu, sebuah kunci dan sedikit RAND (Introduction; faucet chain 20, seperti chain 19, hanya mencetak kepada 16 dompet yang disenaraikan dalam genesisnya). Dompet v0.6.7 tidak boleh menghantar pada chain 20: genesis chain 20 menetapkan binding_domain: 1, maka setiap pengikatan bukti dan mesej bertandatangan merangkumi hash genesis, yang tidak dimasukkan oleh dompet v0.6.7. Titik akhir awam https://rpc.randprotocol.org mencukupi untuk menyegerakkan dompet, melaksanakan dan memanggil (setup). Lelongan dilaksanakan bersama segmen awamnya:
rand fee deploy 258 --public-words 34 # 0.001 + 0.0001 × (258 + 34) = 0.0302 RAND
rand program deploy auction.bin --public auction-public.txt
program deploy mencetak id program, hc, bilangan perkataan, serta bilangan perkataan dan digest input awam. Ia menyemak program berbanding had chain dan input awam berbanding max_program_public_words (32,768 pada chain 20) sebelum membuat sebarang bukti. Kemudian ia membuktikan bundle pindahan kendiri bernilai sifar yang membayar yuran, menghantarnya, dan menunggu pengesahan commit. Pada chain 20, seperti sejak chain 17, itu ialah dua bukti, masing-masing dengan baris outputnya sendiri: kebenaran perbelanjaan tier 10 (beberapa saat, kira-kira 1.37 MB) dan bundle tier 14 (kira-kira dua minit, kira-kira 1.5 MB). Pada chain 15, ia ialah satu bukti tier 14 sekitar 100 s dan kira-kira 1.42 MB. Yuran pelaksanaan tidak mempunyai komponen gas. Lelongan itu belum dilaksanakan pada chain 18 hingga 20; An anonymous vote menunjukkan pelaksanaan dengan segmen awam pada chain 18 seperti yang dicetak oleh dompet.
Id program berubah dengan --public. rand-guest mencetak 148ab0c4…2c73 untuk kod sahaja. Pelaksanaan dengan segmen awam disimpan di bawah blake3("rand-program-2", base_pc ‖ len ‖ words ‖ len ‖ public), yang turut mengikat rizab dan empat komitmen. Gunakan id yang dicetak oleh program deploy. Lelongan kedua dengan kod yang sama tetapi komitmen berbeza ialah id program yang berbeza.
rand program show <auction id> # base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest
code_hash ialah hc yang dicetak oleh rand-guest, dieja secara berbeza. rand-guest mencetak setiap satu daripada lapan perkataan digest dalam big-endian, manakala dompet, rand program show dan rand_getProgram mencetak bait setiap perkataan dalam little-endian, maka setiap kumpulan lapan digit heks dibalikkan susunan baitnya. Bagi binaan di atas, aa04e4c4…050d (Rust) muncul sebagai c4e404aa…4f16, dan 68605aa8…bea0 (C) sebagai a85a6068…b69a.
9. Call
Penjual membuat panggilan dengan kesemua 20 perkataan persendirian:
rand call <auction id> --expect-public auction-public.txt \
--input 400 --input 2654435769 --input 2135587861 --input 4087136352 --input 1559071514 \
--input 250 --input 461845907 --input 3432918353 --input 3864292196 --input 2246822507 \
--input 900 --input 3266489909 --input 668265263 --input 374761393 --input 3550635116 \
--input 300 --input 4252018373 --input 3042594569 --input 1759714724 --input 625341585
Dompet mengambil kod dan segmen awam program, dan menyemak bahawa kedua-duanya di-hash kepada id tersebut. --expect-public juga menolak, sebelum membuat bukti, jika segmen awam itu bukan perkataan fail tersebut. Dompet kemudian menjalankan panggilan dalam emulator dan mencetak gas yang tepat, had yang akan diisytiharkan, tier (yang terkecil yang memuatkan larian, iaitu tier 10 di sini), dan yuran:
gas: <exact> (declaring <limit>, tier <t>)
fee <amount> RAND (incl. two price steps of headroom)
Ia menutup 20 perkataan input dalam sampul surat yang hanya boleh dibuka oleh pemanggil, membuktikan larian secara setempat, membayar yuran melalui bundle, menghantar, menunggu dan mencetak resit. Pada chain 20, itu ialah tiga bukti: panggilan, kebenaran perbelanjaan tier 10 dan bundle tier 14.
Kos panggilan. Pada chain 15, panggilan ini membayar 0.001 + call_fee(10) = 0.002 RAND. Pada chain 20, dompet membayar lantai daripada bahagian 7, yang dikira sebelum bukti wujud, dengan tiga lalai:
- had limit yang diisytiharkan ialah gas tepat yang dibundarkan ke atas kepada gandaan
2^(tier − 2): 256 pada tier 10, 1,024 pada tier 12, 4,096 pada tier 14.--gas-limit maxmengisytiharkan siling tier tersebut dan--gas-limit Nmengisytiharkan angka yang tepat; - bukti dinilai pada had rantaian sebanyak 4,194,304 bait, bukan pada saiz sebenarnya, kerana bukti panggilan pada chain 20 terikat pada transaksinya dan yuran mesti ditetapkan terlebih dahulu;
- ruang lebihan sebanyak dua langkah harga, iaitu faktor 1.125², kerana panggilan itu sampai dua atau tiga blok selepas harganya dikira.
--fee mengatasi nilai lalai, dan rand fee call <tier> --gas <limit> --bytes <B> meminta nod itu sendiri memberikan harga lantai. Panggilan bagi lelongan itu belum dibuat pada chain 18 hingga 20, maka baris gas dan yurannya tidak ditunjukkan di sini. Undian tanpa nama mempunyai panggilan lengkap pada chain 18 seperti yang dipaparkan oleh dompet itu, pada 0.0062 RAND.
Resit itu memuatkan tier, H_IN, H_PUB dan output [1, 2, 400, 4, 0, 0, 0, 0]: perkataan yang sama seperti yang dicetak oleh emulator, kerana bukti itu ialah bukti bagi pelaksanaan yang sama. Keempat-empat bida dan salt tidak berada pada rantaian. --auditor <rand1…> turut mengunci input kepada juruaudit yang dinamakan, dan --no-envelope tidak menerbitkan transkrip langsung, yang tidak boleh dibatalkan (Programs).
10. Semak hasilnya
Seorang pembida, atau sesiapa sahaja, boleh menyemak lelongan itu tanpa mempercayai penjual:
- Kod. Bina semula sumber yang diterbitkan dengan
rand-guest buildpada commit circuits yang sama, Rust 1.98.1 (atau clang 23.1.1). Bandingkanhcdengancode_hashdaripadarand program showataurand_getProgram, dengan mengambil kira ejaan bait-terbalik (bahagian 8). Binaan ini boleh diulang dengan hasil yang sama, maka kedua-duanya sepadan pada mana-mana mesin. - Segmen awam. Dapatkannya dengan
rand_getProgramPublic, atau jalankanrand call --expect-public. Semak bahawa komitmen anda sendiri berada pada indeks anda. - Resit.
rand receipt <tx hash>ataurand_getReceipt: status1, pemenang dan harga, slot 7 bukan0xdeadbeef, danprogramsama dengan id lelongan itu. - Input, jika anda berhak ke atasnya.
rand open-call <tx hash>oleh penjual (atau--as-auditoroleh juruaudit) membuka input termeterai, menyemaknya denganH_IN, menjalankan semula program, dan keluar dengan kod bukan sifar jika terdapat sebarang ketakpadanan. Gunakan dompet v0.6.8: sejak v0.6.8 larian semula dibuat ke atas segmen awam program dan pengikatan panggilan, maka ia berfungsi untuk lelongan (isu fullnode #116). Dalam v0.6.7open-callberhenti denganre-running the program on these inputs failed: PublicIndex(0)bagi mana-mana program yang digunakan dengan segmen awam.
Bukti itu menjamin bahawa peraturan telah dikenakan pada pembukaan bida yang dikomit. Ia tidak memindahkan wang. Pemenang membayar penjual dengan rand send biasa, dan resit itulah yang dirujuk oleh kedua-dua pihak.
Penyelesaian masalah
| symptom | cause and fix |
|---|---|
binaan gagal pada panggilan poseidon2(…), keccak(…) atau sha256_compress(…) yang ditulis untuk commit lebih lama | pada aeacf31 poseidon2 ialah unsafe fn dan dua yang lain menerima &mut [u32; 50] dan &mut [u32; 24] (bahagian 3 dan 7) |
check melaporkan Syscall dengan a7 = 7 | guest memanggil poseidon2_len; rand-guest check pada aeacf31 tidak menyenaraikan syscall 7 (isu zkp-circuits #1) |
check melaporkan Csr pada 0xc0001073 | kod pustaka dikompil menjadi unimp; cari fungsi itu dengan llvm-objdump dan gantikan panggilannya (bahagian 6) |
run menyatakan larian itu tidak muat dalam mana-mana tier, atau memerlukan lebih daripada tier 14 | terlalu banyak baris atau permutasi Poseidon2; kecilkan program, kurangkan hashing, atau pecahkan kerja kepada beberapa panggilan dengan membawa digest antara panggilan tersebut |
| slot 7 = 3735928559 | guest Rust mengalami panic; output sebelumnya tidak boleh dipercayai |
trap: DoubleWrite(n) | slot output n ditulis dua kali; salurkan setiap keluar melalui satu fungsi |
trap: InputIndex(i) | program membaca input persendirian yang tidak dibekalkan oleh pemanggil; baca dan semak kiraan terlebih dahulu |
trap: … pada pemuatan atau penyimpanan | alamat tidak sejajar atau di luar julat; dalam C, indeks tatasusunan melepasi had |
invalid value '0x…' for '--input' | perkataan input ialah perpuluhan pada baris perintah |
panggilan ditolak dengan CallTierTooHigh | bukti melebihi tier 14; semak dengan rand-guest run --tier 14 sebelum memanggil |
hc yang dibina semula berbeza daripada yang digunakan | commit circuits, rantai alat atau versi kebergantungan berbeza; sematkan ketiga-tiganya dan komit Cargo.lock. Jika hanya ejaan yang berbeza, setiap kumpulan lapan digit heks dibalikkan baitnya (bahagian 8) |
rand open-call berhenti dengan PublicIndex(0) | program mempunyai segmen awam dan dompet ialah v0.6.7, yang larian semulanya tidak menghantar apa-apa (isu fullnode #116); gunakan v0.6.8 |
… is not inside a circuits checkout | alihkan direktori guest ke dalam checkout circuits (Deploy a Rust program) |