دا پاڼه هر هغه څه بیانوي چې خپل پروګرام په Rand باندې د ځای پر ځای کولو لپاره ورته اړتیا لرئ: پروګرام څه کېدای شي، هغه څه چې پروګرام یې لولي او خپروي څنګه ډیزاین کړئ، او یوه بشپړه بېلګه چې دوه ځله لیکل شوې ده، یو ځل په Rust او یو ځل په C. بیا ازموینه، د ثبوت جوړولو بودیجه، ځای پر ځای کول، بلل، او دا بیانوي چې هر څوک پایله څنګه کتلی شي. لاندې د rand-guest هره وتنه له یوې ریښتینې اجرا څخه ده او هماغسې راوړل شوې ده لکه څنګه چې چاپ شوې وه. اجراوې د 2026-09-25 په نېټه د circuits په کمېټ 7ef3220 ترسره شوې. testnet اوس chain 20 دی، چې وسیلې یې، لکه د chain 18 او 19 په څېر، په aeacf31 جوړې شوې دي (څانګه feat/v067-circuits؛ fullnode v0.6.8 هم همدا کمېټ تړلی دی)، او وتنې هلته بیا نه دي اجرا شوې. پاڼه وايي چې SDK یا chain چېرته توپیر لري.
دوه لنډ لارښودونه چټکه لار ده: یو Rust پروګرام ځای پر ځای کړئ او یو RISC-V پروګرام ځای پر ځای کړئ. دا پاڼه فرض کوي چې تاسو د Rust لارښود لومړی ګام بشپړ کړی دی: د randprotocol/zkp-circuits یوه کاپي په aeacf31 کې، Rust 1.98.1 د riscv32im-unknown-none-elf هدف سره، د rand-guest باینري، او د C لپاره clang 23.1.1 او llvm-tools. د RISC-V ISA پخپله د ماشین لپاره مرجع ده. بېلګه پروګرامونه له دې ځایه د لویو پروګرامونو سره مخ ته ځي او دا ښيي چې د chain 18 د ګاز اندازه کولو څه بدل کړل. دا پاڼه RPL-1 پروګرامونه بیانوي، چې Call یې بې له حالت ساتلو اجرا کوي؛ chain 20 د RPL-2 پروګرام حالت هم لري، یعنې حجرې او یو والټ چې د Invoke په واسطه بدلېږي (پروګرامونه)، او بشپړې بېلګې یې په randprotocol/example-programs کې دي.
1. پروګرام څه کېدای شي
پروګرام هر هغه محاسبه ده چې له دې بڼې سره برابره وي:
- د ننوت په توګه 32-بیټي کلمې لولي.
- د وتنې په توګه تر ډېره اته 32-بیټي کلمې خپروي.
- ټاکلی دی: ورته ننوتونه تل ورته وتنې ورکوي.
- د خپلې کچې د بودیجې دننه ودرېږي (اوومه برخه).
د دغو حدودو دننه هر څه لیکلی شئ. لوپونه، تکرار، جوړښتونه، اریې، 64-بیټي حساب، crates او که تخصیص کوونکی راوړئ، heap هم لرئ. خو ماشین ساده RV32IM دی، او لاندې یې هېڅ عامل سیستم نشته:
| تاسو یې لرئ | تاسو یې نه لرئ |
|---|---|
د RV32IM د بشپړو عددونو لارښوونې، په ګډون د MUL/DIV/REM | د اعشاري عددونو سختافزار (د Rust سافټوېري اعشاري عددونه کار کوي، خو ورو) |
په ډیفالټ linker script کې له 0x1000 څخه تر 1 MiB پورې RAM، د 64 KiB stack سره | heap، مګر دا چې تخصیص کوونکی برابر کړئ (شپږمه برخه) |
| Poseidon2، Keccak-f او SHA-256 د syscall په توګه | فایلونه، شبکه، ساعتونه، تصادفیت، threads، std، libc |
د no_std Rust crates او پخپله بشپړ C | داسې حالت چې له یوې بللو څخه تر بلې پاتې شي |
| اته عامې وتنې کلمې | د پروګرام له دننه څخه د RAND یا ټوکنونو د لېږدولو لار |
وروستی قطار د پروګرام د ډیزاین پر مهال تر ټولو ډېر اهمیت لري. وتنې معلومات دي، اغېزې نه دي. یوه بلنه ثابتوي چې یو پروګرام چې ټاکلی ډایجسټ لري، پر ځینو خصوصي ننوتونو اجرا شو او اته کلمې یې خپرې کړې. chain ثبوت تاییدوي او اته کلمې په یوه رسید کې ساتي. هېڅ بیلانس نه بدلوي او هېڅ شی نه پیلوي. د کلمو مانا د پروګرام، د هغه د بلونکي، او د هر هغه چا ترمنځ ټاکل کېږي چې رسید لولي. پروګرامونه هغه څه لړلیک کوي چې chain یې لا نه ملاتړ کوي.
2. لومړی انٹرفېس ډیزاین کړئ
پروګرام درې چینلونه لري. مخکې له دې چې هر کوډ ولیکئ، وټاکئ چې پر هر یوه څه ځي.
| چینل | کله ټاکل کېږي | څنګه لوستل کېږي | څوک یې ویني | پر chain 20 حد |
|---|---|---|---|---|
| خصوصي ننوت | په هره بلنه کې | read_input(i) / rand_read_input(i) | یوازې بلونکی، او هر هغه څوک چې بلونکی ورته د بلنې کیلي ورکړي | 16,071 کلمې (د مهر شوي ننوت لفافې حد) |
| عامه برخه | یو ځل، د ځای پر ځای کولو پر مهال | read_public(i) / rand_read_public(i) | هر څوک؛ د پروګرام id یې تړي | 32,768 کلمې |
| وتنې | د اجرا په واسطه | write_output(slot, w) / rand_write_output | هر څوک، په رسید کې | 8 کلمې |
خصوصي ننوتونه پخپله د نړۍ په اړه هېڅ نه ثابتوي
خصوصي ننوت د مالګې لرونکې ژمنې H_IN په واسطه له ثبوت سره تړل شوی دی، نو له ثبوت جوړولو وروسته نه شي بدلېدای. خو پروګرام هېڅ لار نه لري چې پوه شي ننوت رښتیا دی. بلونکی کولی شي هر عدد ولیکي. د دې لپاره چې خصوصي ننوت څه مانا ولري، د پروګرام دننه یې له کوم عامه شي سره وتړئ:
- یوه ژمنه چې مخکې خپره شوې وي (په عامه برخه کې، یا له چینل بهر تاییدوونکي ته ورکړل شوې وي) او خصوصي ننوت باید هغه پرانیزي، لکه په لاندې لیلام کې؛
- یوه Merkle ریښه چې خصوصي پاڼه باید د هغې لاندې ثابته شي (Poseidon2 د دې لپاره syscall دی)؛
- یو ډایجسټ چې تاییدوونکی یې دمخه لري، چې پروګرام یې بیا محاسبه کوي او د وتنې په توګه یې خپروي.
دا د محرمانه پروګرامونو لپاره مرکزي ډیزاین مهارت دی. ثبوت تاییدوونکي ته وايي چې څه محاسبه شوي. له عامه معلوماتو سره تړاو ورته وايي چې پر څه باندې.
معلومات د کلمو په توګه کوډ کول
هر څه د u32 په توګه لېږدول کېږي:
| معلومات | کوډ کول |
|---|---|
| د بایټونو لړۍ | د اوږدوالي یوه کلمه، بیا بایټونه په هره کلمه کې څلور، little-endian، وروستۍ کلمه په صفرونو ډکه (هغه بڼه چې والټ یې د ELF عامه ننوت لپاره کاروي) |
یو u64 یا مقدار | دوه کلمې، لومړی ټیټه بیا لوړه |
| نښه لرونکی بشپړ عدد | په یوه کلمه کې د دوه ګوني بشپړونې بڼه |
| لویه پایله | د هغې Poseidon2 ډایجسټ: 8 کلمې، چې ټولې وتنې ډکوي؛ یا یو څو لنډیز کلمې او یوه لنډه ژمنه خپره کړئ |
د کمانډ لاین وسیلې ننوت کلمې په لسیزه بڼه اخلي: rand-guest run --input او rand call --input د 0x… بڼه نه مني. د rand program deploy --public لپاره د عامې برخې فایل هم لسیزه او هم 0x شپاړسګونې بڼه مني.
وتنو ته بڼه ورکړئ
- ځای 0 د حالت کلمه ده. هرې پایلې ته یو عدد ورکړئ، په ګډون د ناکامیو: داسې پرانیستل چې برابر نه وي، ډېر توکي، له حد بهر ننوت. یوه ناکامه ازموینه باید بیا هم په پاکه توګه د یو حالت سره ودرېږي، ترڅو بلونکی داسې ثبوت ترلاسه کړي چې ووايي څه غلط شول.
- په هر ځای کې تر ډېره یو ځل لیکل کېږي. په ورته ځای کې دویم ځل لیکل یو ټریپ دی (په ایمولېټر کې
DoubleWrite(0))، او هغه اجرا چې ټریپ شي نه شي ثابتېدای. هغه ځایونه چې هېڅکله یې ونه لیکئ، صفر لوستل کېږي. - په Rust کې ځای 7 خالي وساتئ. یو Rust guest چې panic شي،
0xdeadbeef(3,735,928,559) په ځای 7 کې لیکي او ودرېږي. هغه وتنې چې یې له panic مخکې لیکلې وې په خپل ځای پاتې کېږي، نو لوستونکی باید د نورو له باور کولو مخکې ځای 7 وګوري (شپږمه برخه). - له هغو ننوتونو هاخوا لوستل چې درکړل شوي ټریپ دی (په ایمولېټر کې
InputIndex(5)). لومړی شمېر ولولئ او وې ازمویئ.
3. کار شوې بېلګه: د مهر شوو داوطلبیو لیلام
یو پلورونکی غواړي د دویمې بیې لیلام پرته له دې ترسره کړي چې بایلونکې داوطلبۍ خپرې کړي:
- هر داوطلب یوه داوطلبي او یوه تصادفي 128-بیټي مالګه غوره کوي، او پلورونکي ته یوه ژمنه ورکوي: Poseidon2 پر
("BID1", bidder index, bid, salt)باندې. - پلورونکی د لیلام پروګرام د لږترلږه بیې او ټولو ژمنو سره د هغه د عامې برخې په توګه ځای پر ځای کوي. اوس د پروګرام id دا لیلام نوموي او بل هېڅ نه.
- هر داوطلب خپله داوطلبي او مالګه په خصوصي توګه پلورونکي ته لېږي.
- پلورونکی پروګرام د ټولو داوطلبیو او مالګو سره د خصوصي ننوت په توګه بولي. پروګرام هره پرانیستنه د هغې له ژمنې سره ازمويي، ګټونکی ټاکي، او
[status, winner, price, bidder count]خپروي.
chain پوهېږي چې څوک وګټل او په کومه بیه. بایلونکې داوطلبۍ هېڅکله نه ویني. هغه پلورونکی چې هره داوطلبي بدله کړي، د پایلې پر ځای BAD_OPENING ترلاسه کوي، ځکه چې ژمنې د پروګرام په id کې ثابتې دي.
هر څه د circuits د کاپي دننه دي، ځکه چې rand-guest build د guest له ډایرکټري څخه پورته لټون کوي ترڅو guest-sdk ومومي:
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
ایمولېټر د هش حسابماشین په توګه
داوطلبانو ته د Goldilocks پر ساحه Poseidon2 په کار دی، هماغسې لکه څنګه چې ماشین یې محاسبه کوي. تر ټولو ساده سمه وسیله یو کوچنی guest دی چې دا محاسبه کوي او پر ایمولېټر اجرا کېږي. یو ډایجسټ اته کلمې دی، چې دقیقاً د وتنې د ځایونو شمېر دی.
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) په خپل ځای کې n کلمې هش کوي او 8-کلمهییز ډایجسټ پر ptr..ptr+8 لیکي، نو بفر باید لږترلږه اته کلمې ولري که څه هم یوازې اووه هش کېږي. د "BID1" ټګ دا ډایجسټونه د Poseidon2 له هر بل کارونې جلا ساتي.
په aeacf31 کې poseidon2 یو unsafe fn دی، ځکه چې خام پوینټر اخلي، نو بلنه یې په یوه unsafe بلاک کې ده. په 7ef3220 کې، چېرته چې لاندې وتنې تولید شوې، دا یو خوندي فنکشن و او همدا کرښه پرته له بلاک لیکل شوې وه. sponge ډکونه نه کوي: [a] او [a, 0] یو شان هش کېږي. دلته دا زیان نه لري، ځکه هر پیغام اووه کلمې دی او په ټګ پیلېږي. د هغه پیغام لپاره چې اوږدوالی یې بدلېدای شي، SDK په aeacf31 کې poseidon2_len(buf, n) لري، چې اوږدوالی تړي (syscall 7)، خو په هماغه کمېټ کې rand-guest check syscall 7 ردوي، نو داسې guest لا نه جوړېږي (د zkp-circuits مسئله #1). د دې پر ځای اوږدوالی په پیغام کې واچوئ.
$ 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
د cargo کرښې دلته او لاندې د جوړونې له وتنو پرېښودل شوې دي. د unaligned-scalar-mem خبرداری چې cargo یې چاپوي تمه کېږي (یو Rust پروګرام ځای پر ځای کړئ).
څلور داوطلبان، د دغو داوطلبیو او مالګو سره:
| داوطلب | داو | مالګه (څلور کلمې) |
|---|---|---|
| 0 | 400 | 2654435769 2135587861 4087136352 1559071514 |
| 1 | 250 | 461845907 3432918353 3864292196 2246822507 |
| 2 | 900 | 3266489909 668265263 374761393 3550635116 |
| 3 | 300 | 4252018373 3042594569 1759714724 625341585 |
که commit.bin د هر داوطلب لپاره یو ځل وچلول شي او لږترلږه بیه (100)، شمېر (4) او څلور ډایجسټونه په ترتیب سره ولیکل شي، عامه برخه، auction-public.txt، په 34 کلمو سره لاس ته راځي:
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
په یوه رښتیني لیلام کې هر داوطلب commit.bin په خپل ماشین چلوي، او مالګه له یوې رښتینې تصادفي سرچینې څخه راځي.
4. پروګرام په Rust کې
قاعده، د یوې داسې کتابتون په توګه چې ازمویل یې شئ
هغه منطق چې syscall ته اړتیا نه لري په خپل جلا no_std کرېټ کې وساتئ. guest-sdk له riscv32 پرته د بل هېڅ هدف لپاره له جوړېدو انکار کوي، نو د میلمه پروګرام کرېټ ستاسو په لپټاپ cargo test نشي چلولی. هغه کتابتون کرېټ چې په guest-sdk تړلی نه وي، دا کار کولی شي.
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
میلمه پروګرام
my-guests/auction/Cargo.toml د لارې له مخې په دواړو کرېټونو تړلی دی:
[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();
}
د وتلو هره لاره له finish څخه تېرېږي، چې هر سلاټ یو ځل لیکي او بیا درېږي. د لوپ حد له عامې برخې څخه راځي، چې د ځای پر ځای کولو پر مهال ثابتېږي. نو یو غوښتونکی نشي کولی پروګرام دې ته اړ کړي چې له ژمنو شویو داوونو کم داوونه ولولي.
جوړول او چلول
$ 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 عامه برخه د تکرار شویو --public بیرغونو په توګه اخلي. په یوه شېل کې:
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)
پلورل شو (1) داوطلب 2 ته په 400، چې دوهم لوړ داو دی، او څلور داوطلبان شمېرل شوي. دوه چلونې چې باید ناکامې شي:
| بدلون | وتنې 0–3 | مانا |
|---|---|---|
| د داوطلب 1 داو 250 په 260 بدل شو | 3 1 0 0 | BAD_OPENING په داوطلب 1 کې؛ نور هېڅ نه خپرېږي |
| لږترلږه بیه د 100 پر ځای 1,000 (بله عامه برخه، نو د پروګرام بل id) | 2 0 0 4 | NO_SALE: هېڅ داو لږترلږه بیې ته ونه رسېد |
5. همدا پروګرام په C کې
د C میلمه پروګرام فولډر .c فایلونه او ستاسو خپل سرلیک فایلونه لري. rand-guest build --lang c guest.h، _start او یو کوچنی runtime برابروي (یو RISC-V پروګرام ځای پر ځای کړئ). دا په فولډر کې هر .c فایل کمپایل کوي، نو د کوربه ازموینې فایلونه باید بل ځای کې وي.
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 یو صفر ته پیل شوی static دی، نو په .bss کې ځای نیسي. دا د انځور هېڅ کلمه نه لګوي، ځکه چې RAM له مخکې صفر لوستل کېږي.
قاعده په کوربه ماشین وازمویئ
settle.c هېڅ syscall نه کاروي، نو ستاسو عادي کمپایلر یې جوړولی شي. 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
جوړول او چلول
$ 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)
وتنې د Rust میلمه پروګرام له وتنو سره سمون لري. د څلورو داوطلبانو سره، د C انځور 58 کلمې کوچنی او 65 سایکله ګړندی دی؛ اووه برخه ښيي چې تله په 64 داوطلبانو کې اوړي. دا دوه بېل پروګرامونه دي چې بېل hcونه او د پروګرام بېل idګانې لري. هغه یو ځای پر ځای کړئ چې سرچینه یې خپروئ.
6. د ژبو یادښتونه
Rust
کرېټ. #![no_std]، #![no_main]، او یو #[no_mangle] pub extern "C" fn main() -> ! چې په halt() پای ته رسېږي. د release پروفایل panic = "abort" ټاکي. یو خالي [workspace] جدول کرېټ له هر مور workspace څخه بهر ساتي. .cargo/config.toml او خپل کمپایلر بیرغونه مه اچوئ: rand-guest build له لومړي څخه انکار کوي او دوهم ټول پخپله ټاکي (یو Rust پروګرام ځای پر ځای کړئ).
پانیکونه. د guest-sdk د پانیک سمبالوونکی 0xdeadbeef په سلاټ 7 کې لیکي او درېږي. چلونه بیا هم یوه معتبره او د ثبوت وړ چلونه ده. دا پروګرام د 0 او 1 سلاټونو له لیکلو وروسته د یوه شپږ عنصري Vec شاخص 999 لولي:
$ 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
سلاټونه 0 او 1 رښتیني ارزښتونه لري، او سلاټ 7 وايي چې چلونه پانیک شوې. هغه غوښتونکی چې داسې چلونه وسپاري، پر چین رسید ترلاسه کوي. څوک چې رسیدونه لولي باید سلاټ 7 = 3,735,928,559 رد کړي، یا پروګرام باید هېڅکله پانیک نه شي. په هره لاره کې get()، کنټرول شوې حساب او څرګند حالت کوډونه غوره کړئ.
هیپ. په ډیفالټ ډول هېڅ تخصیص کوونکی نشته. alloc هغه وخت کار کوي چې تاسو یو برابر کړئ. پر یوه ثابت ډګر یو bump تخصیص کوونکی هغه پروګرام ته مناسب دی چې یو ځل چلېږي او وځي:
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) };
ډګر صفر شوی دی، نو په .bss کې پروت دی او د انځور هېڅ کلمه نه زیاتوي. ډیفالټ linker سکرېپټ 1 MiB RAM ورکوي، او ډګر، ستاسو statics او د 64 KiB سټیک باید په هغه کې ځای شي. د دې تخصیص کوونکي سره، Vec، collect() او iter().sum() ټول جوړېږي او check تېروي. خو alloc او د هغه تړاوونه کوډ زیاتوي: دا ازموینیز پروګرام 1,341 کلمې دی، د لیلام د 258 کلمو په پرتله. اووه برخه تشریح کوي چې ولې د پروګرام اندازه د ثبوت بودیجه لګوي.
خوځنده ټکی. هدف د float لارښوونې نه لري، او Rust f32/f64 په سافټویري روټینونو اړوي. دا کار کوي: د 9، 2، 7، 4 او 5 منځنۍ چې په f64 کې محاسبه شوه، د 5.4f32 د بټونو په توګه بېرته راغله. هر یو یې ډېر سایکلونه لګوي. هر چېرې چې کولی شئ، بشپړ عددونه یا ثابت ټکی وکاروئ.
کرېټونه. د crates.io څخه no_std کرېټونه د هر تړاو په څېر جوړېږي. د crc کرېټ د hello, rand CRC-32، چې په emulator وچلول شو، 2124296908 چاپ کړ، هماغه ارزښت چې د Python zlib.crc32 یې ورکوي. هغه کرېټونه چې std ته اړتیا لري د دې هدف لپاره نه جوړېږي. rand-guest build --locked نه ورکوي، نو خپل Cargo.lock commit کړئ: له هغه پرته، میاشتې وروسته یو بیا جوړونه کېدای شي یو نوی کرېټ وټاکي او بل hc تولید کړي.
د unimp جال: له کتابتون کوډ څخه یوه Csr موندنه. د core ځینې کوډ په هغه لاره چې LLVM یې د نه رسېدو وړ ګڼي، په یوې unimp کلمې (0xc0001073, csrrw x0, cycle, x0) کمپایل کېږي. ماشین CSRs نه لري، نو check انځور ردوي:
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)
دلته ستونزه slice::sort_unstable وه پر یوه Vec چې اوږدوالی یې یوازې د چلونې پر مهال معلومېږي. ثابت sort() هم همداسې وکړل، په داسې حال کې چې پر یوه ثابت 16 عنصري سرې sort_unstable تېر شو. د داسې موندنې د سرچینې موندلو لپاره، د میلمه پروګرام ELF disassemble کړئ او هغه پته شاوخوا فنکشن وګورئ چې check چاپ کړې:
llvm-objdump -d -C my-guests/heap/target/riscv32im-unknown-none-elf/release/heap | grep -B12 unimp
بیا هغه غوښتنه په خپل کوډ بدله کړئ (د دې پروګرام لپاره، یو لنډ insertion sort). د همدې دلیل لپاره د guest-sdk خپل halt() په loop {} پای ته رسېږي، نه د noreturn په ژمنه.
C
- د ننوتلو ټکی
void main(void)دی، او پهrand_halt()پای ته رسېږي. بنډل شوی_startسټیک ټاکي او هغه رابولي. - هېڅ libc نشته. نه
printf، نهmallocاو نه<string.h>. runtime memcpy،memmove،memsetاوmemcmpبرابروي (clang د struct کاپیو او لویو پیل کوونکو لپاره دوی ته غوښتنې تولیدوي) او څلور د 64-bit وېش مرستندویه فنکشنونه. ټول یې د بایټ لوپونه دي، نو په ګرم کوډ کې په کلمو کاپي او وېش وکړئ. د هیپ لپاره، لکه په Rust کې، پر یوهstaticسرې یو bump تخصیص کوونکی ولیکئ. - ناتعریف شوی چلند نه نیول کېږي. هېڅ پانیک سمبالوونکی نشته. له حد بهر لیکنه حافظه په چوپه خرابوي، او یوه نابرابره بارونه یا ساتنه جال جوړوي، چې مانا یې دا ده چې چلونه ثابتېدای نشي. شاخصونه د خپلو حدونو په وړاندې وڅېړئ، لکه څنګه چې
main.cnدMAX_BIDDERSپه وړاندې څېړي. - د فولډر قواعد. خپل
guest.hیاstart.Sمه زیاتوئ؛build --lang cله دواړو انکار کوي. د ازموینې فایلونه د میلمه پروګرام له فولډر بهر وساتئ، ځکه چې په هغه کې هر.cفایل په انځور کې کمپایل کېږي. - ټاکل شوی clang.
rand-guestله 23.1.1 پرته له هر clang انکار کوي.RAND_GUEST_CLANG_UNPINNED=1دا لغوه کوي، د دې خبرداري سره چې ستاسوhcبه د بل چا له جوړونې سره سمون ونه لري.
7. ایا ثابتېږي به؟ بودیجه
ثابتوونکی هره چلونه تر یوه پوړ t پورې ډکوي. یو پوړ دوه خپلواکې بودیجې لري، او rand-guest run --tier t دواړه راپور کوي:
- کرښې: تر ټولو ډېر
2ᵗ − 1، چې اجرا شوي سایکلونه او د ډایجسټ کرښې شمېري؛ - Poseidon2 بدلونونه: تر ټولو ډېر
2ᵗ⁻³.
| پوړ | کرښې | Poseidon2 بدلونونه | د ګاز سقف پرته له هش جدول | په chain 20 کې یوه غوښتنه |
|---|---|---|---|---|
| 10 | 1,023 | 128 | 1,279 | منل کېږي |
| 12 | 4,095 | 512 | 5,119 | منل کېږي |
| 14 | 16,383 | 2,048 | 20,479 | منل کېږي |
| 16 | 65,535 | 8,192 | 81,919 | له v0.5.6 راهیسې ردېږي |
| 18, 20 | 2¹⁸ − 1, 2²⁰ − 1 | 2¹⁵, 2¹⁷ | 327,679, 1,310,719 | له v0.5.6 راهیسې ردېږي |
فیس د ګاز او بایټونو پسې ځي، نه د پوړ پسې. په chain 15 کې یوې غوښتنې ثابت 0.001 + call_fee(tier) ورکاوه: په پوړ 10 کې 0.002 RAND، په پوړ 12 کې 0.0021 او په پوړ 14 کې 0.0022. له chain 18 راهیسې هره چلونه اندازه کېږي. یوه عادي کرښه 1 ګاز ده، د Poseidon2 جذب کرښه 3، د Keccak بدلون 192 او د SHA-256 فشرده کول 64. ثبوت یو اعلان شوی ګاز حد خپروي، سرکټ دا پلي کوي چې د چلونې ګاز له هغه سره برابر یا کم وي، او دقیق ګاز پټ پاتې کېږي. هغه سقف چې یو پوړ یې پرته له هش جدول اعلانولی شي (2ᵗ − 1) + 2ᵗ⁻² دی، چې پورته ستون دی. د فیس لږترلږه کچه ده
floor = BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉
چې پکې BUNDLE_BASE 0.001 RAND دی، بایټونه د غوښتنې ثبوت او د ننوتې پوښ دي، او بیې د هر ګاز لپاره له 100 واحدونو او د هر KiB لپاره له 800 واحدونو پیل شوې (یو واحد 10⁻⁹ RAND دی) او په هر بلاک کې لږ څه بدلېږي. په chain 20 کې دوی له 10,000 او 80,000 واحدونو پورته نشي تلای، او یوازې د غوښتنو د ثبوت او پوښ بایټونه د بایټ بیه بدلوي؛ په chains 18 او 19 کې هېڅ سقف نه و. نهمه برخه ښيي چې والټ په ډیفالټ ډول څومره ورکوي، او بېلګه پروګرامونه په chain 18 کې څلور اندازه شوې غوښتنې لري، چې هره یې له 0.0057 تر 0.0075 RAND پورې ده.
پوړ 14 تر ټولو لوړ پوړ دی چې یوه بلنه یې کارولی شي. له fullnode v0.5.6 (2026-09-25) راهیسې، نوډ هر هغه د بلنې ثبوت چې له پوړ 14 لوړ وي د CallTierTooHigh په ورکولو ردوي، مخکې له دې چې د تاییدوونکي کلي جوړ کړي. همدا قاعده هغه بلاک هم ناسم ګڼي چې داسې بلنه ولري. دا حد هغه تصدیقوونکي ساتي چې 2–4 GB حافظه لري: د پوړ-20 د تاییدوونکي کلي جوړول دقیقې او ګیګابایټونه وخت او حافظه غواړي. rand call داسې بلنه مخکې له ثبوت ردوي نه، که څه هم په chain 20 کې لومړی پوړ چاپوي. مخکې له بلنې rand-guest run … --tier 14 وچلوئ، او که چلول پکې ځای نه شي، پروګرام بیا جوړ کړئ. د ځای پر ځای کولو مخکې، rand program deploy --input <word> … (یا --check-call د داسې بلنې لپاره چې ننوتې نه لري) هغه بلنه چې تاسو یې په پام کې لرئ د ایمولېټر له لارې چلوي، او ځای پر ځای کول ردوي مګر دا چې بلنه په پوړ 14 یا له هغه ټیټ کې ودرېږي.
بودیجه څه لګوي
د لنډیز قطارونه او جایګشتونه هغه څه دي چې حیرانوونکي دي. مخکې له دې چې ستاسو کوډ وچلېږي، ماشین پروګرام، خصوصي ننوتې او عامه برخه په hc، H_IN او H_PUB کې هش کوي. هر یو یې د هرو څلورو کلمو لپاره شاوخوا یو جایګشت او یو قطار لګوي. سربېره پر دې، هره POSEIDON2 بلنه چې تاسو یې کوئ د هرو څلورو هش شویو کلمو لپاره شاوخوا یو جایګشت لګوي. په پوړ 10 کې د لیلام لپاره:
| توکی | کلمې | جایګشتونه |
|---|---|---|
پروګرام (hc) | 258 | شاوخوا 65 |
خصوصي ننوتې (H_IN) | 20 | شاوخوا 5 |
عامه برخه (H_PUB) | 34 | شاوخوا 9 |
| د ژمنو څلور ازموینې، هره 7 کلمې | 4 × 7 | 8 |
ټول، لکه څنګه چې run یې راپور ورکوي | له 128 څخه 88 |
قطارونه: 641 دورې جمع د لنډیز قطارونه له 1,023 څخه 721 ته ورسېدل.
په chain 20 کې، لکه په chain 19 کې، د بټوې خپله شمېرنه د rand-guest run له شمېرنې یو څه لوړه ده. هلته د بلنې ثبوت له خپلې معاملې سره تړل شوی دی (په chain 20 کې دا تړاو د جنسیس هش هم په همغو اتو کلمو کې رانغاړي)، او تړاو د عامې برخې د اتو نورو کلمو په توګه لېږدول کېږي: د هغه پروګرام لپاره چې عامه برخه لري دوه نور د لنډیز قطارونه او دوه نور جایګشتونه، او د هغه پروګرام لپاره چې عامه برخه نه لري له هر یوه یو نور.
له دې امله د پروګرام اندازه د بودیجې برخه ده. د هیپ ازموینې پروګرام یوازې 1,183 دورې وچلېد، خو د هغه 1,341 کلمو 340 جایګشتونه ونیول او پوړ 12 ته یې ورساوه. د پوړ 14 په 2,048 جایګشتونو کې، هغه پروګرام چې له شاوخوا 8,000 کلمو ډېر لوی وي هېڅ نه شي بلل کېدای، که هر څه هم کوي. په chain 20 کې دقیق حد د هغه پروګرام لپاره چې عامه برخه نه لري او د هغې بلنې لپاره چې ننوتې نه لري 8,180 کلمې دی، او که له دې دوو یو هم شتون ولري، حد ټیټ دی. په chain 20 کې د پروګرام سقف 65,535 کلمې دی، لکه په chain 15 کې، خو chain 19 او 20 هغه پروګرام چې له دې حد لوی وي نه ځای پر ځای کوي (ProgramUncallable)، په داسې حال کې چې chain 15 د داسې پروګرام د ځای پر ځای کولو فیس اخیست چې هېڅوک یې نه شو بللی. له همدې امله د chain 13 ژباړل شوی ERC-20 (11,686 کلمې، په پوړ 16 کې بلل شوی) په chain 20 کې نه ځای پر ځای کېدای شي او نه بلل کېدای شي. پروګرامونه کوچني وساتئ: په Rust کې opt-level = "s"، په C کې -Os (چې دمخه ټاکل شوی)، لږ تړاوونه، او alloc مه کاروئ مګر دا چې اړتیا ورته ولرئ.
لیلام په خطي ډول لویېږي: هر اضافي بولۍ ورکوونکی 5 د ننوتو کلمې، 8 عامې کلمې، د ازموینې لپاره 2 جایګشتونه او شاوخوا سل دورې زیاتوي. د 64 بولۍ ورکوونکو سره (تصادفي بولۍ، 320 د ننوتو کلمې، 514 عامې کلمې)، دواړه جوړښتونه همغه ګټونکی او بیه ټاکي، او دواړه پوړ 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)
جایګشتونه به د پوړ 12 په 512 کې ځای شي، خو قطارونه یې په 4,095 کې نه ځایېږي. دلته دورې، نه هشول، پوړ ټاکي. په 64 بولۍ ورکوونکو کې د Rust جوړښت ګړندی دی. کله چې لوپ غالب شي، د هغه opt-level = 3 د C جوړښت له ثابت -Os څخه مخکې کېږي.
Keccak او SHA-256
keccak او sha256_compress هر یو یو قطار لګوي، خو هغه ثبوت چې له دې دوو یو هم کاروي یو اضافي جدول له ځان سره لري. په aeacf31 کې دواړه د ارې مرجع اخلي، keccak(&mut state) پر [u32; 50] باندې او sha256_compress(&mut buf) پر [u32; 24] باندې، په داسې حال کې چې 7ef3220 خام پوینټر اخیست. له v0.5.6 راهیسې یوه بلنه تر ټولو ډېر 128 Keccak-f جایګشتونه او تر ټولو ډېر 128 SHA-256 فشارونه کارولی شي. په chain 20 کې د Keccak یو جایګشت 192 ګاز هم لګوي او د SHA-256 یو فشار 64.
جدول ثبوت لویوي، او chain 20 د بلنې ثبوت په 4,194,304 بایټونو (4 MiB، لکه په chain 17 تر 19 کې؛ په chain 13 تر 16 کې سقف 8 MiB و) پورې محدودوي. په chain 18 کې د Keccak جدول سره د پوړ-12 یوه بلنه 3.35 MB اندازه شوه، چې د سقف ډېره برخه ده، او د SHA-256 جدول سره د پوړ-14 یوه بلنه 1.91 MB، په داسې حال کې چې له دې دواړو پرته 1.50 MB وه (بېلګه پروګرامونه). هر KiB د بایټ په بیه حسابېږي. په chain 15 کې یوازې هغه بایټونه حسابېدل چې له وړیا 2 MiB + 18,432 څخه تېر وو، د هر KiB لپاره 1,000 واحده (فیسونه). Poseidon2 وکاروئ مګر دا چې باید له یوه بهرني هش سره سمون ولرئ.
د ثبوت لپاره حافظه او وخت
د ثبوت وخت او حافظه د پوړ سره په چټکۍ لویېږي. د پوړ-10 بلنه په لپټاپ کې په څو ثانیو کې ثابتېږي. په chain 20 کې هر ځای پر ځای کول او هره بلنه د بنډل له خوا دوه ثبوتونه هم له ځان سره لري: د پوړ-10 د لګښت اجازه (څو ثانیې) او د پوړ-14 بنډل چې فیس ورکوي (په لپټاپ کې شاوخوا دوه دقیقې). هغو څلورو بلنو چې په chain 18 کې اندازه شوې، هرې یوې له درې تر پنځه نیمو دقیقو پورې وخت ونیو او لوړه کچه یې له 6.1 تر 6.6 GB وه (بېلګه پروګرامونه). په chain 15 کې بنډل د شاوخوا 100 s او شاوخوا 5.7 GB یو واحد د پوړ-14 ثبوت و. د تصدیقوونکي او راټولوونکي هارډویر اندازه شوې شمېرې لري.
8. ځای پر ځای کول
testnet chain 20 دی. ځای پر ځای کول د fullnode v0.6.8 یا وروسته نسخې یوې rand بټوې، یو کلي او یو څه RAND ته اړتیا لري (پېژندنه؛ د chain 20 فاسېټ، لکه د chain 19، یوازې هغو 16 بټوو ته ضربوي چې د هغه جنسیس یې په اجازه لېست کې لري). د v0.6.7 بټوه په chain 20 کې نه شي لېږلی: د chain 20 جنسیس binding_domain: 1 ټاکي، نو هر د ثبوت تړاو او هر لاسلیک شوی پیغام د جنسیس هش رانغاړي، چې د v0.6.7 بټوه یې نه ورکوي. عامه پای ټکی https://rpc.randprotocol.org د بټوې د همغږي کولو، ځای پر ځای کولو او بلنې لپاره بس دی (تنظیم). لیلام د خپلې عامې برخې سره ځای پر ځای کېږي:
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 د پروګرام پېژند، hc، د کلمو شمېر، او د عامې ننوتې د کلمو شمېر او لنډیز چاپوي. دا مخکې له دې چې څه ثابت کړي، پروګرام د ځنځیر له سقف سره او عامه ننوته له max_program_public_words (په chain 20 کې 32,768) سره ازمويي. بیا د صفر ارزښت ځان ته د لېږد یو بنډل ثابتوي چې فیس ورکوي، سپاري یې، او د ثبتولو انتظار باسي. په chain 20 کې، لکه له chain 17 راهیسې، دا دوه ثبوتونه دي، هر یو د خپل د وتنې کرښې سره: د پوړ-10 د لګښت اجازه (څو ثانیې، شاوخوا 1.37 MB) او د پوړ-14 بنډل (شاوخوا دوه دقیقې، شاوخوا 1.5 MB). په chain 15 کې دا د شاوخوا 100 s او شاوخوا 1.42 MB یو د پوړ-14 ثبوت و. د ځای پر ځای کولو فیس د ګاز برخه نه لري. لیلام په chain 18 تر 20 کې نه دی ځای پر ځای شوی؛ یوه بې نومه رایه په chain 18 کې د عامې برخې سره یو ځای پر ځای کول ښيي، لکه څنګه چې بټوې چاپ کړی.
د پروګرام پېژند د --public سره بدلېږي. rand-guest یوازې د کوډ لپاره 148ab0c4…2c73 چاپ کړ. هغه ځای پر ځای کول چې عامه برخه لري د blake3("rand-program-2", base_pc ‖ len ‖ words ‖ len ‖ public) لاندې ساتل کېږي، چې زېرمه او څلور ژمنې هم تړي. هغه پېژند وکاروئ چې program deploy یې چاپوي. دویم لیلام چې همغه کوډ او بېلې ژمنې ولري، د پروګرام بل پېژند دی.
rand program show <auction id> # base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest
code_hash همغه hc دی چې rand-guest چاپ کړ، خو په بل ډول لیکل شوی. rand-guest د لنډیز هره له اتو کلمو په بیګ-اېنډین ډول چاپوي، او بټوه، rand program show او rand_getProgram د هرې کلمې بایټونه په لیټل-اېنډین ډول چاپوي، نو د اتو هېکس عددونو هره ډله بایټ په بایټ اوښتې ده. د پورته جوړښتونو لپاره، aa04e4c4…050d (Rust) د c4e404aa…4f16 په توګه ښکاري، او 68605aa8…bea0 (C) د a85a6068…b69a په توګه.
9. بلنه
پلورونکی د ټولو 20 خصوصي کلمو سره بلنه کوي:
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
بټوه د پروګرام کوډ او عامه برخه راوړي او ازمويي چې د هغوی هش له پېژند سره سمون لري. --expect-public هم، مخکې له ثبوت، ردوي که عامه برخه د هغې دوتنې کلمې نه وي. بیا بټوه بلنه په ایمولېټر کې چلوي او دقیق ګاز، هغه حد چې اعلانوي یې، پوړ (تر ټولو کوچنی پوړ چې چلول پکې ځایېږي، دلته پوړ 10)، او فیس چاپوي:
gas: <exact> (declaring <limit>, tier <t>)
fee <amount> RAND (incl. two price steps of headroom)
دا د ننوتو 20 کلمې په داسې لفافه کې مهر کوي چې یوازې بلونکی یې پرانیستلی شي، چلول په محلي ډول ثابتوي، فیس د یوه بنډل له لارې ورکوي، سپاري یې، انتظار باسي او رسید چاپوي. په chain 20 کې دا درې ثبوتونه دي: بلنه، د پوړ-10 د لګښت اجازه او د پوړ-14 بنډل.
بلنه څومره لګښت لري. په chain 15 کې دې بلنې 0.001 + call_fee(10) = 0.002 RAND ورکړل. په chain 20 کې بټوه د 7مې برخې لږترلږه کچه ورکوي، چې مخکې له دې چې ثبوت شتون ولري حسابېږي، د دریو ډیفالټ ارزښتونو سره:
- اعلان شوې حد دقیق ګاز دی چې د
2^(tier − 2)تر تر ټولو نږدې پورته مضرب پورې پورته ګرد شوی وي: په 10 پوړ کې 256، په 12 پوړ کې 1,024، په 14 پوړ کې 4,096.--gas-limit maxد پوړ لوړه پوله اعلانوي او--gas-limit Nیوه دقیقه شمېره؛ - ثبوت د زنځیر د 4,194,304 بایټه لوړې پولې پر بنسټ بیه کېږي، نه د خپل اصلي اندازې پر بنسټ، ځکه چې په 20 زنځیر کې د غوښتنې ثبوت له خپلې معاملې سره تړلی دی او فیس باید مخکې وټاکل شي؛
- د بیې دوه پړاوه اضافي ځای، چې ضریب یې 1.125² دی، ځکه چې غوښتنه د بیې له ټاکلو دوه یا درې بلاکه وروسته ثبتېږي.
--fee تلواله بیه بدلوي، او rand fee call <tier> --gas <limit> --bytes <B> له نوډ څخه پخپله د فیس لاندینۍ پوله غواړي. د لیلام غوښتنه په 18 تر 20 زنځیرونو کې نه ده شوې، نو د هغې د ګاز کرښه او فیس دلته نه دي ښودل شوي. یوه بېنومه رایه په 18 زنځیر کې یوه بشپړه غوښتنه لري، هماغسې چې بټوې چاپ کړې، په 0.0062 RAND.
رسید پوړ، H_IN، H_PUB او وتونکي [1, 2, 400, 4, 0, 0, 0, 0] لېږدوي: هماغه کلمې چې ایمولېټر چاپ کړې وې، ځکه چې ثبوت د هماغه اجرا دی. څلور وړاندیزونه او سالټونه په زنځیر کې نه دي. --auditor <rand1…> ننوتونکي یو ټاکلي پلټونکي ته هم مهر کوي، او --no-envelope هېڅ لیکنه نه خپروي، چې بېرته نه شي اړول کېدای (پروګرامونه).
10. پایله وڅېړئ
یو وړاندیز ورکوونکی، یا بل هر څوک، کولای شي لیلام پرته له دې وڅېړي چې پر پلورونکي باور وکړي:
- کوډ. خپره شوې سرچینه له
rand-guest buildسره د circuits په هماغه commit، Rust 1.98.1 (یا clang 23.1.1) سره بیا جوړه کړئ.hcدrand program showیاrand_getProgramلهcode_hashسره پرتله کړئ، او د بایټ له مخې سرچپه لیکدود ته پام وکړئ (8 برخه). جوړونه بیا تولیدېدونکې ده، نو دواړه په هر ماشین کې سره سمون خوري. - عامه برخه. هغه له
rand_getProgramPublicسره راواخلئ، یاrand call --expect-publicوچلوئ. وګورئ چې ستاسو خپله ژمنه ستاسو په شاخص کې ده. - رسید.
rand receipt <tx hash>یاrand_getReceipt: حالت1، ګټونکی او بیه، 7 ځای0xdeadbeefنه وي، اوprogramد لیلام له id سره برابر وي. - ننوتونکي، که تاسو یې حق لرئ. د پلورونکي
rand open-call <tx hash>(یا د پلټونکي--as-auditor) مهر شوي ننوتونکي پرانیزي، لهH_INسره یې پرتله کوي، پروګرام بیا چلوي، او د هر ډول ناسمون په صورت کې له غیر صفري کوډ سره وځي. د v0.6.8 بټوه وکاروئ: له v0.6.8 راهیسې بیا چلول د پروګرام پر عامه برخه او د غوښتنې پر تړنه ترسره کېږي، نو د لیلام لپاره کار کوي (fullnode issue #116). په v0.6.7 کېopen-callد هر هغه پروګرام لپاره چې له عامې برخې سره ځای پر ځای شوی و، لهre-running the program on these inputs failed: PublicIndex(0)سره درېده.
ثبوت تضمینوي چې قاعده د ژمنو شویو وړاندیزونو پر پرانیستلو پلي شوې ده. دا پیسې نه لېږدوي. ګټونکی پلورونکي ته له یو عادي rand send سره پیسې ورکوي، او رسید هغه څه دي چې دواړه خواوې ورته اشاره کوي.
د ستونزو حل
| نښه | لامل او حل |
|---|---|
جوړونه په یوه poseidon2(…)، keccak(…) یا sha256_compress(…) غوښتنه کې ناکامېږي چې د یو زاړه commit لپاره لیکل شوې وه | په aeacf31 کې poseidon2 یو unsafe fn دی او نور دوه &mut [u32; 50] او &mut [u32; 24] اخلي (3 او 7 برخې) |
check له a7 = 7 سره Syscall راپوروي | ګیسټ poseidon2_len غواړي؛ په aeacf31 کې rand-guest check د syscall 7 نوم نه اخلي (zkp-circuits issue #1) |
check په 0xc0001073 کې Csr راپوروي | د کتابتون کوډ په unimp بدل شوی؛ فنکشن له llvm-objdump سره ومومئ او غوښتنه یې بدله کړئ (6 برخه) |
run وايي چې چلول په هېڅ پوړ کې نه ځاييږي، یا له 14 پوړ څخه ډېر ته اړتیا لري | ډېر قطارونه یا د Poseidon2 ډېر permutations؛ پروګرام کوچنی کړئ، لږ hash کړئ، یا کار پر څو غوښتنو ووېشئ او د هغو ترمنځ یو digest ولېږدوئ |
| 7 ځای = 3735928559 | د Rust ګیسټ panic شو؛ له هغه مخکې وتونکي د باور وړ نه دي |
trap: DoubleWrite(n) | د وتونکي ځای n دوه ځله لیکل شوی؛ هره وتنه د یو فنکشن له لارې تېره کړئ |
trap: InputIndex(i) | پروګرام یو خصوصي ننوتونکی ولوست چې غوښتونکي نه و ورکړی؛ لومړی شمېر ولولئ او وڅېړئ |
trap: … په یوه لوستلو یا ساتلو کې | یو ناسم برابر شوی یا له حد بهر پته؛ په C کې، د لړۍ شاخص له خپلې پولې تېر شوی |
invalid value '0x…' for '--input' | د قوماندې په کرښه کې د ننوتونکو کلمې لسیزې دي |
یوه غوښتنه له CallTierTooHigh سره رد شوه | ثبوت له 14 پوړ څخه پورته دی؛ له غوښتنې مخکې له rand-guest run --tier 14 سره یې وڅېړئ |
بیا جوړ شوی hc له ځای پر ځای شوي سره توپیر لري | د circuits بېل commit، toolchain، یا د تړاوونو نسخې؛ درې واړه ثابت کړئ او Cargo.lock commit کړئ. که یوازې لیکدود توپیر ولري، د اتو hex عددونو هره ډله د بایټ له مخې سرچپه ده (8 برخه) |
rand open-call له PublicIndex(0) سره درېږي | پروګرام عامه برخه لري او بټوه v0.6.7 ده، چې بیا چلول یې هېڅ عامه برخه نه ورکوي (fullnode issue #116)؛ v0.6.8 وکاروئ |
… is not inside a circuits checkout | د ګیسټ لارښود د checkout دننه ولېږدوئ (یو Rust پروګرام ځای پر ځای کړئ) |