sbpf2rv mengambil program Solana yang telah dikompilasi (ELF sBPF, yaitu berkas .so yang dihasilkan cargo build-sbf) dan mengubahnya menjadi program Rand. Alat ini menulis C, dan rand-guest build mengompilasi C tersebut menjadi image RV32IM. Image tersebut menerbitkan delapan kata keluaran yang sama dengan guest interpreter sBPF milik Rand, sbpf.bin, untuk ELF dan instruksi yang sama. Halaman ini menunjukkan seluruh alurnya pada program SPL Token yang sebenarnya.
Apa itu
| masukan | sebuah ELF sBPF v1 |
| keluaran | sebuah crate shim C; rand-guest build mengubahnya menjadi kontainer image |
| tempat berjalan | di luar rantai, pada mesin pengembang; rantai hanya melihat image-nya |
| yang harus dicocokkan | delapan kata keluaran sbpf.bin, pada dua pita masukan yang sama |
| repositori | sbpf2rv/ di repositori circuits |
| toolchain yang dipatok | Rust 1.98.1, clang 23.1.1, crate cc 1.4.6 |
Program hasil terjemahan mempertahankan ABI interpreter. Program ini membaca pita yang sama: ELF pada pita publik dan instruksi yang diserialisasi (akun, lalu data instruksi) pada pita privat. Program ini menjalankan harness sbpf-core yang sama di sekeliling program: mendekode masukan, memuat ELF, memeriksa wilayah akun, dan menghitung hash masukan serta keluaran. Hanya eksekusi sBPF itu sendiri yang digantikan oleh kode native. Itulah sebabnya keluarannya sama dengan keluaran interpreter kata demi kata.
Model kepercayaan
Tiga hal mengikat bukti dari program hasil terjemahan.
hcmengikat kode hasil terjemahan. C dikompilasi ke dalam image, sehinggahcmilik image mencakupnya.- Penjaga ELF mengikat
hcpada satu program. Shim memuat program dari ELF pada pita, sehingga tanpa pemeriksaan, satu image dapat menjalankan ELF lain yang dipilih penelepon di bawahhcyang sama. Karena ituprogram.cyang dihasilkan diakhiri dengan digest dari program termuat yang menjadi sumber terjemahannya: teks, data hanya-baca, alamat keduanya, dan titik masuk. Sebelum menjalankan apa pun, shim menghitung hash program termuat pada pita dengan cara yang sama (Poseidon2) dan membandingkannya. Program lain akan dihentikan denganBadElf, status 2, di atas pra-status. Jadi bukti yang terverifikasi terhadaphcini adalah hasil eksekusi program tersebut. H_PUBmengikat byte ELF. ELF dikirim pada pita publik, dan digest masukan publik dari bukti mencakupnya kata demi kata.
Apakah hc merupakan terjemahan yang setia dari suatu ELF diperiksa dengan membangun ulang. Jalankan sbpf2rv yang dipatok pada ELF tersebut, bangun dengan toolchain yang dipatok, lalu bandingkan hc. Karena alasan inilah build-nya dapat direproduksi.
Rantai tidak mencatat ELF sumber maupun hash-nya. Rantai menyimpan image dan id programnya. Pengembang yang ingin ELF-nya diketahui publik menerbitkannya di samping program.
Penjaga mengikat program termuat, bukan berkasnya. Dua ELF yang berbeda hanya pada bagian yang tidak dilihat loader (padding header, tabel simbol) dimuat menjadi program yang sama dan berjalan identik di kedua sisi. ELF dengan satu byte .rodata yang diubah, bahkan byte yang tidak pernah dibaca oleh transfer, ditolak oleh terjemahan dan dijalankan oleh interpreter.
Apa yang menyebabkan trap dan apa yang ditolak
Tidak ada yang ditolak saat penerjemahan. ELF yang cacat ditolak dengan pesan milik loader sendiri, tetapi tidak ada instruksi dalam ELF yang tertata baik yang ditolak. Pemindai melaporkan apa yang dapat dijumpai suatu program sebagai peringatan, dan program hasil terjemahan melakukan trap saat dijalankan tepat di tempat interpreter melakukannya:
| peringatan | apa itu | saat dijalankan |
|---|---|---|
UnknownSyscall | hash syscall yang tidak diimplementasikan runtime | trap UnknownSyscall |
Cpi | pemanggilan lintas program (sol_invoke_signed*) | trap UnknownSyscall yang sama; CPI tidak diimplementasikan |
RegisterOutOfRange | register r11..r15, atau register callx di atas r10 | trap BadInsn |
UnknownOpcode | opcode tanpa kelas v1, termasuk keluarga sdiv/srem khusus v2 dan hor64 | trap BadInsn |
JumpOutOfText | lompatan, pemanggilan, atau fall-through di luar teks fungsi | trap BadJump |
BadCallImmSrc | call imm dengan src dalam 2..=10 (hanya pada ELF yang dibuat manual) | trap BadInsn |
Penerjemah yang menolak kode yang tidak pernah dijangkau akan menolak program yang saat ini dijalankan interpreter. SPL Token adalah program semacam itu: program ini memanggil sol_set_return_data dan sol_get_sysvar, yang tidak pernah dijangkau oleh jalur Transfer.
Tiga divergensi diterima. Masing-masing hanya membuat terjemahan berhenti di tempat yang mungkin tidak menghentikan interpreter; terjemahan tidak pernah menyelesaikan eksekusi yang dihentikan interpreter.
callxdi luar himpunan yang dipindai melakukan trapBadJump, padahal interpreter mungkin menjalankan kode sungguhan pada alamat tersebut.- Pemeriksaan anggaran dapat ditunda satu basic block. Agar SPL Token muat di bawah batas 65.535 kata, 1.505 dari 3.546 block-nya melewati pemeriksaan batas instruksinya sendiri. Jenis penghentian dapat berbeda di dalam satu block yang melintasi batas. Status dan kedelapan katanya sama.
- Hanya program sumber yang berjalan. ELF lain ditolak (penjaga, di atas).
Ed25519 dan secp256k1. Implementasi perangkat lunak sol_ed25519_verify dan sol_secp256k1_recover ada di sbpf-rt/ tetapi tidak ditautkan. Interpreter melakukan trap pada keduanya, sehingga demi paritas terjemahan pun melakukan trap. Setiap pemanggilan menghabiskan sekitar 14,3 juta dan 15,7 juta siklus secara berturut-turut, jauh di atas tier terbesar (2²⁰). Keduanya memerlukan tabel koprosesor.
Angka-angkanya
Diukur pada SPL Token (guests-compiled/sbpf/programs/spl_token.so, fixture uji milik interpreter sendiri), dengan penjaga ELF terpasang.
| image | kata program | batas | hc |
|---|---|---|---|
| SPL Token, hasil terjemahan | 65.096 | 65.535 (sisa 439) | 8ca905ae…758b |
sbpf.bin, interpreter | 8.317 | 65.535 | 059a7086…f476 |
| vektor | status | instruksi sBPF | siklus hasil terjemahan | siklus sbpf.bin | tier (keduanya) |
|---|---|---|---|---|---|
Transfer 250 | 1 | 143 | 765.851 | 694.498 | 20 |
Transfer melebihi saldo | 0 | 133 | 748.836 | 679.814 | 20 |
MintTo 250 | 1 | 120 | 708.648 | 634.423 | 20 |
MintTo oleh otoritas yang salah | 0 | 134 | 696.694 | 627.241 | 20 |
Burn 250 | 1 | 131 | 710.245 | 637.038 | 20 |
Burn melebihi saldo | 0 | 121 | 695.434 | 624.781 | 20 |
Transfer dengan dua akun | 0 | 69 | 645.015 | 569.656 | 20 |
| terlalu banyak akun (harness menolak) | 2 | 0 | 303.933 | 304.616 | 20 |
Ke mana siklus digunakan pada Transfer:
| tahap | hasil terjemahan | sbpf.bin |
|---|---|---|
| membaca kedua pita | 302.881 | 302.880 |
| memuat ELF | 177.012 | 173.859 |
| memeriksa wilayah akun | 103.829 | 103.824 |
| mengosongkan stack dan heap sBPF | 49.181 | 49.181 |
| hash keluaran dan masukan, kata publik, glue | 51.091 | 42.723 |
| penjaga ELF | 72.949 | — |
| program sBPF itu sendiri | 8.908 | 22.031 |
| total | 765.851 | 694.498 |
Eksekusi program hasil terjemahan sekitar 2,4 hingga 2,5 kali lebih murah per instruksi sBPF (sekitar 62 siklus berbanding 154). Namun sekitar 98% dari setiap eksekusi adalah harness tetap, dan penjaga menambahkan 73 ribu siklus. Jadi untuk SPL Token, image hasil terjemahan lebih mahal 69 ribu hingga 75 ribu siklus, dan keduanya tetap berada di tier 20.
Penerjemahan hanya bermanfaat untuk program yang padat komputasi. Dengan penghematan sekitar 90 siklus per instruksi, pemanggilan yang mengeksekusi 10.000 instruksi sBPF menghemat sekitar 0,9 juta siklus. SPL Token mengeksekusi 120 hingga 143.
Panduan langkah demi langkah: SPL Token, diterjemahkan dan dicocokkan
Setiap perintah dijalankan dari root checkout circuits pada 7ef3220, dengan toolchain dari Deploy a Rust program dan clang 23.1.1 dari Deploy a RISC-V program. Keluaran yang panjang dipotong pada bagian yang ditandai ….
0. Bangun alat-alatnya
(cd sbpf2rv && cargo +1.98.1 build --release)
(cd rand-guest && cargo +1.98.1 build --release)
1. Terjemahkan
--out harus berupa direktori di dalam checkout circuits.
$ sbpf2rv/target/release/sbpf2rv guests-compiled/sbpf/programs/spl_token.so --out sbpf2rv/target/spl-token --name spl-token
entry pc 225: 30 function(s), 3546 block(s), 12061 instruction(s), 30 callx target(s), 0 refusal(s), 4 warning(s)
fn 225: 2065 block(s)
… (30 functions in all)
warning: UnknownSyscall { pc: 7803, hash: 2720453611 }
warning: UnknownSyscall { pc: 9530, hash: 2720453611 }
warning: UnknownSyscall { pc: 8698, hash: 2720453611 }
warning: UnknownSyscall { pc: 11547, hash: 331461893 }
wrote sbpf2rv/target/spl-token/program.c (521757 bytes of C) and the spl-token shim crate: rand-guest build sbpf2rv/target/spl-token
0 refusal(s). Keempat peringatan itu adalah syscall nyata yang tidak pernah dijangkau Transfer: hash 2720453611 adalah sol_set_return_data (tiga lokasi pemanggilan) dan 331461893 adalah sol_get_sysvar.
2. Bangun image
Image melebihi batas bawaan 4.096 kata, jadi berikan --max-words 65535.
$ rand-guest/target/release/rand-guest build sbpf2rv/target/spl-token --max-words 65535
…
65096 words against a cap of 65535 (fits); 2 ecall(s) with a non-static a7
OK
wrote sbpf2rv/target/spl-token/image.bin and its .sha256 (65096 words, hc 8ca905ae3c62f503de7de86829040f323e098b53dba5fffe09b42f1aad16758b, program id 65d234ce9ca4d082f487c038abfa76e67f41a0d91e10b9ac388f1e1541d59184)
$ rand-guest/target/release/rand-guest info sbpf2rv/target/spl-token/image.bin
form: image container
text 64819 words at 0x10000; data 102 words (101 non-zero) at 0x4f4cc; prologue 277 words; program 65096 words from base_pc 0xfbac
hc 8ca905ae3c62f503de7de86829040f323e098b53dba5fffe09b42f1aad16758b
program id 65d234ce9ca4d082f487c038abfa76e67f41a0d91e10b9ac388f1e1541d59184
65096 words against a cap of 4096: does not fit
Siapa pun yang menjalankan kedua langkah ini dengan toolchain yang sama akan mendapatkan hc ini.
3. Jalankan keduanya, lalu bandingkan
Transfer sebesar 250 membutuhkan dua daftar kata, yang dibangun oleh tests/parity.rs dari pengodean pemanggilan milik interpreter sendiri: 27.151 kata publik (ELF, dikodekan dalam kata) dan 10.458 kata privat (akun, lalu diskriminan dan jumlah Transfer). rand-guest run menerimanya sebagai --public dan --input. Dipersingkat menjadi kata-kata pertama dari masing-masing daftar:
$ rand-guest run sbpf2rv/target/spl-token/image.bin \
--public 108600 1179403647 65794 0 0 17235971 1 2088 … (27 151 words)
--input 41825 4 0 65791 0 50529027 50529027 50529027 … (10 458 words)
out[0] = 1
out[1] = 2892832079
out[2] = 376091303
out[3] = 1311040261
out[4] = 2015764099
out[5] = 3682593600
out[6] = 3311553006
out[7] = 141115266
cycles 765851
tier 20
Kedua daftar yang sama terhadap guest interpreter:
$ rand-guest run guests-compiled/bin/sbpf.bin \
--public 108600 1179403647 65794 0 0 17235971 1 2088 … (27 151 words)
--input 41825 4 0 65791 0 50529027 50529027 50529027 … (10 458 words)
out[0] = 1
out[1] = 2892832079
out[2] = 376091303
out[3] = 1311040261
out[4] = 2015764099
out[5] = 3682593600
out[6] = 3311553006
out[7] = 141115266
cycles 694498
tier 20
Kedelapan kata identik. out[0] = 1 adalah status (berhasil). out[1..8] adalah digest yang didefinisikan ABI interpreter atas masukan dan keluaran.
4. Periksa kedelapan vektor dengan satu perintah
Uji paritas membangun image hasil terjemahan, menjalankan setiap vektor melalui rand-guest run pada kedua image, dan gagal pada setiap perbedaan di kedelapan kata:
$ cd sbpf2rv && cargo +1.98.1 test --test parity the_spl_token -- --nocapture
…
translated SPL Token: 65096 program words (cap 65 535), hc 8ca905ae3c62f503de7de86829040f323e098b53dba5fffe09b42f1aad16758b
transfer 250: Ok(0), status 1; cycles translated 765851, interpreter guest 694498
transfer too much: Ok(1), status 0; cycles translated 748836, interpreter guest 679814
MintTo 250: Ok(0), status 1; cycles translated 708648, interpreter guest 634423
MintTo 250 signed by someone else: Ok(4), status 0; cycles translated 696694, interpreter guest 627241
Burn 250: Ok(0), status 1; cycles translated 710245, interpreter guest 637038
Burn 1 000 001 (balance 1 000 000): Ok(1), status 0; cycles translated 695434, interpreter guest 624781
transfer with two accounts: Ok(47244640256), status 0; cycles translated 645015, interpreter guest 569656
an account count above MAX_ACCOUNTS: Err(BadElf), status 2; cycles translated 303933, interpreter guest 304616
…
test the_spl_token_transfer_translates_and_matches_the_interpreter_word_for_word ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 27.84s
5. Deploy
rand program deploy sbpf2rv/target/spl-token/image.bin --public spl_token.so
Image membaca ELF dari tape publik, sehingga ELF menjadi input publik program, yang ditetapkan saat deploy. --public menerima berkas .so itu sendiri; wallet mengodekan berkas tersebut menjadi kata (word) yang sama sebanyak 27,151 kata seperti pada langkah 4.
Hal ini memerlukan chain yang genesis-nya menetapkan max_program_words setidaknya 65,096 dan max_program_public_words setidaknya 27,151. Chain 12 membatasi deploy pada 4,096 kata dan tidak menerima input publik, sehingga wallet menolak image ini di sana sebelum membuktikan apa pun. Chain 13, chain v0.4, menetapkan 65,535 dan 32,768, dan setiap chain sesudahnya mempertahankan kedua nilai tersebut. Chain 20, chain yang aktif (genesis 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135), juga mempertahankan batas blok dan batas call-envelope milik chain 13, serta memangkas batas proof menjadi setengahnya, yakni 4 MiB (4,194,304 byte, sejak chain 17; chain 13 hingga 16 menetapkan 8 MiB). Batas ukuran bukan lagi satu-satunya penghalang. Genesis chain 20 menetapkan hardening_v6, dan di bawahnya deploy dengan jumlah kata program yang lebih besar daripada yang dapat dimuat sebuah call pada batas call tier-14 dinyatakan tidak valid (ProgramUncallable): paling banyak 8,180 kata tanpa input publik, lebih sedikit bila disertai input publik. Image ini berukuran 65,096 kata dengan input publik 27,151 kata, sehingga chain 20 menolak deploy-nya, dan akan menolak call-nya juga: setiap vektor SPL berjalan pada tier 20, di atas batas call tier-14 (Batasan). Agregasi blok nonaktif pada chain 20 (genesis-nya tidak memiliki bagian aggregation); node bootstrap-nya tercantum di Menjalankan validator. Biaya deploy menghitung kata publik bersama kata kode: 0.001 + 0.0001 × (65,096 + 27,151) = 9.2257 RAND, biaya yang dibayar oleh deploy chain 13 di bawah ini.
Deploy ini dijalankan di chain 13 pada 2026-09-19, ketika chain 13 menjadi chain yang aktif (profil FRI produksi, genesis 8123ccac1883a45750e4df6964fb7cd3f0b321798cde4c0ef406a0293939ece3), dari wallet genesis shielded-1. Setiap chain sesudahnya (14 hingga 19) dibuat sebagai chain baru tanpa program milik chain 13, dan image ini belum dideploy ulang di salah satunya, sehingga transaksi dan tinggi blok di bawah ini milik chain 13 dan tidak lagi dapat ditemukan di explorer. Program id adalah hash dari base_pc, kata-kata, dan kata publik saja, sehingga image dan ELF yang sama mendapatkan id yang sama pada chain mana pun yang menerima deploy tersebut. Keluaran wallet, dengan baris progresnya dihilangkan (…):
$ rand program deploy <image.bin> --public spl_token.so
program id: 740236918310f8e52bb0c1ef49b2b0e0c018762e289666660685b0694c8dd00a (65096 words, hc ae05a98c03f5623c68e87dde320f0429538b093efeffa5db1a2fb4098b7516ad, public input 27151 words, digest ec57b10ed6f3a3d5fe87ec072f691322ad747377ba2f0a1788bcfe3e7334d67e)
…
proved in 94.3s: tier 14, 1418406 bytes
submitted deploy 626fc9387ecc1f6b39f44184807303ac2843029d8fb8e28338bf186445927530
…
| program id | 740236918310f8e52bb0c1ef49b2b0e0c018762e289666660685b0694c8dd00a |
| image | 65,096 kata; hc 8ca905ae… dalam penulisan rand-guest, ae05a98c… dalam penulisan wallet |
| input publik | 27,151 kata (ELF), digest ec57b10ed6f3a3d5fe87ec072f691322ad747377ba2f0a1788bcfe3e7334d67e |
| bundel biaya (bundel 2-in-2-out milik chain 13) | tier 14, 1,418,406 byte, dibuktikan dalam 94.3 s |
| tx deploy (chain 13) | 626fc9387ecc1f6b39f44184807303ac2843029d8fb8e28338bf186445927530 |
| di-anchor pada tinggi (chain 13) | 400 |
| biaya | 9.2257 RAND |
Sejak chain 14, bundel pembayar adalah satu bundel hidden-asset 4-in-4-out yang dibawa setiap transaksi, dibuktikan terhadap binding transaksi. Bundel ini masih tier 14: sekitar 100 s dan sekitar 1.42 MB pada profil produksi di chain 14 dan 15. Sejak chain 17, sebuah bundel juga membawa proof auth tier-10 (otorisasi terpisah), sehingga pada chain 18 dan 19 bundel biaya sebuah transaksi berukuran sekitar 1.49 MB proof bundel dan sekitar 1.37 MB proof auth.
Program id mengikat ELF, sehingga berbeda dari id yang dicetak rand-guest info untuk image kosong. Panggil id yang dicetak oleh deploy.
Batasan
- Tidak ada call ke program ini yang pernah dibuktikan, program ini tidak dideploy di chain 20, dan chain 20 menolak deploy maupun call-nya. Deploy di atas dijalankan di chain 13. Di bawah
hardening_v6milik chain 20, deploy dengan kata yang lebih banyak daripada yang dapat dimuat call tier-14 dinyatakan tidak valid (ProgramUncallable; paling banyak 8,180 kata), sedangkan image ini berukuran 65,096. Sisi input masih akan muat: ELF adalah input publik deploy, yang diambil sendiri olehrand call, dan 10,458 kata privat muat dalam batas input 16,071, yang dipertahankan chain 20. Namun setiap vektor SPL berada pada tier 20, dan sejak fullnode v0.5.6 (2026-09-25) sebuah node menolak setiap proof call di atas tier 14 (CallTierTooHigh), sebelum membangun verifier key. Pemeriksaan yang sama juga membatasi tabel hash yang boleh dideklarasikan sebuah call: 2¹³ baris SHA-256 (128 kompresi) dan 2¹² baris Keccak (128 permutasi). Call tier-20 hanya dapat dibuktikan di luar chain, dan belum ada proof tier-20 yang tersedia (poin berikutnya). - Belum ada proof yang dibuat pada tier 20. Proof tier-20 dihentikan paksa pada laptop 48 GB di sekitar 31 GB, dan dimatikan oleh OOM pada droplet 64 GB di 65.1 GB setelah 10 m 41 s. Diekstrapolasi dari penskalaan tier-16/18 yang terukur (memori sekitar ×3.9, waktu sekitar ×4.1 per +2 tier), tier 20 membutuhkan sekitar 330 GB dan sekitar 3.6 jam — lebih dari droplet memori terbesar DigitalOcean (m-32vcpu-256gb, 256 GB). Prover berjalan dengan satu thread: kotak 16-vCPU berada pada 99% dari satu core. Lihat memori pembuktian.
- Tidak ada CPI. Cross-program invocation menyebabkan trap.
- Hanya sBPF v1. Opcode khusus v2 menyebabkan trap.
- Syscall Ed25519 dan secp256k1 menyebabkan trap hingga tersedia tabel koprosesor untuknya.
- Chain hanya mencatat ELF sebagai input publik. Deploy dengan
--publicmenyimpan kata-kata ELF dan digest-nya, danrand_getProgramPublicmenyajikannya. Chain tidak mencatat bagaimana image dibangun darinya: publikasikan input build agar pihak lain dapat membangun ulanghc.