Sebuah program pada Rand tidak mempunyai storan: satu panggilan membaca inputnya, menerbitkan lapan perkataan dan meninggalkan resit (Programs). Halaman ini menunjukkan cara piawai untuk tetap menyimpan keadaan. Keadaan itu disimpan di luar rantaian sebagai pokok Merkle, program membuktikan setiap perubahan sebagai satu langkah daripada satu root kepada root berikutnya, dan resit-resit merantai langkah-langkah itu bersama.
Contohnya ialah lejar 256 akaun. Satu panggilan melaksanakan sehingga empat pemindahan dan membuktikan bahawa setiap penghantar memiliki akaun itu, mempunyai dana yang mencukupi, dan tiada apa-apa yang dicipta atau hilang. Rantaian hanya melihat satu status, satu bilangan dan satu cincangan.
Example programs mengandungi persediaan yang dikongsi oleh keempat-empat halaman contoh.
1. Apa yang dibuktikan
Lejar ini ialah pokok Merkle berkedalaman 8 di atas Poseidon2, iaitu cincangan yang paling murah dibuktikan oleh mesin. Satu daun ialah satu akaun:
owner = first 4 words of Poseidon2("OWNR", secret)
leaf = Poseidon2("LEAF", owner, balance low, balance high, nonce)
node = Poseidon2(left, right, "NODE")
Rahsia ialah empat perkataan yang disimpan oleh pemegang akaun itu.
Satu panggilan menerima root semasa dan satu kelompok pemindahan sebagai input persendirian. Bagi setiap pemindahan, ia membuktikan bahawa:
- daun penghantar berada dalam pokok, melalui laluan Merkle-nya;
- pemanggil mengetahui rahsia di sebalik id pemilik penghantar;
- jumlah itu tidak melebihi baki penghantar;
- pokok selepas debit mengandungi daun penerima;
- kredit tidak melimpah.
Seterusnya ia menerbitkan satu pautan: Poseidon2(old root, new root, count, "LINK"), dipotong kepada 160 bit.
Mengapa pautan dan bukan kedua-dua root. Satu root ialah lapan perkataan dan terdapat lapan slot output. Menerbitkan kedua-dua root bermakna memotong setiap satu kepada kira-kira 96 bit, dan nilai 96 bit boleh dilanggarkan dengan kira-kira 2⁴⁸ kerja, yang membolehkan seorang pembukti menunjukkan dua lejar yang berbeza di bawah satu resit. Satu cincangan 160 bit bagi kedua-dua root tidak mempunyai masalah ini. Root penuh dihantar di luar rantaian bersama data lejar, dan sesiapa yang memegangnya boleh mengira semula pautan itu.
Mengikuti lejar. Seorang pengikut memegang keadaan semasa. Apabila resit bagi program itu muncul dengan status 1, pengikut memperoleh daun baharu daripada pihak yang membuat panggilan, membina semula root, dan mengira pautan daripada root lamanya sendiri, root baharu dan bilangan dalam resit itu. Jika ia sama dengan output resit, langkah itu sah dan pengikut menerima pakai keadaan baharu. Resit yang tidak berpaut daripada root semasa pengikut diabaikan. Itu juga yang menyelesaikan perlumbaan: dua pemanggil yang membina di atas root yang sama kedua-duanya mendapat resit, dan yang disusun dahulu oleh rantaian ialah yang diterima oleh setiap pengikut.
Siapa yang boleh membuat panggilan. Sesiapa yang mengetahui rahsia penghantar dan pokok semasa. Tiada pengendali dalam peraturan ini: seorang pemegang boleh membuktikan pemindahannya sendiri pada mesinnya sendiri, supaya rahsia itu tidak pernah meninggalkannya.
2. Antara muka
Tiada segmen awam. Identiti lejar ialah root genesisnya, yang dipersetujui di luar rantaian.
| perkataan persendirian | maksud |
|---|---|
| 0 | bilangan pemindahan, paling banyak 4 |
| 1 hingga 8 | root lama |
| kemudian, bagi setiap pemindahan, 146 perkataan | |
| +0, +1 | indeks daun penghantar dan penerima, masing-masing di bawah 256 |
| +2, +3 | jumlah, satu u64, perkataan rendah kemudian tinggi |
| +4 hingga +7 | rahsia penghantar |
| +8 hingga +10 | baki penghantar (rendah, tinggi) dan nonce |
| +11 hingga +74 | laluan Merkle penghantar: 8 saudara sebanyak 8 perkataan, dari daun ke atas |
| +75 hingga +78 | id pemilik penerima |
| +79 hingga +81 | baki penerima (rendah, tinggi) dan nonce |
| +82 hingga +145 | laluan Merkle penerima, dalam pokok yang ditinggalkan oleh debit |
| slot output | maksud |
|---|---|
| 0 | status: 1 dilaksanakan, 2 daun penghantar tiada dalam pokok (atau rahsia salah), 3 daun penerima tiada dalam pokok, 4 baki tidak mencukupi, 5 kredit melimpah, 6 penghantar ialah penerima, 7 lebih daripada 4 pemindahan, 8 indeks di luar julat |
| 1 | pemindahan yang dilaksanakan: bilangan apabila berjaya, indeks pemindahan yang gagal jika tidak |
| 2 hingga 6 | apabila berjaya, lima perkataan pertama pautan |
| 7 | tidak digunakan; panik Rust menulis 0xdeadbeef di sini |
Pemindahan dilaksanakan mengikut urutan, setiap satu pada pokok yang ditinggalkan oleh yang sebelumnya. Dalam kelompok demo, akaun 3 bermula pada sifar, menerima 125 dalam pemindahan pertama dan membelanjakan 25 daripadanya dalam pemindahan ketiga.
3. Guest
my-guests/ledger/src/main.rs, lengkap:
#![no_std]
#![no_main]
use guest_sdk::{halt, poseidon2, read_input, write_output};
/// The ledger is a Merkle tree of 2^8 = 256 accounts.
const DEPTH: usize = 8;
const MAX_TRANSFERS: u32 = 4;
/// Private words per transfer: from, to, amount (2), secret (4), sender balance (2) and nonce,
/// sender path (64), receiver owner (4), balance (2) and nonce, receiver path (64).
const TRANSFER_WORDS: u32 = 146;
const OK: u32 = 1;
const BAD_SENDER: u32 = 2;
const BAD_RECEIVER: u32 = 3;
const INSUFFICIENT: u32 = 4;
const OVERFLOW: u32 = 5;
const SELF_TRANSFER: u32 = 6;
const TOO_MANY: u32 = 7;
const BAD_INDEX: u32 = 8;
const OWNER_TAG: u32 = 0x4f57_4e52; // "OWNR"
const LEAF_TAG: u32 = 0x4c45_4146; // "LEAF"
const NODE_TAG: u32 = 0x4e4f_4445; // "NODE"
const LINK_TAG: u32 = 0x4c49_4e4b; // "LINK"
type Digest = [u32; 8];
/// Poseidon2 over `buf[..n]`. The sponge does not pad, so every message this program hashes
/// has a fixed length and carries its own tag word.
fn hash(buf: &mut [u32], n: usize) -> Digest {
assert!(n <= buf.len() && buf.len() >= 8);
// SAFETY: the syscall reads `buf[..n]` and overwrites `buf[..8]`; the assert keeps both
// inside `buf`, which `&mut` makes aligned, valid and unaliased for the call.
unsafe { poseidon2(buf.as_mut_ptr(), n) };
let mut d = [0u32; 8];
d.copy_from_slice(&buf[..8]);
d
}
/// An account's owner id: the first four words of Poseidon2("OWNR", secret).
fn owner_of(secret: &[u32; 4]) -> [u32; 4] {
let mut buf = [OWNER_TAG, secret[0], secret[1], secret[2], secret[3], 0, 0, 0];
let d = hash(&mut buf, 5);
[d[0], d[1], d[2], d[3]]
}
fn leaf(owner: &[u32; 4], balance: u64, nonce: u32) -> Digest {
let mut buf = [
LEAF_TAG, owner[0], owner[1], owner[2], owner[3], balance as u32, (balance >> 32) as u32, nonce,
];
hash(&mut buf, 8)
}
/// One level of a walk. `buf` holds a node's message, `left ‖ right ‖ "NODE"`, and arrives with
/// the running hash in words 0..8, where the last hash left its digest. `side` is 0 when the
/// running hash is the left child and 8 when it is the right one: it is moved to its half and
/// the sibling written to the other. The halves are picked by an offset, not a branch, so both
/// cases execute the same instructions (for a left child the move is a copy onto itself).
fn level(buf: &mut [u32; 17], sib: &Digest, side: usize) {
let mut cur = [0u32; 8];
cur.copy_from_slice(&buf[..8]);
buf[side..side + 8].copy_from_slice(&cur);
buf[8 - side..16 - side].copy_from_slice(sib);
// SAFETY: the syscall reads the 17 words of `buf` and overwrites its first 8.
unsafe { poseidon2(buf.as_mut_ptr(), 17) };
}
/// Walks two leaves up the same path at once and returns the root each one reaches. The path
/// is the eight siblings whose words start at private input `at`; bit `d` of `index` says
/// whether the running hash is the right child at level `d`. An update needs both walks (the
/// leaf before and the leaf after), and doing them together reads each sibling once.
fn roots_from(before: Digest, after: Digest, index: u32, at: u32) -> (Digest, Digest) {
let (mut a, mut b) = ([0u32; 17], [0u32; 17]);
a[..8].copy_from_slice(&before);
b[..8].copy_from_slice(&after);
a[16] = NODE_TAG;
b[16] = NODE_TAG;
for d in 0..DEPTH {
let side = 8 * ((index >> d) & 1) as usize;
let sib: Digest = core::array::from_fn(|k| read_input(at + (8 * d + k) as u32));
level(&mut a, &sib, side);
level(&mut b, &sib, side);
}
let (mut ra, mut rb) = ([0u32; 8], [0u32; 8]);
ra.copy_from_slice(&a[..8]);
rb.copy_from_slice(&b[..8]);
(ra, rb)
}
fn read_u64(at: u32) -> u64 {
read_input(at) as u64 | (read_input(at + 1) as u64) << 32
}
/// Applies the transfer whose words start at `at` to the tree with root `root`.
fn apply(root: Digest, at: u32) -> Result<Digest, u32> {
let from = read_input(at);
let to = read_input(at + 1);
let amount = read_u64(at + 2);
let secret = [read_input(at + 4), read_input(at + 5), read_input(at + 6), read_input(at + 7)];
let sender_balance = read_u64(at + 8);
let sender_nonce = read_input(at + 10);
let receiver = [read_input(at + 75), read_input(at + 76), read_input(at + 77), read_input(at + 78)];
let receiver_balance = read_u64(at + 79);
let receiver_nonce = read_input(at + 81);
if from >= 1 << DEPTH || to >= 1 << DEPTH {
return Err(BAD_INDEX);
}
if from == to {
return Err(SELF_TRANSFER);
}
// The sender's leaf is in the tree, and the caller knows the secret behind its owner id.
// The second walk carries the debited leaf up the same path: the root after the debit.
let sender = owner_of(&secret);
let (seen, debited) = roots_from(
leaf(&sender, sender_balance, sender_nonce),
leaf(&sender, sender_balance.wrapping_sub(amount), sender_nonce.wrapping_add(1)),
from,
at + 11,
);
if seen != root {
return Err(BAD_SENDER);
}
if amount > sender_balance {
return Err(INSUFFICIENT);
}
// The receiver's leaf is in the tree the debit left behind; the second walk credits it.
let (seen, credited) = roots_from(
leaf(&receiver, receiver_balance, receiver_nonce),
leaf(&receiver, receiver_balance.wrapping_add(amount), receiver_nonce),
to,
at + 82,
);
if seen != debited {
return Err(BAD_RECEIVER);
}
if receiver_balance.checked_add(amount).is_none() {
return Err(OVERFLOW);
}
Ok(credited)
}
/// Private input: [count, old root (8 words), then `count` transfers of 146 words each].
/// Outputs: [status, transfers applied, link (first 5 words), 0], where
/// link = Poseidon2(old root, new root, count, "LINK").
#[no_mangle]
pub extern "C" fn main() -> ! {
let count = read_input(0);
if count > MAX_TRANSFERS {
write_output(0, TOO_MANY);
halt();
}
let mut old = [0u32; 8];
for (k, w) in old.iter_mut().enumerate() {
*w = read_input(1 + k as u32);
}
let mut root = old;
let mut done = 0;
while done < count {
match apply(root, 9 + TRANSFER_WORDS * done) {
Ok(next) => root = next,
Err(status) => {
write_output(0, status);
write_output(1, done);
halt();
}
}
done += 1;
}
let mut buf = [0u32; 18];
buf[..8].copy_from_slice(&old);
buf[8..16].copy_from_slice(&root);
buf[16] = count;
buf[17] = LINK_TAG;
let link = hash(&mut buf, 18);
write_output(0, OK);
write_output(1, count);
for k in 0..5 {
write_output(2 + k as u32, link[k]);
}
halt();
}
Root lama ialah input persendirian, dan itu mantap. Tiada apa yang memaksa pemanggil bermula daripada root sebenar. Panggilan yang bermula daripada root rekaan membuktikan langkah yang betul daripada lejar yang tiada orang lain pegang, dan pautannya tidak sepadan dengan keadaan mana-mana pengikut. Pautan itulah pengikatnya: ia ialah komitmen awam kepada kedua-dua root, dan pengikut membandingkannya dengan keadaan mereka sendiri.
Pemuliharaan terjamin melalui pembinaan. Jumlah yang didebit dan jumlah yang dikredit ialah pemboleh ubah yang sama. Tiada jumlah berasingan untuk disemak.
Debit dan kredit sengaja menggunakan aritmetik wrapping_. Program membina daun “selepas” sebelum ia tahu daun “sebelum” itu tulen, kerana kedua-duanya menyusuri laluan bersama. Jika jumlah melebihi baki, daun yang berbalik nilai dikira kemudian dibuang apabila semakan dua baris selepas itu mengembalikan status 4.
Nonce bertambah pada setiap debit, jadi daun penghantar berubah walaupun baki kembali kepada nilai terdahulu. Pengikut dapat melihat berapa kali sesebuah akaun telah berbelanja.
4. Dua fakta rantaian alat yang ditemui guest ini
rand-guest check menolak Poseidon2 terikat panjang. SDK mempunyai dua panggilan Poseidon2. poseidon2 (syscall 3) tidak melakukan pelapik: mesej sehingga empat perkataan menghasilkan cincangan yang sama seperti mesej itu dengan perkataan sifar ditambah di hujungnya. poseidon2_len (syscall 7) menyemai panjang ke dalam span dan merupakan lalai yang lebih selamat bagi data berpanjang berubah. Mesin melaksanakan kedua-duanya, tetapi pada circuits aeacf31 senarai syscall penyemak berhenti pada 6:
$ rand-guest build my-guests/ledger --max-words 65535
…
0x00001114 0x00000073 Syscall: ecall with a7 = 7, which is no syscall the machine implements
…
Error: the image is rejected by the checker (it was still written)
Maka guest ini menggunakan poseidon2 dan mengikut peraturan yang menjadikan span tanpa pelapik itu selamat: setiap mesej mempunyai satu panjang tetap, dan setiap jenis mesej membawa perkataan tag tersendiri.
Menyifarkan penimbal dalam gelung ialah baris paling mahal yang tidak anda sedari. Versi pertama bagi penyusuran Merkle ialah yang paling jelas: fungsi yang membina [0u32; 17] baharu bagi setiap nod, menyalin dua anak ke dalamnya, mencincang, dan memulangkan digest. Empat pemindahan mengambil 26,124 kitaran dan tidak muat dalam tier 14, tier tertinggi yang boleh digunakan oleh satu panggilan. Tiga perubahan, tiada satu pun menyentuh apa yang dibuktikan:
| perubahan | kitaran bagi empat pemindahan |
|---|---|
| penimbal bersifar baharu dan tatasusunan laluan bagi setiap nod | 26,124 |
| satu penimbal bagi setiap penyusuran, disifarkan sekali; tiada tatasusunan laluan berisi sifar | 15,897 |
| penyusuran sebelum dan selepas dilakukan bersama, setiap saudara dibaca sekali | 15,445 |
| cincangan yang sedang berjalan disimpan dalam penimbal cincangan, tempat syscall meninggalkannya | 12,393 |
[0u32; 17] di dalam gelung dikompil menjadi satu panggilan memset bagi setiap lelaran. Syscall menulis digestnya di atas lapan perkataan pertama penimbalnya, jadi meletakkan mesej sebagai left ‖ right ‖ tag bermakna digest sudah berada di tempat anak kiri bagi aras seterusnya. llvm-objdump -d pada ELF di bawah target/riscv32im-unknown-none-elf/release/ menunjukkan di mana kitaran digunakan.
5. Bahagian hos
Membina laluan memerlukan Poseidon2 di luar mesin. Checkout circuits mempunyainya: rand_zkvm::hash::sponge_hash ialah fungsi sebelah hos bagi span yang dikira oleh syscall POSEIDON2. ledger-host ialah binari Rust kecil yang bergantung padanya melalui laluan:
[dependencies]
rand_zkvm = { path = "../../research" }
ledger-host owner <secret: 32 hex> the owner id of a secret
ledger-host genesis <accounts file> <state file> build a tree, write its state
ledger-host root <state file> the state's root
ledger-host step <state file> <transfers file> <new state file>
stdout: the private words
stderr: old root, new root, link
ledger-host link <old state file> <new state file> <count>
the link a receipt must show
Fail keadaan ialah 256 baris owner balance nonce. Lejar demo mempunyai enam akaun berdana. Rahsia mereka berada dalam demo-accounts.txt dan, seperti setiap demo pada halaman ini, sengaja didedahkan kepada umum. Baki dalam unit 10⁻⁸.
$ cd my-guests/ledger-host && cargo +1.98.1 build --release && cd ../ledger
$ alias ledger-host=../ledger-host/target/release/ledger-host
$ cat demo-accounts.txt
de0f9e0173fe8358df1de251d81b4bec 50000000000
350826bc703c63ee3497f907373397f0 12000000000
8d5684dd764886843e4660d2c1ef12cc 7550000000
89442144b6673a3f93a5aa2359ddc6cf 0
99e9f82eede0b8b9feca124c0e1808be 100000000000
f29afdfbf0b821aa6c52f21cedc4c14a 4200000000
$ ledger-host genesis demo-accounts.txt state-0.txt
root bd1e27e17c0a0fe6e102f4c2197ed8721f22fccc99657860f035d1f5a4d1a568
Fail pemindahan mempunyai satu from to amount secret bagi setiap baris:
$ cat batch-1.txt
0 3 12500000000 de0f9e0173fe8358df1de251d81b4bec
4 1 99900000000 99e9f82eede0b8b9feca124c0e1808be
3 2 2500000000 89442144b6673a3f93a5aa2359ddc6cf
1 5 1500000000 350826bc703c63ee3497f907373397f0
$ ledger-host step state-0.txt batch-1.txt state-1.txt > witness-1.txt
old root bd1e27e17c0a0fe6e102f4c2197ed8721f22fccc99657860f035d1f5a4d1a568
new root c021ecd07d7ca8a04ee6dd7fce48052f2a88cf2b38040387066849b8e2e87e91
link bcf87def900bb2978b6c24bfaaf6c9d357d58ba9
link words 3170401775 2416685719 2339120319 2868300243 1473612713
$ head -6 state-1.txt
ff2a2fadae3eb422e7f8810b701d45c6 37500000000 1
d6f41168e6db1d63402588a7d50dda9c 110400000000 1
d240d9f11db1ed16b2902e4533c17015 10050000000 0
b357d759652ee04004d9c5c21a026d17 10000000000 1
7a179726cd16187bb760c0272c011cab 100000000 1
c393d03f75fcadfd1f1d7102040fc47b 5700000000 0
Keenam-enam baki masih berjumlah 1,737.5. Saksi ialah 593 perkataan: bilangan, root lama dan 146 bagi setiap pemindahan.
6. Bina dan jalankan
$ rand-guest build my-guests/ledger --max-words 65535
773 words against a cap of 65535 (fits); 0 ecall(s) with a non-static a7
OK
wrote my-guests/ledger/image.bin and its .sha256 (773 words, hc 6bb2eccbe70a562774053da4631777d84f2bc68361f30f9be5c4aa16f0153814, program id c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab)
$ rand-guest run my-guests/ledger/image.bin --input $(cat witness-1.txt) --tier 14
out[0] = 1
out[1] = 4
out[2] = 3170401775
out[3] = 2416685719
out[4] = 2339120319
out[5] = 2868300243
out[6] = 1473612713
out[7] = 0
cycles 12394
tier 14
tier 14: fits (cycles 12739 of 16383, Poseidon2 permutations 1030 of 2048)
Lima perkataan pautan sepadan dengan perkataan alat hos. Penolakan, masing-masing dijalankan dalam emulator dengan kelompok satu pemindahan terhadap state-0.txt:
| kelompok | out[0], out[1] |
|---|---|
| akaun 2 menghantar lebih daripada yang dipegangnya | 4, 0 |
| pemindahan akaun 2 dengan rahsia akaun 5 | 2, 0 |
| satu pemindahan sah, kemudian akaun 5 membayar dirinya sendiri | 6, 1 |
satu pemindahan sah yang dibina atas state-1.txt dengan satu perkataan root lama diubah | 2, 0 |
| satu pemindahan sah dengan baki penerima dinyatakan salah | 3, 0 |
| bilangan 5 | 7, 0 |
7. Belanjawan
| satu pemindahan | empat pemindahan | |
|---|---|---|
| perkataan persendirian | 155 | 593 |
| kitaran | 3,221 | 12,394 |
| Permutasi Poseidon2, termasuk digest | 1,030 daripada 2,048 | |
| peringkat | 12 | 14 |
Satu pemindahan ialah 37 cincangan: 32 nod (dua laluan sepanjang lapan, setiap satu dilalui untuk daun sebelum dan selepas), empat daun dan satu id pemilik. Empat pemindahan meninggalkan kira-kira 3,600 kitaran bagi bajet peringkat 14 tidak digunakan. Binaan dengan had dinaikkan kepada 5 menjalankan lima pemindahan dalam 15,832 daripada 16,383 kitaran, yang muat dengan sedikit ruang berbaki, maka program yang digunakan berhenti pada 4.
Poseidon2 ialah fungsi cincang yang murah di sini. Setiap kitaran biasa berharga 1 gas dan setiap baris absorb Poseidon2 berharga 3, berbanding 64 bagi satu pemampatan SHA-256 dan 192 bagi satu permutasi Keccak. 149 cincangan Poseidon2 dalam panggilan ini menambah kira-kira 2,500 gas kesemuanya, lebih kurang kos 13 permutasi Keccak. Undi tanpa nama membuat 12 permutasi:
gas: 14110 (declaring 16384, tier 14)
12,740 baris pada 1 gas setiap satu, ditambah 2 lagi bagi setiap satu daripada 685 baris absorb, ialah 14,110.
8. Penggunaan
Program ini tidak mempunyai segmen awam, jadi id-nya ialah id yang dicetak oleh rand-guest build.
$ rand fee deploy 773
0.0783 RAND
$ rand program deploy my-guests/ledger/image.bin
program id: c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab (773 words, hc cbecb26b27560ae7a43d0574d877176383c62b4f9b0ff36116aac4e5143815f0)
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 10.8s: tier 10, 1370188 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 113.1s: tier 14, 1494406 bytes
submitted deploy 40a8e53d7e916c90db77504cf6601c443d7da0756e04c52745f350969659588f
0 RAND out, 59.7762 RAND change, fee 0.0783 RAND, anchored at height 97945, proved in 113.1s (bundle) + 10.8s (auth)
Satu program yang digunakan melayani sebarang bilangan lejar. Dua lejar berbeza pada root genesis masing-masing, dan resit mereka tidak pernah saling dipautkan.
9. Panggilan
$ rand call c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab \
$(sed 's/[0-9][0-9]*/--input &/g' witness-1.txt)
proving the call locally (593 inputs stay private)…
gas: 14110 (declaring 16384, tier 14)
fee 0.007489462 RAND (incl. two price steps of headroom)
proved in 114.8s: tier 14, 1498589 bytes, outputs [1, 4, 3170401775, 2416685719, 2339120319, 2868300243, 1473612713, 0]
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 19.9s: tier 10, 1374567 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 134.0s: tier 14, 1495974 bytes
submitted call 0ec66eed3bdeefc4a95741d26ad84f0a2d8198c4af3f7b1ec778e0525fa56a99
0 RAND out, 59.701731664 RAND change, fee 0.007489462 RAND, anchored at height 98577, proved in 134.0s (bundle) + 19.9s (auth)
{
"h_in": "68ea34ef767a30987c155a5a3cf0e69e832166b54611510ce0abc3430b8ff44c",
"h_pub": null,
"height": 98831,
"index": 0,
"outputs": [
1,
4,
3170401775,
2416685719,
2339120319,
2868300243,
1473612713,
0
],
"program": "c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab",
"tier": 14,
"tx": "0ec66eed3bdeefc4a95741d26ad84f0a2d8198c4af3f7b1ec778e0525fa56a99"
}
input transcript published; open it with `rand open-call 0ec66eed3bdeefc4a95741d26ad84f0a2d8198c4af3f7b1ec778e0525fa56a99`
Status 1, empat pemindahan, dan pautan bcf87def 900bb297 8b6c24bf aaf6c9d3 57d58ba9 dalam heks. h_pub ialah null kerana program ini tidak mempunyai segmen awam. Rantaian ini tidak menyimpan sebarang akaun, jumlah atau root.
Bukti ini berukuran 1,498,589 bait, iaitu saiz bukti panggilan tanpa jadual cincangan: Poseidon2 sudah ada dalam setiap bukti, sedangkan Keccak dan SHA-256 masing-masing membawa jadualnya sendiri. Panggilan ini mengambil masa sedikit lebih daripada lima minit pada komputer riba Apple Silicon (115 s untuk bukti panggilan, 20 s untuk kebenaran perbelanjaan, 134 s untuk bundle) dan mencecah puncak 6.1 GB.
10. Ikuti sendiri
Pengikut yang memegang state-0.txt dan diberi state-1.txt boleh menyemak resit dengan satu arahan:
$ ledger-host link state-0.txt state-1.txt 4
link bcf87def900bb2978b6c24bfaaf6c9d357d58ba9
link words 3170401775 2416685719 2339120319 2868300243 1473612713
Kelima-lima perkataan itu ialah output 2 hingga 6 bagi resit tersebut, dan 4 ialah output 1. Bilangan yang berbeza, atau salah satu keadaan yang berubah sebanyak satu unit, menghasilkan pautan yang berbeza.
Langkah kedua. Lejar bergerak sekali lagi untuk halaman ini. batch-2.txt ialah dua pemindahan terhadap state-1.txt:
$ cat batch-2.txt
2 0 1050000000 8d5684dd764886843e4660d2c1ef12cc
5 4 700000000 f29afdfbf0b821aa6c52f21cedc4c14a
$ ledger-host step state-1.txt batch-2.txt state-2.txt > witness-2.txt
old root c021ecd07d7ca8a04ee6dd7fce48052f2a88cf2b38040387066849b8e2e87e91
new root a1f79db3379bc2a06c6da4326f122bb7bec04689329b79ba76f931cbb6ef7b47
link 2e2738c49dc6edcfd1349a6932145c12b23d366b
link words 774322372 2647059919 3509885545 840195090 2990356075
$ rand call c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab \
$(sed 's/[0-9][0-9]*/--input &/g' witness-2.txt)
…
gas: 7240 (declaring 8192, tier 14)
fee 0.00645165 RAND (incl. two price steps of headroom)
proved in 100.9s: tier 14, 1516155 bytes, outputs [1, 2, 774322372, 2647059919, 3509885545, 840195090, 2990356075, 0]
…
submitted call 590f9814a9de00e6d667ac17587b3ea9742b745b6b43657876c42bc095ce8a99
Root lamanya ialah root baharu bagi langkah pertama, c021ecd0…7e91. Itulah rantaian jagaan: pada chain 18, resit 0ec66eed…6a99 memautkan keadaan genesis kepada state-1 dan resit 590f9814…8a99 memautkan state-1 kepada state-2. Pada chain 20, dua langkah yang sama, dengan root yang sama, ialah 9c9998bb…66eb dan dd756a89…ffc2. Keadaan semasa lejar tunjuk cara ialah keadaan yang dihasilkan oleh kedua-dua kelompok itu daripada demo-accounts.txt, jadi sesiapa sahaja boleh membinanya semula dan mengambil langkah ketiga.
Membuka panggilan. Dompet memeterai input persendirian setiap panggilan dalam sampul surat yang hanya boleh dibuka oleh kunci pelihat pemanggil. Pemanggil boleh membukanya kemudian, dan dompet menjalankan semula program pada apa yang ditemuinya:
$ rand open-call 0ec66eed3bdeefc4a95741d26ad84f0a2d8198c4af3f7b1ec778e0525fa56a99
opened with the caller's viewing key: 593 input word(s)
inputs: [4, 3172870113, 2081034214, 3775067330, 427743346, 522386636, 2573564000, 4030058997, 2765202792, 0, 3, …]
emulator outputs: [1, 4, 3170401775, 2416685719, 2339120319, 2868300243, 1473612713, 0]
receipt outputs: [1,4,3170401775,2416685719,2339120319,2868300243,1473612713,0]
verdict: faithful — these are the words the proof was made over, and they reproduce its outputs
Input tersebut mengandungi root lama dan setiap daun yang berubah, jadi sampul surat yang dimeterai kepada juruaudit (rand call --auditor <rand1…>) ialah satu cara untuk meletakkan data sesuatu langkah di tempat yang sentiasa boleh dicapai oleh pihak yang dinamakan. Dengan v0.6.7, open-call berfungsi untuk program ini dan gagal pada tiga yang lain: bagi program yang mempunyai segmen awam, ia membuka input kemudian berhenti dengan re-running the program on these inputs failed: PublicIndex(0), kerana larian semula dibuat tanpa input awam program tersebut.
Apa yang tidak diberikan oleh bukti
- Ketersediaan. Rantaian membuktikan setiap langkah adalah sah. Ia tidak menyimpan lejar. Sesiapa yang membuat sesuatu langkah mesti menerbitkan daun baharu, atau pengikut tidak dapat mengambil langkah seterusnya. Pemanggil yang membuktikan sesuatu langkah tetapi menahan data telah membekukan lejar pada titik itu bagi semua orang yang menerima resit, jadi penggunaan sebenar memerlukan peraturan untuk perkara ini: jawatankuasa yang menandatangani bagi data, atau menyiarkan daun yang berubah di tempat awam.
- Privasi dalam kalangan peserta. Rantaian tidak melihat apa-apa selain bilangan dan satu cincangan. Namun pembayar perlu membekalkan daun penerima, jadi pembayar mengetahui baki penerima, dan sesiapa yang memegang fail keadaan memegang setiap baki. Kolam shielded Rand sendiri mengelakkan perkara ini dengan menggunakan nota dan bukannya akaun (Nota dan pokok komitmen). Contoh ini mengekalkan akaun supaya kekal kecil.
- Pengurutan. Dua langkah sah daripada root yang sama bercanggah, dan hanya yang pertama mengikut susunan rantaian dikira. Pemanggil kedua telah membayar untuk resit yang tidak diterima sesiapa dan perlu membina semula pada root baharu.
- Pautan kepada RAND atau kepada token. Baki ialah nombor dalam sebuah pokok. Tiada apa-apa yang memindahkannya masuk atau keluar daripada kolam shielded: output sesebuah program ialah data, dan rantaian tidak bertindak ke atas mana-mana daripadanya.
- Pemindahan yang ditandatangani oleh pemegang dan dibuktikan oleh orang lain. Memiliki sesebuah akaun bermakna mengetahui praimej cincangan, dan praimej itu ialah input persendirian, jadi pembukti mesti pemegangnya. Pendelegasian memerlukan semakan tandatangan di dalam program. Pengesahan Dilithium atau ECDSA tidak muat dalam panggilan peringkat 14.