بخش زیر را بخوانید تا ماشینِ پشت هر اثبات در Rand را بشناسید: چه چیزی را اثبات میکند، یک اجرای برنامه چگونه به چیزی تبدیل میشود که یک اعتبارسنج در چند میلیثانیه بتواند وارسیاش کند، و حریم خصوصی از کجا میآید. برای هزینهٔ یک اثبات و نحوهٔ ساخت آن توسط کیف پول، اثباتها را ببینید. برای پارامترها و ساختارهای هش، رمزنگاری را ببینید. برای مجموعه دستوری که یک برنامه بر پایهٔ آن نوشته میشود، ISA مبتنی بر RISC-V را ببینید.
گزارهٔ یگانهای که هر اثبات بیان میکند
zkVM برای هر برنامهای که زنجیره اجرا میکند، یک نوع گزاره را اثبات میکند:
برنامهای که چکیدهٔ آن
hcاست، با شروع ازbase_pc، روی یک ورودی خصوصی کهH_INبه آن متعهد است و یک بخش ورودی عمومی کهH_PUBبه آن متعهد است اجرا شد، حداکثر در2ᵗ − 1چرخه متوقف شد، حداکثرGAS_LIMITگس مصرف کرد و این هشت کلمهٔ خروجی را نوشت.
یک انتقال shielded همین گزاره دربارهٔ یک برنامهٔ ثابت است، یعنی guest بستهٔ دارایی پنهان که hc آن در فایل genesis با نام hc_bundle ثبت شده است. از chain 17 به بعد این گزاره برای هر انتقال دو بار ساخته میشود: یک اثبات دوم و کوچک دربارهٔ guest احراز (hc_auth در فایل genesis) نشان میدهد که فرستنده کلید خرج را در اختیار دارد (اثباتها). یک فراخوان محرمانه همین گزاره است دربارهٔ یک برنامهٔ مستقرشده. برای همهٔ اینها یک ماشین، یک تأییدکننده و یک مجموعه قید وجود دارد. بنابراین استقرار یک برنامه یک عملیات دادهای است (ذخیرهٔ کلمات آن و hc) و نه رمزنگاشتی: مدار اختصاصی برای هر برنامه وجود ندارد و تنظیم مورد اعتماد (trusted setup) هم لازم نیست.
نظام اثبات یک STARK است که با Plonky3 ساخته شده است. به چیزی جز یک تابع هش نیاز ندارد: نه مراسم تنظیم و نه منحنی بیضوی، بنابراین چیزی برای شکستن توسط الگوریتم Shor باقی نمیگذارد.
خط لوله
secret input ──► run the program ──► execution trace ──► polynomials ──► commit ──► FRI ──► proof
(keys, (RV32IM emulator) (nine tables, (one per (8× code, (80 queries,
notes, paths) one row per step) column) Merkle) 20-bit grinding)
every validator: recompute the challenges from the Merkle roots, open 80 random positions,
check the rules there → ≈ 16 ms
ردیف بالا یک بار و روی دستگاه فرستنده اجرا میشود، حدود 100 ثانیه برای یک بسته و حدود 7 ثانیه برای اثبات احرازِ همراه آن. ردیف پایین روی هر اعتبارسنج، تنها از روی فیلدهای عمومی و خود اثبات اجرا میشود.
1. برنامه را اجرا کنید و همهچیز را ثبت کنید
ماشین یک پردازندهٔ 32 بیتی RISC-V است: RV32IM منهای FENCE، دستورهای CSR و EBREAK (ISA مبتنی بر RISC-V). اثباتکننده ابتدا برنامه را روی یک شبیهساز اجرا میکند و به ازای هر تیک ساعت یک سطر ثبت میکند. این رکورد ردّ اجرا (execution trace) نام دارد. هر کس آن را در اختیار داشته باشد میتواند اجرا را گامبهگام بازپخش کند؛ بقیهٔ طرح برای این وجود دارد که بتوان تأییدکننده را بدون در اختیار گذاشتن ردّ اجرا به درستیِ آن مطمئن کرد.
یک برنامه تنها از طریق هشت فراخوان سیستمی با بیرون ارتباط برقرار میکند و راه دیگری ندارد:
| فراخوان سیستمی | شماره | کارکرد |
|---|---|---|
HALT | 0 | توقف؛ هشت کلمهٔ خروجی نهایی میشوند |
WRITE_OUTPUT | 1 | تعیین یکی از هشت کلمهٔ خروجی عمومی |
READ_INPUT | 2 | خواندن یک کلمه از ورودی خصوصی |
POSEIDON2 | 3 | هش درجای یک بافر در حافظه |
KECCAK | 4 | یک جایگشت Keccak-f[1600] |
SHA256 | 5 | یک تابع فشردهسازی SHA-256 |
READ_PUBLIC | 6 | خواندن یک کلمه از بخش ورودی عمومی |
POSEIDON2_LEN | 7 | POSEIDON2 با درج طول پیام در هش (مجموعه قید 7) |
تعهدهای یادداشتها، نولیفایرها و مسیرهای Merkle در خود ماشین تعبیه نشدهاند. آنها کد معمولی guest هستند که پیامی با برچسب دامنه را در حافظه آماده میکند و POSEIDON2 را فرا میخواند. آن فراخوان پدینگ اضافه نمیکند، بنابراین [a] و [a, 0] هش یکسانی دارند. guestهای ارائهشده آن را فقط روی پیامهایی با طول ثابت فرا میخوانند. برنامهٔ جدیدی که دادهٔ با طول متغیر را هش میکند از POSEIDON2_LEN استفاده میکند (ISA مبتنی بر RISC-V).
امکان لغو وجود ندارد
پردازنده دستوری ندارد که از پایانیافتن امتناع کند، پس برنامه نمیتواند هنگام شکست یک وارسی متوقف شود. guest بسته بهجای آن شکست را ثبت میکند: هر وارسی ناموفق مقدار 1 را با ثباتی به نام bad OR میکند و bad آخرین کلمهای است که در چکیدهٔ خروجی هش میشود:
D = H(64; anchor, nf₀..₃, cm₀..₃, fee, burn_a, burn_r, burn_asset, time, c, bad)
c تعهد احرازی است که اثبات احراز نیز آن را منتشر میکند (از chain 17؛ چکیدهٔ chainهای 14 تا 16 c نداشت).
زنجیره هرگز پیشتصویرِ D را از فرستنده نمیگیرد. آن را از فیلدهای عمومی تراکنش بازسازی میکند، خودش bad = 0 قرار میدهد، هش میگیرد و مقایسه میکند. متقلب میتواند اثباتی کاملاً معتبر از اجرایی بسازد که در آن یک وارسی شکست خورده، اما آن اجرا H(…, 1) را چاپ کرده است، در حالی که زنجیره به دنبال H(…, 0) است. یکی کردن این دو به یک برخورد هش نیاز دارد.
2. ردّ اجرا را به نُه جدول تقسیم کنید
یک جدول که کل یک پردازنده را توصیف کند بهطرز اسرافآمیزی عریض میشد، بنابراین ردّ اجرا به جدولهای تخصصی تقسیم میشود که با هم، بهصورت یک دسته، اثبات میشوند:
| جدول | تعداد سطرها در سطح t | آنچه ثبت میکند |
|---|---|---|
program | اعلامشده، ≥ 2⁷ | کلمات دستور برنامه که در مدار رمزگشایی میشوند؛ تعیین میکند CPU چه چیزی را میتواند واکشی کند |
cpu | 2ᵗ | یک سطر برای هر چرخه، که پیش از آن سطرهایی هستند که برنامه، ورودی خصوصی و بخش عمومی را هش میکنند؛ 35 مقدار عمومی را نگه میدارد |
memory | اعلامشده، ≥ 2ᵗ⁺² | ثباتها و RAM، مرتبشده بر اساس (فضا، نشانی، زمان) |
alu | 2ᵗ⁺¹ | هر عمل حسابی، منطقی، شیفت، مقایسه، ضرب و تقسیم، روی اجزای بایتی |
range، nibble | هرکدام 256 | جدولهای جستجوی ثابت: بایتها، توانهای دو، AND/OR/XOR چهاربیتی |
poseidon2 | 2ᵗ⁺² | جایگشت هش، یک سطر برای هر دور |
input | اعلامشده، ≥ 2⁷ | کلمات ورودی خصوصیِ متعهدشده |
public | اعلامشده، ≥ 2⁷ | کلمات بخش عمومی (برای یک بسته، پیوند تراکنش) |
دو جدول دیگر، keccak و sha256، فقط در اثباتی حضور دارند که برنامهٔ آن آنها را فراخوانده باشد، هرکدام در صورت حضور دستکم 2⁷ سطر؛ guest بسته از هیچکدام استفاده نمیکند. کف 2⁷ سطر برای هر جدول اعلامشده همراه با مجموعه قید 7 آمد (بخش 9).
سطح (tier) t یکی از 10، 12، 14، 16، 18 یا 20 است و اندازهٔ جدولها را تعیین میکند. هر بسته در سطح 14 اثبات میشود: یک جدول CPU با 16,384 سطر که guest حدود 12,400 تای آن را استفاده میکند. guest احراز در سطح 10، 250 چرخه اجرا میکند. بقیه پدینگ است، بنابراین اثبات آشکار میکند که برنامه به کدام سطح نیاز داشته است، اما هرگز نشان نمیدهد در آن سطح چند گام برداشته است (سطحها).
جدول حافظه چیزی است که RAM را وارسیپذیر میکند. چون بر اساس نشانی و سپس زمان مرتب شده است، «یک خواندن آخرین مقدار نوشتهشده در آن نشانی را برمیگرداند» به یک قاعده میان دو سطر مجاور تبدیل میشود.
3. جدولها را با باسها به هم وصل کنید
تأییدکننده از کجا میداند جمعی که CPU استفاده کرده همان جمعی است که ALU محاسبه کرده؟ از طریق باسها (buses). وقتی CPU به یک جمع نیاز دارد پیامی (op, a, b, c) را میفرستد؛ ALU برای هر سطری که محاسبه میکند یک پیام دریافت میکند. ماشین هفده باس دارد:
| باس | میان |
|---|---|
PROGRAM، PROGRAM_WORD | cpu ← program: واکشی دستورها، و کلماتی که چکیدهٔ برنامه جذب میکند |
MEMORY | cpu ↔ memory: هر خواندن و نوشتن |
ALU | cpu → alu |
RANGE8، POW2، AND4، OR4، XOR4 | وارسیهای بازهٔ بایت، شیفتها و عملیات بیتی در برابر جدولهای ثابت |
POSEIDON2 | cpu → poseidon2: یک پیام برای هر جایگشت |
INPUT_DIGEST، INPUT_READ | cpu ← input: کلماتی که چکیدهٔ ورودی جذب میکند، و خواندنهای برنامه |
PUBLIC_DIGEST، PUBLIC_READ | cpu ← public: همین جفت برای بخش عمومی |
KECCAK، SHA256 | cpu → جدولهای هش اختیاری |
BLIND | هر جدول → جدول بعدی در یک چرخهٔ ثابت: هرکدام یک مقدار تصادفی، در سطر اول (مجموعه قید 7) |
باسها با یک استدلال مشتق لگاریتمی (LogUp) وارسی میشوند. پس از متعهد شدن جدولها، یک مقدار تصادفی α انتخاب میشود و هر جدول یک مجموع جاری از ±1/(α − m) را روی پیامهایی که میفرستد یا دریافت میکند نگه میدارد. این مجموعها دقیقاً و تنها در صورتی خنثی میشوند که هر پیام فرستادهشده دریافت شده باشد، یعنی یک چندمجموعهٔ یکسان در دو طرف. یک پیام جعلی باقیماندهای میگذارد که یک α تصادفی آن را با احتمالی بسیار بالا آشکار میکند. باس حافظه از یک خویشاوند نزدیک استفاده میکند، یعنی وارسی جایگشت چندمجموعه.
هر جدول مقدار نهایی مجموع جاری خود را منتشر میکند، یعنی مقدار پایانی (terminal) آن را، و تأییدکننده وارسی میکند که مقادیر پایانی روی هم صفر میشوند. تا مجموعه قید 6، مقدار پایانی تابعی وارسیپذیر از محتوای جدولش بود. از مجموعه قید 7، هر جدول پنج ستون اضافی دارد و در سطر اولش یک مقدار تصادفی میفرستد و مقدار همسایهٔ خود را روی باس BLIND دریافت میکند. هر مقدار پایانی بهاندازهٔ یک عنصر یکنواخت از میدان چالش جابهجا میشود و مجموع صفر میماند.
جفتهای چکیده و خواندن عمداً جدا هستند. LogUp به ازای هر پیام تراز میکند و نه به ازای هر مصرفکننده، بنابراین یک باس مشترک به اثباتکننده اجازه میداد کلمهای را از چکیده حذف کند در حالی که خواندن همان کلمه همچنان موفق شود.
4. ردّ اجرا را به دنیای بیرون لنگر کنید
سیوپنج مقدار عمومی از ردّ اجرا بیرون میآیند، همه در جدول CPU:
| مقادیر | چیستی آنها |
|---|---|
| 1 | نقطهٔ ورود pc |
| 1 | رده (tier) |
| 8 | کلمات خروجی (برای یک bundle، چکیدهٔ D) |
| 8 | hc، چکیدهٔ برنامه، که درون ردگیری (trace) از کلمات جدول برنامه محاسبه میشود |
| 8 | H_IN، چکیدهٔ ورودی خصوصی، روی ورودی و با یک salt تازهٔ ۱۲۸ بیتی در ابتدای آن |
| 8 | H_PUB، چکیدهٔ بخش عمومی، بدون salt |
| 1 | GAS_LIMIT، سقف گازی که اثباتکننده اعلام میکند (مجموعهٔ قیود 8) |
از آنجا که hc در مدار محاسبه میشود، اثباتِ مربوط به برنامهای دیگر نمیتواند بهجای اثباتِ مربوط به guest مربوط به bundle پذیرفته شود. راستیآزما هرگز کلمهٔ دستورالعمل را نمیبیند؛ او hc را با چکیدهای که انتظار دارد مقایسه میکند. از آنجا که H_IN دارای salt است، ورودی خصوصی را بدون افشای آن به اثبات پیوند میدهد، حتی در برابر کسی که بتواند ورودی را حدس بزند. از آنجا که H_PUB بدون salt است، راستیآزما میتواند آن را از کلماتی که از قبل در اختیار دارد دوباره محاسبه کند. برای یک bundle این کلمات پیوند تراکنش (transaction binding) هستند، یعنی blake3("rand-tx-bind-1" ‖ chain_id ‖ tx) روی تراکنشی که بایتهای اثبات آن خالی شده است؛ بنابراین اثباتی که روی تراکنشی با مقصد، memo، envelope یا chain id متفاوت کپی شود تأیید نمیشود. در chain 20 (با binding_domain: 1 در genesis) این مقدار blake3("rand-tx-bind-2" ‖ genesis_hash ‖ chain_id ‖ tx) است، پس یک اثبات حتی با chain id یکسان نیز روی زنجیرهٔ دیگر تأیید نمیشود؛ این کلمات ورودی عمومی هستند، بنابراین این تغییر چیزی را در مدار جابهجا نکرد. اثبات احراز هویت (auth) روی همین هشت کلمه ساخته میشود. اثبات فراخوانی (call) در chain 20 به همین شیوه پیوند میخورد: بخش عمومی آن شامل کلمات عمومی زمان استقرارِ برنامه و پس از آن هشت کلمهٔ پیوند فراخوانی است (rand-call-bind-2، و پیش از chain 20 rand-call-bind-1)، بنابراین اثبات فراخوانیای که زیر bundle کارمزد دیگری کپی شود تأیید نمیشود.
گاز (Gas) معیار ماشین برای سنگینی یک اجراست و از مجموعهٔ قیود 8 در مدار قرار دارد. جدول CPU یک ستون GAS دارد که از 1 شروع میشود و به ازای هر سطر 1 واحد، برای سطر جذب (absorb) در Poseidon2 مقدار 3، برای سطر SHA256 مقدار 64 و برای سطر KECCAK مقدار 192 به آن افزوده میشود. در سطر HALT، مدار GAS ≤ GAS_LIMIT را از طریق چهار اندام (limb) با بازهٔ بررسیشده از تفاضل اثبات میکند و این اندامها هرگز باز نمیشوند: سقف عمومی است و گازی که اجرا مصرف کرده عمومی نیست. راستیآزما پیش از ساختن کلید، سقفی را که از آنچه سرآیند خودِ اثبات میتواند نیاز داشته باشد بیشتر باشد رد میکند:
gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ / 32) + 63·(2ˢ / 64)
که در آن k و s لگاریتم ارتفاع اعلامشدهٔ جدولهای keccak و sha256 هستند و هر جمله برای جدول غایب صفر است. سقف سرآیند رده ۱۰ بدون هش برابر 1,279 و سقف رده ۱۴ برابر 20,479 است. زنجیره هر فراخوانی را بر اساس سقف اعلامشدهٔ آن قیمتگذاری میکند (کارمزدها).
5. قواعد را به اتحادهای چندجملهای تبدیل کنید
هر ستون فهرستی از اعداد در میدان Goldilocks است، یعنی اعداد صحیح به پیمانهٔ p = 2⁶⁴ − 2³² + 1. فهرستی از n عدد همان چندجملهایای از درجهٔ کمتر از n است که وقتی آن اعداد روی زیرگروه {1, ω, ω², …} از میدان چیده شوند از آنها میگذرد. Goldilocks برای هر اندازهٔ توان دو تا 2³² چنین زیرگروههایی دارد، بنابراین این تبدیل با تبدیل سریع Fourier انجام میشود.
آنگاه یک قاعدهٔ ماشین به معادلهای میان چندجملهایهای ستونی تبدیل میشود. «شمارندهٔ برنامه چهار واحد پیش میرود مگر آنکه دستورالعمل بپرد» بهطور طرحوار چنین میشود:
is_jump(x)·(pc(ωx) − target(x)) + (1 − is_jump(x))·(pc(ωx) − pc(x) − 4) = 0
برای هر x در زیرگروه، که در آن pc(ωx) یعنی «سطر بعدی». مجموعهٔ کامل این قواعد برای یک جدول، AIR (بازنمایی میانی جبری) آن جدول است. قواعد CPU و ALU به درجهٔ 8 میرسند؛ بقیه بین 2 تا 4 میمانند.
چندجملهایای که در همهٔ نقاط زیرگروه صفر باشد بر Z(x) = xⁿ − 1 بخشپذیر است. اثباتکننده همهٔ قواعد را با وزنهای تصادفی در یک چندجملهای C(x) ترکیب میکند و بر Z(x) تقسیم میکند. اگر هر قاعده در هر سطر برقرار باشد، خارجقسمت چندجملهای واقعیِ کمدرجه است. اگر یک قاعده در یک سطر نقض شود، چنین نیست. پرسش «آیا هر گام از اجرایی با 16,000 چرخه درست بود؟» به این پرسش تبدیل شده است: «آیا این تابع به یک چندجملهای کمدرجه نزدیک است؟» و به این پرسش میتوان با نگاه به چند مقدارِ تصادفی پاسخ داد.
6. پیش از دانستن پرسشها تعهد بسپارید
اثباتکننده باید چندجملهایهای خود را پیش از دیدن پرسشهای تصادفی راستیآزما ثابت کند، وگرنه میتواند پاسخهایش را متناسب با آنها بسازد. او هر ستون را روی دامنهای هشت برابر بزرگتر از جدول ارزیابی میکند (blow-up برابر 8، نرخ کد 1/8) و ریشهٔ Merkle روی ارزیابیها را منتشر میکند. این افزونگی یک کد Reed–Solomon است: یک چندجملهای کمدرجه با هر یکهشتم از مقادیرش مشخص میشود، بنابراین متقلبی که حتی یک سطر از ردگیری را تغییر دهد باید بیشتر کدواژهٔ گسترشیافته را تغییر دهد تا سازگار بماند، و بررسیهای تصادفی نقطهای به این آسیب برخورد خواهند کرد.
درختهای Merkle از Poseidon2 روی Goldilocks استفاده میکنند: عرض 8، S-box برابر x⁷، 4 دور کامل، 22 دور جزئی و 4 دور کامل، با چکیدههایی از چهار عنصر میدان (حدود 256 بیت). هر برگ با چهار عنصر تصادفی تازه هش میشود، بنابراین یک ریشه چیزی از مقادیر زیر خود فاش نمیکند. همین جایگشت درون ماشین، تعهد یادداشت (note) و هش nullifier است. 86 ثابت دور آن جدولی صریح در سورس node است (poseidon2_constants.rs)، پس هیچ ارتقای کتابخانهای نمیتواند آنها را تغییر دهد.
7. بررسی نقطهای با FRI
راستیآزما اکنون ریشههای Merkle را در دست دارد. نخست خارجقسمت را به ردگیری پیوند میدهد: نقطهای تصادفی مانند z بیرون از دامنهٔ جدول انتخاب میکند، مقدار هر ستون و مقدار خارجقسمت را در آن نقطه میخواهد و مستقیماً C(z) = Q(z)·Z(z) را بررسی میکند. آنچه میماند این است که تأیید شود توابع متعهدشده واقعاً چندجملهایهای کمدرجهاند، و این همان چیزی است که FRI آزمون میکند.
FRI با تاکردن (folding) کار میکند. چندجملهای را به بخش زوج و فرد تقسیم کنید: f(x) = fₑ(x²) + x·fₒ(x²). برای β تصادفی از سوی راستیآزما، f′(y) = fₑ(y) + β·fₒ(y) نصف درجه را روی دامنهای نصفاندازه دارد. اثباتکننده به f′ تعهد میسپارد، β تازهای میگیرد و دوباره تا میکند (Rand در هر دور تا هشت برابر تا میکند) تا ثابتی باقی بماند که آن را آشکار میفرستد. سپس یک پرسوجو (query) یک موقعیت تصادفی در لایهٔ نخست برمیگزیند و آن را به پایین دنبال میکند و در هر لایه بررسی میکند که مقادیر بازشدهٔ لایهٔ بالاتر به مقدار لایهٔ پایینتر تا میشوند. تابعی که از کمدرجه بودن دور بوده باشد نمیتواند همهٔ این تعهدها را سازگار نگه دارد.
Rand 80 پرسوجو اجرا میکند. بر پایهٔ قضیهٔ اثباتشدهٔ proximity gaps، هر پرسوجو حداکثر 9/16 شانس برای متقلب باقی میگذارد، بنابراین 80 پرسوجو (9/16)⁸⁰ ≈ 2⁻⁶⁶·⁴ میدهد. بر پایهٔ حدس ethSTARK که STARKهای مستقر به آن تکیه دارند، هر پرسوجو 1/8 باقی میگذارد که بسیار کوچکتر است.
8. گفتوگو را حذف کنید
همهٔ آنچه گذشت بهصورت یک گفتوگو توصیف شد، اما یک تراکنش باید یک پیام واحد باشد که هر اعتبارسنج بتواند بهتنهایی آن را بررسی کند. تبدیل Fiat–Shamir راستیآزما را حذف میکند: اثباتکننده هر چالش «تصادفی» را بهصورت هش هر آنچه تا آن لحظه به آن تعهد سپرده شده، با یک اسفنج دوبلکس Poseidon2 محاسبه میکند. تغییر هر تعهد، همهٔ چالشهای پس از آن را تغییر میدهد، بنابراین اثباتکننده نمیتواند تعهدها را متناسب با چالشها انتخاب کند.
دو اقدام دیگر شکافهای باقیمانده را میبندند:
- میدان گسترشیافته. میدانی با
2⁶⁴عنصر برای استخراج چالشها بسیار کوچک است: متقلب فقط لازم دارد چالش روی یکی از معدود مقادیر بد یک چندجملهای بیفتد و با احتمال حدودN/2⁶⁴موفق میشود، هر تعداد پرسوجو که اجرا شود. بنابراین ردگیری در Goldilocks میماند، اما هر چالش از گسترش درجه دوم آن، با حدود2¹²⁸عنصر، استخراج میشود. - Grinding. پیش از آنکه موقعیتهای پرسوجو استخراج شوند، اثباتکننده باید nonceای بیابد که هش آن با رونوشت (transcript) با 20 بیت صفر آغاز شود، یعنی حدود یک میلیون هش. اثباتکنندهٔ درستکار این هزینه را یک بار میپردازد. متقلبی که موقعیتهای پرسوجو را دوباره میاندازد تا از جعل او دور بمانند، این هزینه را در هر تلاش میپردازد. این تنها اثبات کار (proof of work) در همهٔ Rand است و هیچ ارتباطی با اجماع ندارد.
جمعبندی: 66.4 بیت از پرسوجوها بهعلاوهٔ 20 بیت از grinding، 86 بیت اثباتشده میدهد، و حدود 100 بیت بر پایهٔ حدس، که در آن جملهٔ میدان گسترشیافته حد را تعیین میکند.
9. دانش صفر از کجا میآید
یک STARK بهخودیِخود دانش صفر نیست: مقادیر بازشده در موقعیتهای پرسوجو مقادیر چندجملهایهای ردگیریاند و بدون احتیاط نشت میکنند. پیکربندی Rand چهار لایه میافزاید:
- هر چندجملهای متعهدشده تصادفیسازی میشود (FRI پنهانساز Plonky3 چهار کدواژهٔ تصادفی را در آن میآمیزد)، بنابراین 80 مقدار بازشده را میتوان، از نظر توزیع، توسط کسی که هرگز ردگیری را ندیده بازتولید کرد؛
- هر برگ Merkle دارای salt است، بنابراین ریشهها و مسیرهای احراز چیزی فاش نمیکنند؛
- تصادفیبودن اثباتکننده برای هر اثبات از نو از سیستمعامل گرفته میشود، بنابراین دو اثبات از یک پرداخت ظاهراً بیارتباطاند؛
- هر جدول اعلامشده دستکم
2⁷سطر دارد و هر پایانهٔ LogUp کور (blinded) میشود (مجموعهٔ قیود 7). یک اثبات هر جدول را در 80 نقطهٔ پرسوجو و دو نقطهٔ خارج از دامنه باز میکند، و تعهد پنهانساز بهاندازهٔ تعداد سطرهای جدول سطر تصادفی در آن میآمیزد، بنابراین جدول تنها تا وقتی پنهان است که دستکم 82 سطر داشته باشد. 128 کوچکترین توان دویی است که چنین است.
لایهٔ ۴ یک اصلاحیه است. بازبینی ۲۷‑۰۹‑۲۰۲۶ توانست ورودیهای خصوصی فراخوانیهایی با ۳۱ کلمهٔ ورودی یا کمتر را از اثباتهای production بازیابی کند، تعداد fetchهای یک برنامه را از جدول برنامهٔ کوتاه آن بخواند، و اسلاتهای ورودی واقعی یک bundle را از اسلاتهای دروغین آن با ترمینالهای بدون پوشش (unblinded) تشخیص دهد. این اثباتها همانهایی هستند که روی chain 15 و پیش از آن ساخته شدهاند. chain 16 (مجموعهٔ قیود ۷، با guest بدون شاخهٔ bundle) هر سه مورد را بست و chainهای ۱۸ تا ۲۰ این اصلاحها را حفظ کردهاند.
تضمین، دانشصفر آماری در مدل اوراکل تصادفی است. آنچه یک اثبات پنهان نمیکند دقیقاً مشخص است: tier آن، ارتفاعهای اعلامشدهٔ جدولهایش، و مقادیر عمومی آن، از جمله سقف gas. ارتفاع اعلامشدهٔ keccak یا sha256 برابر ۰ یعنی اجرا هرگز از آن هش استفاده نکرده است؛ بنابراین سرآیند اثبات یک فراخوانی نشان میدهد که فراخوانی هش کرده است یا نه، و بالاتر از کف، تعداد دفعات را تا ضریب دو. برای یک bundle، مقادیر عمومی و فیلدها عبارتاند از anchor، چهار nullifier، چهار commitment، کارمزد، burnها، دارایی burn، زمان، commitment احراز اختیار و تعهد تراکنش (transaction binding). داراییای که یک انتقال جابهجا میکند ورودی خصوصی است، پس پرداخت zUSD و پرداخت RAND روی زنجیره یکسان به نظر میرسند. hc قطعی است، بنابراین هر کس میتواند تشخیص دهد که یک اثبات به guest مربوط به bundle تعلق دارد (هر اثبات جابهجاکنندهٔ ارزش چنین است) یا، برای یک فراخوانی، کدام برنامهٔ مستقرشده را اجرا کرده است.
10. آنچه یک validator بررسی میکند
Machine::verify_public(hc, words, proof) به ترتیب از ارزانترین بررسی، این مراحل را اجرا میکند:
- tier اعلامشده و ارتفاعهای جدول در اثبات محدود هستند (
check_declared_heights) پیش از آنکه چیزی بر اساس آنها اندازهگیری شود، بنابراین یک سرآیند نادرست فقط چند مقایسه هزینه دارد، نه ساخت چندثانیهای کلید؛ یک bundle باید tier 14 را بدون هیچیک از جدولهای هش اعلام کند، و یک اثبات احراز اختیار tier 10؛ - ۳۵ مقدار عمومی وجود دارد که هر کدام یک عنصر میدان در شکل کانونی است، با
pv[HC] = hc،pv[TIER]برابر با tier اثبات، هر کلمهٔ خروجی کمتر از2³²، وGAS_LIMITحداکثر برابرgas_maxسرآیند؛ - STARK دستهای (batch) با یک کلید verifier تأیید میشود که بهازای هر شکل
(tier, program, input, keccak, sha256, public heights)در حافظهٔ نهان نگه داشته میشود، نه بهازای هر برنامه؛ H_PUBبرابر با digest مربوط بهwordsاست که برای یک bundle همان تعهد تراکنشی است که خود node محاسبه کرده است.
پیش از همهٔ اینها، node هر اثبات ارسالشده یا gossipشده را از نظر کدگذاری کانونی بررسی میکند: برنامهٔ تاشدن FRI آن باید همان برنامهای باشد که یک prover درستکار برای درجههایی که سرآیند اعلام میکند میسازد. از fullnode v0.6.8 (audit v6، ZKV-4) این بررسی ابتدا هر درجهٔ اعلامشدهٔ بیش از ۲۵ بیت را رد میکند (MAX_HONEST_DEGREE_BITS، یعنی سقف 2²⁴ جدول حافظه بهعلاوهٔ دو برابر شدن دانشصفر، بیشترین مقداری که یک اثبات پذیرفتهشده دارد). پیش از این حد، سرآیندی که درجهٔ بسیار بزرگی اعلام میکرد باعث میشد node برنامهٔ تاشدن عظیمی را روی event loop خود دوباره استخراج کند و تا هر مدت که فرستنده میخواست آن را مشغول نگه دارد؛ verifier به هر حال اثبات را رد میکرد، پس این حد فقط هزینهٔ «نه» گفتن را تغییر میدهد. یک panic درون این بررسی اکنون فقط همان یک تراکنش را رد میکند و node را متوقف نمیکند.
سپس ledger هش bundle یعنی D را با bad = 0 دوباره محاسبه میکند و با هشت کلمهٔ خروجی اثبات مقایسه میکند. روی chain 20 همچنین لازم است GAS_LIMIT اثبات یک bundle دقیقاً برابر bundle_gas_limit در genesis یعنی 20,479 باشد و برای اثبات احراز اختیار 1,279، تا هر انتقال مقادیر یکسانی اعلام کند. با یک کلید گرم، کل بررسی حدود 16 ms طول میکشد.
پارامترها و دلیل انتخاب آنها
هر عدد زیر بخشی از اجماع است: verifier آن را بررسی میکند، اثباتی که با مقدار دیگری ساخته شود تأیید نمیشود، و تغییر هر یک یک مجموعهٔ قیود جدید و یک hard fork است (Constraint sets). پروفایل FRI را فایل genesis تعیین میکند (fri_profile: production روی chain 20)، هرگز خود اثبات.
| پارامتر | مقدار | دلیل این مقدار |
|---|---|---|
| میدان پایه | Goldilocks، p = 2⁶⁴ − 2³² + 1 | عددی اول ۶۴بیتی که حسابش با چند دستور ماشین انجام میشود و زیرگروههای ضربیاش به هر اندازهٔ توانی از دو تا 2³² وجود دارند؛ بنابراین تبدیل یک ستون به چندجملهای یک تبدیل فوریهٔ سریع است. ردگیری (trace) در اینجا قرار میگیرد، چون این میدان ارزانترین است. |
| میدان چالش | توسیع درجه دو F_p² (≈ 2¹²⁸) | اگر چالشها از خود Goldilocks انتخاب میشدند، کفی برای درستی (soundness) در حدود N/2⁶⁴ باقی میماند که هیچ تعداد پرسوجویی نمیتواند پایینترش بیاورد. توسیع این جمله را برای ردگیریهایی تا 2²⁵ سطر به حداکثر 2⁻¹⁰⁰ میرساند. فقط چالشها هزینهٔ میدان بزرگتر را میپردازند؛ ردگیری نه. اگر روزی ۱۲۸ بیت لازم شود، توسیع درجه سه (≈ 2¹⁹²) حاشیهٔ اطمینانی است که نام برده شده است. |
| ضریب انبساط (blow-up) | ۸ (نرخ ρ = 1/8، log_blowup = 3) | دو دلیل همزمان. جدولهای CPU و ALU قواعدی از درجهٔ ۸ دارند و با پرچم دانشصفر، خارجقسمت درجهٔ ۹ پیدا میکند که بیشترین مقداری است که ضریب انبساط ۸ میتواند تحمل کند؛ قاعدهای با درجهٔ بالاتر ضریب ۱۶ را الزامی میکرد. و توان هر پرسوجو در گرفتن متقلب به نرخ بستگی دارد: (1 + ρ)/2 = 9/16 بهازای هر پرسوجو در حالت اثباتشده، و ρ = 1/8 در حالت حدسی. ضریب انبساط بزرگتر پرسوجوهای کمتری میخرید، ولی به بهای توسیع درجهپایین (low-degree extension) بزرگتری به همان نسبت که اثباتکننده باید آن را محاسبه کند و در حافظه نگه دارد. |
| پرسوجوهای FRI | ۸۰ | عددی که درستی اثباتشده را در حدود ۸۶ بیت نگه میدارد. این عدد یک بار به ۲۷ کاهش یافت (مجموعهقیدهای ۳ و ۴)، که هدف حدسی ۱۰۰ بیت را برآورده میکرد (3·27 + 20 = 101) اما فقط حدود ۴۲ بیت اثباتشده باقی میگذاشت؛ ممیزی zk مورخ 2026-09-12 (یافتهٔ ZM1) عدد ۸۰ را بازگرداند. پرسوجوها بیشترِ حجم اثبات را تشکیل میدهند و به همین دلیل اثبات یک بسته ≈ ۱٫۵۰ MB است. رسیدن به ۱۰۰ بیت اثباتپذیر ۹۷ پرسوجو میخواهد. |
| grinding | ۲۰ بیت، در فاز پرسوجو | پیش از آنکه مکانهای پرسوجو انتخاب شوند، اثباتکننده باید نانسی پیدا کند که هش آن با ۲۰ بیت صفر آغاز شود: حدود یک میلیون هش که اثباتکنندهٔ درستکار یک بار میپردازد و متقلبی که مکانها را دوباره قرعه میکشد در هر تلاش مجدد. این کار ۲۰ بیت را ارزان اضافه میکند؛ grinding بیشتر (۳۴ بیت هم ۱۰۰ بیت اثباتپذیر میداد) هر اثبات درستکار را کندتر میکند. فاز تعهد (commit) grinding ندارد. |
| ضریب تاشدن (arity) | تا ۸ در هر دور (max_log_arity = 3) | تا کردن با ضریب هشت بهجای دو، تعداد لایههای FRI را به یکسوم میرساند؛ پس هر پرسوجو مسیرهای Merkle کمتری باز میکند و اثبات کوچکتر میشود، به بهای کمی کار بیشتر در هر لایه. |
| چندجملهای نهایی | یک ثابت (log_final_poly_len = 0) | FRI تا انتها تا میشود و یک مقدار را آشکار میفرستد؛ سادهترین بررسی پایانی برای تأییدکننده. |
| هش (در مدار، Merkle، رونوشت) | Poseidon2، عرض ۸، S-box برابر x⁷، ۴ + ۲۲ + ۴ دور، نرخ ۴، ظرفیت ۴ | هشی که برای ارزان بودن بهصورت حساب میدانی ساخته شده است، تا هش کردن درون یک اثبات (تعهدها، nullifierها، مسیرهای Merkle، چکیدهٔ برنامه) هزینهاش سطر باشد، نه هزاران عمل بیتی. نمای S-box نباید با p − 1 = 2³²·3·5·17·257·65537 هیچ عامل مشترکی داشته باشد و ۷ کوچکترین مقداری است که کار میکند. عرض ۸ با نرخ ۴ چکیدهای چهارعنصری میدهد، حدود ۲۵۶ بیت. ۸۶ ثابت دور بهصورت جدولی صریح در نود قرار دارند (poseidon2_constants.rs). |
| عرض چکیده | ۴ عنصر میدانی (≈ ۲۵۶ بیت) | برای نقشهای پیشتصویر (nullifierها، تعهدها: ۱۲۸ بیت در برابر Grover) کافی است. برای مقاومت در برابر برخورد کوانتومی ۱۲۸ بیتی در رونوشت کافی نیست (2²⁵⁶/³ ≈ 2⁸⁵)؛ طراحی کاغذ سفید هشی ۳۸۴ بیتی برای رونوشت میخواهد و زنجیری که در حال اجراست آن را نپذیرفته است. |
| پنهانسازی | ۴ کدواژهٔ تصادفی در FRI؛ ۴ عنصر نمک برای هر برگ Merkle؛ تصادفیبودن تازهٔ سیستمعامل برای هر اثبات | کمترین مقداری که ۸۰ مکان بازشده و مسیرهای Merkle را فقط از مقادیر عمومی شبیهسازیپذیر میکند، تا اثبات از نظر آماری دانشصفر باشد. اثباتهای یک پرداخت واحد که دو بار انجام شده باشد، بیارتباط به نظر میرسند. |
| کف جدول | 2⁷ سطر برای هر جدول اعلامشده (مجموعهقید ۷) | هر اثبات ۸۰ نقطهٔ پرسوجو و ۲ نقطهٔ بیرون از دامنه از هر جدول باز میکند، در مجموع ۸۲ نقطه، و جدولی با h سطر با h سطر تصادفی کور میشود. ۶۴ سطر کم است و ۱۲۸ نزدیکترین توان دو است. تعداد پرسوجوی بالاتر از ۱۲۶ به 2⁸ نیاز دارد. |
| کوری پایانی | یک مقدار از F_p² برای هر جدول، روی گذرگاه BLIND (مجموعهقید ۷) | پایانهٔ LogUp از ردگیری خام محاسبه میشود، پس بدون کوری، تعهدی بدون نمک به جدول خود است. کوری یک عنصر از میدان توسیع است، چون یک مختصات از میدان پایه انتقال را به مجموعهای نازک محدود میکرد که میشد شاهدی نامزد را در برابرش آزمود. |
| وزنهای گس | ۱ برای هر سطر؛ ۳ برای هر سطر جذب Poseidon2؛ ۶۴ برای هر سطر SHA256؛ ۱۹۲ برای هر سطر KECCAK (مجموعهقید ۸) | هر وزن سطرهایی را میشمارد که آن سطر در جدولهای دیگر پدید میآورد: سطر جذب یک جایگشت ۳۲سطری، سطر SHA256 ۶۴ سطر با ۴۶۶ ستون، و سطر KECCAK ۳۲ سطر با ۲٬۶۱۲ ستون و ۱۰۰ سطر حافظه. |
| بیشینهٔ درجهٔ قید | ۸ | با ضریب انبساط بالا تعیین میشود؛ درجهٔ هر جدول با یک آزمون ثابت نگه داشته میشود تا قاعدهای جدید نتواند بیصدا از آن فراتر برود. |
| ردهها (tiers) | ۱۰، ۱۲، ۱۴، ۱۶، ۱۸، ۲۰ | اندازهٔ جدولها در شش گام ثابت تعیین میشود که هر یک چهار برابر قبلی است، و اثبات فقط ردهٔ خود را آشکار میکند: سطلی درشت و پدشده از طول اجرا، نه تعداد دقیق چرخهها. هر بسته ردهٔ ۱۴ است و هر اثبات احراز هویت ردهٔ ۱۰؛ از fullnode v0.5.6 به بعد فراخوانیها به ردهٔ ۱۴ محدود شدهاند. |
| بیشینهٔ ردگیری | 2²⁴ سطر (2²⁵ با بیت دانشصفر) | اندازهای که جملهٔ میدان توسیع برایش اثبات شده است؛ ردگیری بلندتر رد میشود و تحت ادعای درستیای که برقرار نیست اثبات نمیشود. |
| مقادیر عمومی | ۳۵ عنصر میدانی | pc ورودی، ردهٔ اجرا، هشت واژهٔ خروجی، سه چکیدهٔ هشتواژهای hc، H_IN، H_PUB، و سقف گس اعلامشده: کوچکترین گزارهای که برنامه، ورودی خصوصی، ورودی عمومی و کرانی بر کار انجامشده را نام میبرد. تا مجموعهقید ۷ این عدد ۳۴ بود. |
| سقف اندازهٔ اثبات | پیشفرض ۲ MiB؛ ۴ MiB در chain 20 (max_proof_bytes) | ۲ MiB بالاتر از هر اثباتی است که guestهای مستقرشده بدون جدول هش میسازند و پایینتر از اثباتی است که keccak دارد (≈ ۳٫۳ MB)؛ genesis زنجیر ۲۰ مقدار 4,194,304 را میگذارد تا هر دو پذیرفته شوند، همانطور که زنجیرهای ۱۷ تا ۱۹ چنین کردند. زنجیرهای ۱۳ تا ۱۶ مقدار ۸ MiB گذاشتند. سقف پیش از رمزگشایی اثبات بررسی میشود. |
جمع این اعداد چیست: ۸۶ بیت درستی اثباتشده، حدود ۱۰۰ بیت تحت حدس ethSTARK (که در آن میدان توسیع، نه پرسوجوها، حد را تعیین میکند)، و حدود ۸۵ بیت در برابر حریف کوانتومی که هش ۲۵۶ بیتی رونوشت آن را تعیین میکند. کاغذ سفید عمداً هر دو رقم درستی را کنار هم نگه میدارد: هر FRI-STARK مستقر برای بیتهای آخر خود به حدس تکیه دارد، و خوانندهای که اندازهٔ یک استقرار را میسنجد باید بداند هر فرض بار کدام بیتها را میکشد.
اثباتهایی دربارهٔ اثباتها
با حدود ۱٫۵۰ MB، اثبات یک بسته سنگین است، اثبات احراز هویت ۱٫۳۷ MB در کنار آن حرکت میکند و هر نود همه را ذخیره میکند. درمانی که ساخته شده بازگشت (recursion) است: اثباتی که گزارهاش این است «من این اثباتها را تأیید کردم». fullnode برای این کار ماشینی دوم و کوچکتر دارد، recursion VM، که واژهٔ بومیاش یک عنصر Goldilocks است و دستورهایش عملیات خود تأییدکنندهاند (حساب میدانی، Poseidon2، گامهای Merkle و FRI). برنامهٔ تجمیعی آن تأیید کامل N اثبات بسته را بازپخش میکند و چکیدهای از مقادیر عمومی آنها منتشر میکند که به زنجیر، تجمیعکننده و یک nonce بسته شده است. تأیید یک اثبات بسته درون آن تحت مجموعهقید ۶ برابر 1,968,619 سطر و تحت مجموعهقید ۷ برابر 2,044,506 سطر هزینه داشت؛ تحت مجموعه ۸ دوباره اندازهگیری نشده است. این ماشین پیادهسازی شده و تا امروز در genesis هر زنجیر، از جمله chain 20، خاموش شده است. برنامهٔ تجمیعی فقط اثباتهای بسته را تأیید میکند، پس در زنجیری با مجوزدهی تفکیکشده اثبات احراز هویت میماند. اثباتهای تولیدی آن به حافظهٔ بیشتری از پیشبینیشده هم نیاز دارند: در اندازهگیری تحت مجموعهقید ۷، ۱۳۳ GB و بیشتر (Upcoming: block aggregation).
آنچه این طرح ادعا نمیکند
- حاشیهٔ امنیت کوانتومی حدود ۸۵ بیت است، نه ۱۲۸. جستوجوی تصادم کوانتومی روی یک هش
nبیتی حدود2ⁿ/³هزینه دارد. طراحی مقالهٔ فنی (whitepaper) یک هش ۳۸۴ بیتی برای transcript را در نظر گرفته است؛ زنجیرِ در حال اجرا از خروجی ۲۵۶ بیتی Poseidon2 استفاده میکند. افزایش پهنای آن به یک مجموعهٔ قید (constraint) جدید و یک هارد فورک نیاز دارد. - ۱۰۰ بیت یک حدس است؛ ۸۶ بیت اثباتشده است. بالا بردن عدد اثباتشده به ۱۰۰ بیت ۹۷ پرسوجو (query) هزینه دارد، یعنی حدود یکپنجم دادهٔ پرسوجوی بیشتر در هر اثبات.
- ثابتهای Poseidon2 یک نمونهگیری با seed هستند، نه یک نمونهسازی منتشرشده. حملهای به آن شناخته نیست؛ پذیرش یک مجموعهٔ استاندارد، هر هش روی زنجیر را تغییر میدهد.
- برخی گزارهها در کنار اثبات بررسی میشوند، نه درون آن. مقدار عمومی
TIERرا verifier با ساختار اثبات مقایسه میکند، اما مدار آن را قید نمیکند (ممیزی ZL1)، و انضباط taint در guest بستهای (bundle) با استدلال ساختاری، یک harness با guest تضعیفشده و fuzzing جهش پشتیبانی میشود، نه با راستیآزمایی ماشینی. - سرایند (header) یک اثبات پنهان نیست. سطح (tier) و ارتفاع دو جدول هش آشکارند، بنابراین اثبات یک فراخوانی نشان میدهد که فراخوانی از
KECCAKاستفاده کرده است یاSHA256. گزینهای برای prover وجود دارد که هر دو جدول را پر (pad) میکند و هیچ کیف پولی آن را تنظیم نمیکند: اندازهٔ اثبات tier-10 را از حدود ۱٫۳ مگابایت به حدود ۳٫۶ مگابایت میرساند. - فراخوانی سیستمی
POSEIDON2روی ورودی با طول متغیر یکبهیک (injective) نیست. به همان شکل نگه داشته شده است، چون هر تعهد (commitment)، nullifier، ریشه و digest برنامه به آن وابسته است. هنوز هیچ guest منتشرشدهای ازPOSEIDON2_LENاستفاده نمیکند. - اثباتسازی کند است و فرستنده انجامش میدهد، یا proverای که فرستنده با او جفت میشود: برای یک bundle روی CPU لپتاپ حدود ۱۰۰ ثانیه و ۵٫۷ گیگابایت. هستههای GPU برای اولین بار در 2026-09-28 روی یک GPU (یک NVIDIA H100) اجرا شدند و با مرجع CPU یکسان بودند؛ کیف پول همچنان روی CPU اثبات میکند.
جای مطالعهٔ بیشتر
- بخش «The Zero-Knowledge Scheme: How a Rand Proof Works» در whitepaper (
randprotocol.tex) همین هفت جزء را همراه با قضیههای امنیت (soundness) شرح میدهد. - مقالهٔ پیادهسازی (
randprotocol_implementation.tex) مجموعه دستورها و فراخوانیهای سیستمی، guest بستهای (bundle)، اسفنج و digestها، هر جدول و باس، پیکربندی ماشین و ماشین مجازی بازگشتی (recursion VM) را مشخص میکند. - کد:
crates/randprotocol-zkvm/src/در fullnode —emulator.rs،tables/،machine.rs،gas.rs،guests.rs،hash.rs،poseidon2_constants.rs.