Rand Protocol
مستندات
مفاهیم اصلی

تراکنش‌ها و کنش‌ها

تراکنش در شبکه، شناسه‌ی آن، هر Action که دفتر کل می‌شناسد، آن‌هایی که chain 20 می‌پذیرد، و ترتیبی که یک گره ارسال را بر اساس آن بررسی می‌کند.

از این صفحه برای ساختن، ارسال یا رمزگشایی یک تراکنش استفاده کنید: شکل سیمی‌ای که rand_sendTransaction می‌پذیرد، نحوهٔ محاسبهٔ شناسه، فهرست کامل کنش‌ها با قواعد بسته و کارمزدشان، و ترتیب پذیرشی که تعیین می‌کند ابتدا کدام خطا را دریافت کنید.

تراکنش

Transaction { chain_id: u64, bundle: Option<Bundle>, action: Action }

Bundle {                                        // the hidden-asset bundle (since chain 14)
  anchor: Word8, nullifiers: [Word8; 4], commitments: [Word8; 4],
  fee: u64, burn_a: u64, burn_r: u64, burn_asset: u32, time: u32,
  envelopes: [Envelope; 4], proof: Vec<u8>,     // postcard(Proof) of the hidden-asset guest
  auth_commit: Word8, auth_proof: Vec<u8>,      // since chain 17: split authorisation's c and its auth proof
}
Envelope { kem_ct: Vec<u8>, to_receiver: Vec<u8>, to_sender: Vec<u8>, body: Vec<u8> }

Word8 هشت واژهٔ u32 به‌صورت little-endian است که با ۶۴ نویسهٔ هگز با حروف کوچک نمایش داده می‌شود. شکل سیمی همان bincode(Transaction) با پیش‌فرض‌های bincode است (اعداد صحیح با عرض ثابت، پیشوند طول ۸ بایتی و برچسب‌های enum از نوع u32) و rand_sendTransaction آن را به‌صورت هگز دریافت می‌کند. هیچ امضایی روی تراکنش و هیچ فیلد فرستنده‌ای وجود ندارد: یک بسته با اثبات‌های خود مجاز می‌شود (اثبات بسته و، از زنجیرهٔ ۱۷، اثبات احراز هویت در کنار آن) و کنشی که امضا می‌شود، امضا را در درون خود دارد. حفاظت در برابر بازپخش برای بسته‌ها مجموعهٔ ابطال‌گرهاست، برای کنش‌های امضاشدهٔ سهام‌گذاری یک nonce برای هر اعتبارسنج در ثبت‌نامه، برای TokenMint و SetAuthority یک mint_nonce برای هر توکن، و برای کنش‌های حاکمیتی پل، pause_nonce و list_nonce آن.

یک تراکنش حداکثر یک بسته دارد و هر کنشی که بدون بسته نباشد دقیقاً یک بسته دارد. شکاف‌های ۰ و ۱ بسته دارایی خصوصی و شکاف‌های ۲ و ۳ RAND را حمل می‌کنند، پس یک اثبات هم توکن را جابه‌جا می‌کند و هم کارمزد RAND آن را می‌پردازد؛ سوزاندن دوبسته‌ای پل در طراحی پیشین حذف شده است: یک سوزاندن یک بسته است (یادداشت‌ها).

کیف پول rand همهٔ این کارها را از طریق randprotocol_client::wallet::{send, submit} انجام می‌دهد: انتخاب ورودی‌ها، دریافت لنگر، محاسبهٔ شاهدها از نسخهٔ خود کیف پول از درخت، ساخت خروجی‌ها و مهروموم هر چهار پاکت، گرفتن پیوند تراکنش، اثبات بسته و اثبات احراز هویت، بررسی چکیدهٔ منتشرشدهٔ اثبات بسته، و ارسال.

پیوند تراکنش

یک تراکنش امضا ندارد و اثبات یک بسته فقط به چکیدهٔ خود بسته متعهد می‌شود: لنگر، ابطال‌گرها، تعهدها، کارمزد، فیلدهای سوزاندن و time. تا زنجیرهٔ ۱۳ هیچ چیز بستهٔ اثبات‌شده را به کنشی که همراهش می‌آمد یا به پاکت‌هایش پیوند نمی‌داد، بنابراین هر کسی که تراکنشی را می‌دید می‌توانست همهٔ بایت‌های اثبات را نگه دارد و بقیه را تغییر دهد: مقصد یک BridgeBurn، اعتبارسنج یک Bond یا یک پاکت. از زنجیرهٔ ۱۴ هر اثبات بسته روی پیوند تراکنش ساخته و در برابر آن راستی‌آزمایی می‌شود:

binding(tx) = blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action'))            chains 14 to 19
binding(tx) = blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action'))  chain 20
                                                                                  as 8 LE u32 words

که در آن ' یعنی هر رشته‌بایت اثبات بسته با بردار تهی جایگزین می‌شود: bundle.proof، از زنجیرهٔ ۱۷ bundle.auth_proof، و proof یک Aggregate. هر چیز دیگری داخل پیوند است: auth_commit، هر چهار پاکت، هر فیلد سوزاندن، یک مقصد، یک اعتبارسنج، یک گیرنده، امضاها، گواهی نگهبان و هم‌امضایی‌های Dilithium2 آن، و اثبات خود یک Call. هشت واژه در بخش ورودی عمومی اثبات قرار می‌گیرند که چکیدهٔ آن، یعنی H_PUB، پیشاپیش به مدار بسته شده است؛ از این رو این اصلاح به هیچ تغییری در guest یا مدار نیاز نداشت. گره Machine::verify_public(hc_bundle, binding, proof) را اجرا می‌کند و اثبات بسته‌ای را که ارتفاع عمومی دیگری اعلام کند رد می‌کند (public height not the transaction binding's). Transaction::binding تنها تابعی است که کیف پول پیش از اثبات و دفتر کل پیش از راستی‌آزمایی فراخوانی می‌کند. از زنجیرهٔ ۱۷ اثبات احراز هویت روی همین هشت واژه ساخته و در برابر آن راستی‌آزمایی می‌شود، پس آن را هم نمی‌توان به تراکنش دیگری منتقل کرد.

زنجیرهٔ ۲۰ هش جنسیس را به پیوند می‌افزاید. تا زنجیرهٔ ۱۹ پیوند، و پیام‌های امضاشدهٔ کنش‌های بدون بسته، فقط شناسهٔ زنجیره را پوشش می‌دادند؛ بنابراین دو زنجیر با شناسهٔ زنجیرهٔ یکسان (یک برش مجدد، یک نسخهٔ خصوصی) اثبات‌ها و امضاهای یکدیگر را می‌پذیرفتند. جنسیس زنجیرهٔ ۲۰ مقدار binding_domain: 1 را تعیین می‌کند (v0.6.8، ممیزی v6 BIND-1؛ crates/randprotocol-core/src/types/binding.rs). سپس هش جنسیس در آغاز هر پیش‌تصویر، زیر برچسبی یک نسخه بالاتر قرار می‌گیرد: rand-tx-bind-2 و rand-call-bind-2 برای دو پیوند، و همین‌طور برای ضرب شیرکت (rand-mint-3)، Unbond و Withdraw (rand-unbond-2 و rand-withdraw-2)، پیام‌های ضرب و اختیار RPL (rand-rpl-mint-2 و rand-rpl-authority-2)، کنش‌های aggregator و پیام‌های حاکمیتی پل. ثبت‌نام اعتبارسنج در چنین زنجیره‌ای همیشه پیام نسخهٔ ۲ است (جنسیس، زنجیره، نشانی، پرداخت). هیچ چیزی که برای یک زنجیره اثبات یا امضا شده باشد، روی زنجیرهٔ دیگر، با هر شناسهٔ زنجیره‌ای، راستی‌آزمایی نمی‌شود. واژه‌های پیوند ورودی عمومی هستند، بنابراین هیچ کلید guest یا verifier تغییر نکرد. کیف پولی قدیمی‌تر از v0.6.8 همچنان در برابر شکل شناسهٔ زنجیره اثبات می‌کند و در زنجیرهٔ ۲۰ رد می‌شود. rand_getLimits و rand_status مقدار binding_domain را برمی‌گردانند (1 در زنجیرهٔ ۲۰، و 0 بدون این فیلد).

تحت hardening_v6 جنسیس (زنجیره‌های ۱۶ تا ۲۰) اثبات خود یک Call نیز با چکیدهٔ دومی از همان ساختار، زیر دامنهٔ خودش، پیوند می‌خورد؛ در آن اثبات call نیز مانند اثبات‌های بسته تهی می‌شود:

call_binding(tx) = blake3("rand-call-bind-1" || bincode(chain_id, bundle', action''))   as 8 LE u32 words

در زنجیرهٔ ۲۰ برچسب rand-call-bind-2 است و هش جنسیس در آغاز پیش‌تصویر می‌آید، همان‌طور که در بالا آمد. اثبات یک Invoke به همین شکل پیوند می‌خورد و گذار اعلام‌شدهٔ آن در پیش‌تصویر می‌ماند، پس اثبات دقیقاً به تغییر وضعیت، پرداخت‌ها و گیرندگانی که تراکنش حمل می‌کند متعهد می‌شود.

اثبات call این واژه‌ها را در بخش ورودی عمومی خود حمل می‌کند، پس از ورودی عمومی زمان استقرار برنامه در صورتی که داشته باشد؛ از این رو نسخه‌ای از آن که به بستهٔ کارمزد دیگری الصاق شود راستی‌آزمایی نمی‌شود. این دو پیوند تودرتو هستند: کیف پول یادداشت‌های بستهٔ کارمزد را برمی‌گزیند و تراکنش را با اثبات‌های تهی می‌سازد، call را در برابر پیوند call اثبات می‌کند، آن اثبات را جای می‌دهد و تنها سپس پیوند تراکنش را می‌گیرد و بسته را اثبات می‌کند.

پیامد برای کلاینت: کل تراکنش — خروجی‌ها، پاکت‌ها، کنش — را پیش از اثبات بسازید. کیف پولی که پیش از فورک ساخته شده باشد در برابر بخش عمومی تهی اثبات می‌کند و رد می‌شود. کنش‌های بدون بسته اثباتی برای پیوند ندارند و به امضاهای خود تکیه می‌کنند. پیوند همان شناسهٔ تراکنش نیست، و شناسه بدون تغییر است.

شناسهٔ تراکنش

txid = blake3("rand-txid-3" || bincode(view))

که در آن view همان تراکنش است با این تفاوت که بایت‌های proof بسته با blake3(proof) (proof_hash) و بایت‌های auth_proof آن با blake3(auth_proof) (auth_proof_hash، پس از auth_commit که همان‌طور که هست وارد می‌شود) جایگزین شده‌اند و هر فیلد دیگر عیناً حفظ می‌شود. دامنه تا زنجیرهٔ ۱۶ rand-txid-2 و بدون دو فیلد احراز هویت بود؛ با زنجیرهٔ ۱۷ شناسهٔ همهٔ تراکنش‌ها تغییر کرد. هش‌کردن اثبات از طریق چکیده باعث می‌شود بستهٔ هرس‌شده (تجمیع بلوک، که در زنجیرهٔ ۲۰ فعال نیست) همان شناسهٔ شکل خام خود را نگه دارد؛ بستهٔ هرس‌شده اثبات احراز هویت خود را نگه می‌دارد. تراکنش بدون بسته بایت‌هایش را همان‌گونه که هستند هش می‌کند. همین شناسه کلید ممپول، رسیدها و هر جست‌وجوی RPC است.

همهٔ کنش‌ها

Action یک enum از نوع bincode است و برچسب‌ها به ترتیب اعلان آمده‌اند:

برچسبکنشفیلدهابستهحداقل کارمزد
0None—الزامیBUNDLE_BASE = 0.001 RAND
1Mintcm, pk, time, r, envelope, amount, minter: PublicKey, signatureباید غایب باشد0 (با امضای validator؛ فقط زنجیره‌های faucet، amount ≤ 100 RAND)
2Deploybase_pc: u32, words: Vec<u32>, public: Vec<u32>الزامیپایه + ۱۰۰٬۰۰۰ واحد به‌ازای هر کلمهٔ برنامه و هر کلمهٔ عمومی
3Callprogram, proof, input_envelope: Option<CallEnvelope>الزامیدر chain 20، پایه + gas_price × GAS_LIMIT اعلام‌شدهٔ اثبات + byte_price به‌ازای هر KiB از اثبات و پاکت ورودی؛ در زنجیره‌ای بدون بخش gas، پایه + call_fee(tier)
4Bondvalidator, amount, registration: Option<Registration>الزامی، و burn_r == amountپایه
5Unbondvalidator, amount, nonce, signatureباید غایب باشد0
6Withdrawvalidator, amount, nonce, time, r, envelope, signatureباید غایب باشد0 در شبکه؛ پایه از amount کسر می‌شود
7BridgeAttestattestation, recipient: ShieldedAddress, r, time, asset, envelope, pq_signatures: Vec<PqSignature>الزامیپایه
8BridgeBurnasset, amount, relayer_fee, to_chain: u16, token: [u8; 32], to: [u8; 32]الزامی، با burn_asset == asset و burn_a == amountBRIDGE_BURN_FEE = 0.01 RAND
9RegisterAggregatorregistrationالزامی، burn_r = وثیقهپایه
10UnbondAggregatoraggregator, nonce, signatureباید غایب باشد0
11WithdrawAggregatoraggregator, nonce, time, r, envelope, signatureباید غایب باشد0
12SlashAggregatorدو سرایند تجمیعِ امضاشدهباید غایب باشد0
13Aggregatecovers, proof, aggregator, nonce, time, r, envelope, signatureباید غایب باشد0
14RegisterTokenname, symbol, decimals: u8, authority: MintAuthority, initial: Option<InitialMint>, salt: [u8; 32], index: u32الزامیپایه + registration_fee دفتر ثبت (۱ RAND در chain 20، سوزانده می‌شود)
15TokenMintasset, amount, recipient: ShieldedAddress, r, time, envelope, nonce, signatureالزامیپایه
16SetAuthorityasset, new: Option<PublicKey>, nonce, signatureالزامیپایه
17TokenBurnasset, amountالزامی، با burn_asset == asset و burn_a == amountپایه
18PauseMintsnonce, signatureباید غایب باشد0
19UnpauseMintsnonce, pq_signaturesباید غایب باشد0
20RegisterBridgedTokenname, symbol, salt, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signaturesالزامیپایه + registration_fee
21ListBackingtoken_index, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signaturesالزامیپایه
22RotatePqGuardiansnew_pq_guardians: Vec<PublicKey>, nonce, pq_signaturesباید غایب باشد0
23RotatePauseKeynew_pause_key: PublicKey, nonce, pq_signaturesباید غایب باشد0
24ClaimVestedentry: [u8; 32], amount, nonce, to: ShieldedAddress, time, r, envelope, signatureباید غایب باشد0 در شبکه؛ پایه از amount کسر می‌شود
25RevokeVestingentry: [u8; 32], unvested, nonce, to: ShieldedAddress, time, r, envelope, signatures (از v0.6.8 آستانه‌ای از ابطال‌کنندگان آن ورودی، که به خزانه‌ای که ورودی genesis آن نام برده پرداخت می‌کند؛ پیش‌تر یک signature)باید غایب باشد0 در شبکه؛ پایه از unvested کسر می‌شود
26BondVestedentry: [u8; 32], validator, amount, registration: Option<Registration>, nonce, signatureباید غایب باشد0
27UnbondVestedentry: [u8; 32], amount, nonce, signatureباید غایب باشد0
28AdmitValidatorcandidate: PublicKey, signatures: Vec<(PublicKey, Signature)>باید غایب باشد0
29SlashEquivocationfirst, second: دو سرایند بلوکِ امضاشده از یک leader برای یک viewباید غایب باشد0
30RotatePqGuardiansV2new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>, nonce, pq_signaturesباید غایب باشد0
31RotatePauseKeyV2new_pause_key: PublicKey, possession: Vec<u8>, nonce, pq_signaturesباید غایب باشد0
32CancelRotationkind: u8, nonce, signatureباید غایب باشد0
33Invokeprogram, proof, input_envelope: Option<CallEnvelope>, transition: Transitionالزامی؛ burn_r و burn_a از burn_asset همان چیزی است که وارد برنامه می‌شودحداقل کارمزد یک call + cell_fee به‌ازای هر سلول ایجاد‌شده
MintAuthority = None | Key(PublicKey) | Bridge { backings: Vec<Backing> } | Program(Hash)
InitialMint   { amount: u64, recipient: ShieldedAddress, r: Word8, time: u32, envelope: Envelope }
PqSignature   { index: u8, signature: Vec<u8> }   // one guardian's Dilithium2 co-signature, by set index

در chain 20، برچسب‌های ۰ تا ۸، ۱۴ تا ۲۱، ۲۸ و ۳۰ تا ۳۳ قابل استفاده‌اند. genesis آن دارای بخش bridge با rules_v2، بخش tokens، staking.admission_by_vote (برچسب ۲۸)، bridge.rotation (برچسب‌های ۳۰ تا ۳۲) و program_state (برچسب ۳۳) است. برچسب‌های ۲۲ و ۲۳ در آن رد می‌شوند: تحت bridge.rotation.needs_possession یک چرخش باید به شکل …V2 باشد که امضای خودِ هر کلید جدید را حمل می‌کند. در chain 19، که هیچ‌یک از این بخش‌ها را نداشت، برچسب‌های ۰ تا ۸ و ۱۴ تا ۲۳ قابل استفاده بودند (chain 14، بدون rules_v2، برچسب‌های ۲۲ و ۲۳ را رد می‌کرد). SlashEquivocation به بخش staking.slashing نیاز دارد و بخش chain 20 تهی است. پنج کنش تجمیع به بخش aggregation نیاز دارند که chain 20 ندارد (یک build از شروع به کار روی genesisی که آن را داشته باشد خودداری می‌کند، تا زمانی که تجمیع دوباره اندازه‌گیری شود). یک node همهٔ این‌ها را در دروازه، پیش از هر بررسی دیگر، رد می‌کند، اما آن‌ها همچنان برچسب‌های bincode خود را اشغال می‌کنند؛ بنابراین رمزگذاری که شمارهٔ برچسب‌ها را مستقیم در کد می‌نویسد باید آن‌ها را بشمارد. تجمیع به‌عنوان قابلیتی آینده در اثبات‌ها مستند شده است. چهار کنش vesting در genesis (برچسب‌های ۲۴ تا ۲۷، از v0.5.11 در build) به بخش vesting نیاز دارند که هیچ زنجیره‌ای، از جمله chain 20، نداشته است، و بدون آن هنگام پذیرش رد می‌شوند. برچسب‌های ۲۸ تا ۳۳ در v0.6.8 جدیدند و در انتها افزوده شده‌اند، تا تاریخچهٔ هر زنجیرهٔ پیشین همچنان رمزگشایی شود.

چهار کنش RPL (برچسب‌های ۱۴ تا ۱۷) یک توکن را ثبت می‌کنند، از توکنی با اختیار Key مقدار بیشتری mint می‌کنند، اختیار mint آن را واگذار یا رها می‌کنند، و یک دارایی را می‌سوزانند؛ RegisterToken که اختیار Bridge یا Program را نام ببرد رد می‌شود. کنش انتقال توکن وجود ندارد: انتقال توکن همان None است و از پرداخت RAND قابل تشخیص نیست. نگاه کنید به توکن‌های RPL. چهار کنش حاکمیت پل (برچسب‌های ۱۸ تا ۲۱) عبارت‌اند از توقف mint، رفع آن، و فهرست‌کردن یک توکن پل‌شده یا یک پشتوانهٔ دیگر پس از genesis. PauseMints را کلید توقفِ genesis امضا می‌کند که فقط می‌تواند متوقف کند؛ سه کنش دیگر به حدنصابی از نگهبانان Dilithium2 (PQ) نیاز دارند. یک BridgeAttest نیز چنین حدنصابی را در کنار حدنصاب ECDSA خود حمل می‌کند. برچسب‌های ۲۲ و ۲۳، تحت rules_v2 پل، مجموعهٔ نگهبانان PQ یا کلید توقف را جایگزین می‌کنند، هرکدام تحت حدنصاب PQ مجموعهٔ فعلی و rotation_nonce پل. برچسب‌های ۳۰ و ۳۱ همین کار را تحت bridge.rotation انجام می‌دهند و افزون بر آن امضای Dilithium2 خودِ هر کلید جدید را بر پیام چرخش حمل می‌کنند، که اثباتی است بر اینکه کلید در اختیار دارندهٔ آن است؛ در chain 20 یک چرخش به‌صورت در انتظار ثبت می‌شود و پس از delay_secs، یعنی 86,400 s، اعمال می‌شود. CancelRotation (برچسب ۳۲)، که کلید توقف فعلی آن را امضا می‌کند، چرخشی را که در انتظار است پیش از اعمال حذف می‌کند. نگاه کنید به پل.

AdmitValidator (برچسب ۲۸) رأی مجموعهٔ validatorها برای پذیرش یک کلید جدید است: امضاهایی از validatorهایی که بیش از دو سوم سهام را در اختیار دارند، به ترتیب صعودی آدرس. تحت staking.admission_by_vote (chain 20)، یک Bond که بخواهد validator جدیدی ثبت کند رد می‌شود، مگر اینکه آدرس آن از این راه پذیرفته شده باشد (سهام‌گذاری). Invoke (برچسب ۳۳، RPL-2) یک call است که اثبات آن یک transition اعلام‌شدهٔ وضعیتِ برنامه را تضمین می‌کند: سلول‌های خوانده و نوشته‌شده، آنچه فیلدهای سوزاندن بسته به خزانهٔ برنامه می‌آورند، و یادداشت‌هایی که خزانه می‌پردازد یا برنامه mint می‌کند. دفتر کل transition را اعمال می‌کند اگر هر خواندنِ اعلام‌شده همچنان برقرار باشد و هر دو اثبات تأیید شوند (برنامه‌ها).

یک Deploy و یک Call تراکنش‌های محافظت‌شدهٔ معمولی‌اند که بستهٔ آن‌ها خودانتقالِ صفر است: وجودش برای آن است که حداقل کارمزد از یادداشت‌های خودِ فراخواننده پرداخت شود، تا زنجیره هرگز نفهمد این یادداشت‌ها از آنِ چه کسی بوده‌اند. RAND فقط از طریق burn_r از استخر خارج می‌شود — سهمِ یک Bond، وثیقهٔ یک RegisterAggregator، سپردهٔ یک Invoke به خزانهٔ برنامه — و یک توکن فقط از طریق burn_a با burn_asset که آن را نام ببرد، در یک TokenBurn، یک BridgeBurn یا یک Invoke. هر کنش دیگری چیزی نمی‌سوزاند، و بسته‌ای با burn_asset = 0 و burn_a ناصفر در همه‌جا رد می‌شود.

ترتیب پذیرش

Ledger::validate_inner بررسی‌های ارزان را پیش از گران انجام می‌دهد، به این ترتیب؛ mempool پیش از انتشار همین بررسی را اجرا می‌کند، بنابراین یک تراکنش نامعتبر فقط یک بار، در لبه، رد می‌شود.

۱. سقف اندازه — کل تراکنش ≤ max_block_bytes زنجیره؛ هر یک از چهار envelope بسته (bundle) و هر envelope یادداشتی که اکشن حمل می‌کند، دقیقاً برابر envelope_bytes در genesis (در chain 20 برابر 1,860؛ در زنجیره‌ای بدون این فیلد حداکثر 2,048 بایت)؛ اثبات bundle، اثبات auth و اثبات call هر کدام ≤ max_proof_bytes (به‌طور پیش‌فرض 2 MiB، در genesis زنجیره chain 20 برابر 4 MiB؛ در زنجیره‌های 13 تا 16 برابر 8 MiB بود)؛ یک برنامه ≤ سقف برنامه در آن زنجیره (به‌طور پیش‌فرض 4,096 کلمه، در genesis زنجیره chain 20 برابر 65,535)؛ یک envelope فراخوانی ≤ max_call_envelope_bytes (به‌طور پیش‌فرض 18,432 بایت، در chain 20 برابر 65,536)؛ یک attestation ≤ 16 KiB. اثبات bundle به شکل نشان‌گر مُهرشده (هرس‌شده) در اینجا، خارج از همگام‌سازی به شکل مُهرشده، رد می‌شود (PrunedFormOutsideSync)، پیش از هر کار مربوط به اثبات. ۲. شناسه زنجیره مطابقت دارد. ۳. ساختار و کف کارمزد — اکشنِ بدون bundle هیچ bundle‌ای حمل نمی‌کند و هر اکشن دیگر باید دقیقاً یکی حمل کند؛ check_burn_shape تعیین می‌کند اکشن مجاز است کدام‌یک از سه فیلد سوزاندن را مقدار دهد: TokenBurn یا BridgeBurn دارایی asset و مقدار amount خودش را از طریق burn_asset و burn_a می‌سوزاند، Bond یا RegisterAggregator مقدارش را از طریق burn_r می‌سوزاند، و هر اکشن دیگری چیزی نمی‌سوزاند؛ fee ≥ fee_floor(action). در زنجیره‌ای با بخش gas (chain 20) کف یک Call یا Invoke در این گام همان قاعده gas در سقف اعلام‌شده 1 است، یعنی BUNDLE_BASE + gas_price + byte_price × ⌈bytes / 1024⌉ بر پایه اثبات و envelope ورودی آن، که پیش از هر راستی‌آزمایی معلوم‌اند. ۴. لنگر (anchor) — یکی از 1,024 ریشه پایان بلوک آخر در chain 20، که genesis آن proof_window_blocks: 1024 را تعیین می‌کند (v0.6.8)؛ یکی از 256 ریشه آخر در زنجیره‌ای بدون این فیلد، مانند تا chain 19. ۵. زمان — در بازه [height − 1024, height] در chain 20؛ [height − 256, height] بدون این فیلد. همان یک مقدار genesis هر دو پنجره را تعیین می‌کند. ۶. nullifierها و commitmentها — هر چهار nullifier با هم متفاوت‌اند و هیچ‌کدام خرج نشده است؛ هر چهار commitment با هم متفاوت‌اند و هیچ‌کدام از پیش برگ نیست. ۷. بررسی‌های اکشن — faucet فعال است، mint زیر سقف است و اعتبارسنج آن را امضا کرده است، و در chain 20 گیرنده‌ای را نام می‌برد که در فهرست مجاز staking.faucet_recipients در genesis است، در faucet_budget_per_epoch همان epoch می‌گنجد و اعتبارسنجی از فهرست staking.faucet_minters در genesis آن را امضا کرده است (قاعده اعتبار از chain 16)؛ mint یا واریز برابر یا بیشتر از 2⁶³ در هر زنجیره‌ای رد می‌شود؛ برنامه رمزگشایی می‌شود؛ برنامه وجود دارد؛ قواعد staking (ثبت‌نام دقیقاً هنگامی حاضر است که اعتبارسنج ناشناس است، nonce، امضا، سهام کافی)؛ قواعد خود پل برای BridgeAttest و BridgeBurn و برای اکشن‌های توقف و فهرست‌کردن؛ قواعد RPL برای چهار اکشنش، از جمله کارمزد ثبت‌نام؛ در chain 20 حدنصاب رأی پذیرش، و برای Invoke ساختار گذار، خواندن‌های اعلام‌شده در برابر سلول‌های ذخیره‌شده، پوشش خزانه برای هر پرداخت و کارمزد سلول. همه این‌ها پیش از اثبات bundle اجرا می‌شود، بنابراین تراکنشی که نمی‌تواند اعمال شود هزینه‌ای برای راستی‌آزمایی STARK ندارد. تحت hardening_v6 (زنجیره‌های 16 تا 20) دو رد دیگر اینجا قرار دارند: Deploy برنامه‌ای که هیچ call‌ای نمی‌تواند آن را نگه دارد (ProgramUncallable)، و اثبات bundle یا call که سرآیند یا transcript آن فیلدی حمل می‌کند که prover درستکار نمی‌نوشت (non-canonical proof: …)، پیش از راستی‌آزمایی هر دو اثبات رد می‌شود. از v0.6.8 اثباتی که سرآیندش درجه‌ای فراتر از سقف بلندترین جدول اعلام می‌کند، پیش از همه و با همان حکم رد می‌شود (audit v6 ZKV-4)، پیش از آنکه بررسی آن را پیمایش کند. ۸. digest بسته — در زنجیره‌ای با مجوزدهی جدا (هر زنجیره از 17 به بعد) فیلدهای auth ابتدا بررسی می‌شوند: bundle بدون اثبات auth رد می‌شود، c که اثبات auth منتشر می‌کند (خوانده می‌شود، هنوز راستی‌آزمایی نشده) باید برابر auth_commit باشد، و در chain 20 اثبات auth باید GAS_LIMIT را دقیقاً 1,279 اعلام کند. سپس دفتر کل digest فیلدهای منتشرشده bundle را، که auth_commit یکی از آن‌هاست، دوباره محاسبه می‌کند و باید با آنچه اثبات منتشر کرده برابر باشد. در chain 20 اثبات bundle نیز باید GAS_LIMIT را دقیقاً برابر bundle_gas_limit در genesis، یعنی 20,479، اعلام کند. ۹. اثبات bundle — Machine::verify_public در برابر hc_bundle ثابت‌شده در genesis و binding تراکنش. در زنجیره‌ای با مجوزدهی جدا، اثبات auth بعد از آن، در برابر hc_auth ثابت‌شده در genesis و همان binding راستی‌آزمایی می‌شود. ۱۰. اثبات call و کارمزد tier آن — برای Call یا Invoke، ابتدا سرآیند اثبات پین می‌شود (tier حداکثر 14، جدول keccak حداکثر 2¹²، جدول sha256 حداکثر 2¹³، ارتفاع برنامه مستقرشده، که تحت hardening_v6 حداقل به 2⁷ سطر می‌رسد، و در chain 20 GAS_LIMIT اعلام‌شده‌ای که از سقف سرآیند یعنی gas_max بیشتر نباشد)، سپس STARK مربوط به call در برابر code_hash برنامه و، تحت hardening_v6، binding فراخوانی تراکنش. کارمزد آخر بررسی می‌شود، چون فقط پس از رمزگشایی اثبات قابل دانستن است: در chain 20 fee ≥ BUNDLE_BASE + gas_price × GAS_LIMIT + byte_price × ⌈bytes / 1024⌉ به قیمت‌های جاری، به‌علاوه cell_fee برای هر سلول ساخته‌شده در Invoke (کارمزدها)؛ در زنجیره‌ای بدون بخش gas برابر fee ≥ BUNDLE_BASE + call_fee(tier).

خطاها از rand_sendTransaction با کد -32000 و دلیل برمی‌گردند: nullifier already spent، anchor is not one of the last 1024 roots (256 در زنجیره‌ای بدون proof_window_blocks)، time 12 is outside [476, 1500]، fee 1000000 below minimum 2000000، the bundle's digest is not what its proof published، invalid bundle proof: …، unknown program …، already in mempool، conflicts with a pending transaction over <nullifier>، faucet is disabled on this chain، و از chain 14 the bundle burns asset 2 on an action that burns no token، a burn of 5 is not allowed on this action، the bundle burns 399, not the 400 the action declares، wrong mint nonce: expected 1, got 0، wrong token index: expected 3, got 2، از chain 17 this chain requires an auth proof on every bundle (genesis hc_auth) و the auth proof publishes a different commitment than the bundle's auth_commit، و در chain 20 envelope is 1348 bytes, this chain requires exactly 1860 و bundle proof declares gas limit …, this chain requires exactly 20479. پذیرش به معنای نهایی‌شدن نیست: rand_getTransaction را پیوسته فراخوانی کنید تا بلوکی برگرداند.

بلوکی که شامل هر تراکنش نامعتبر باشد نامعتبر است. mempool هر nullifier و commitment در انتظار را نمایه می‌کند و هر تعارض را بی‌درنگ رد می‌کند، بنابراین دو bundle که یک یادداشت را خرج می‌کنند هرگز در یک بلوک نمی‌آیند.

اندازه‌ها

بخشبایت
Word832
یک Envelope1,892 رمزگذاری‌شده (1,860 از آن متن رمز) از chain 18؛ 1,380 (1,348) تا chain 17
Bundle منهای دو اثباتش7,936 (چهار nullifier، چهار commitment، چهار envelope، auth_commit)
تراکنش انتقال منهای دو اثباتش7,949
تراکنش deploy منهای دو اثباتش، برنامه 100 کلمه‌ای، بدون ورودی عمومی8,369
تراکنش call منهای سه اثباتش، بدون envelope ورودی7,990
تراکنش mint (بدون bundle)5,761 (یک کلید Dilithium2 به اندازه 1,312 بایت و یک امضای 2,420 بایتی)
یک اثبات bundle، tier 14، پروفایل تولید1,497,544 و 1,504,260 خوانده‌شده در chain 18 (یک deploy در ارتفاع 97,821 و یک call در 98,335، 2026-09-30)؛ 1,422,116 و 1,423,170 اندازه‌گیری‌شده در chain 14 (ثبت zUSD و فهرست‌شدن یک backing، 2026-09-20)
یک اثبات auth، tier 10، پروفایل تولید1,370,827 و 1,367,303 در همان دو تراکنش chain 18

شش ردیف اول رمزگذاری از chain 18 به بعد است: فیلد memo به هر envelope 512 بایت افزود، و مجوزدهی جدا پیش از بایت‌های خود اثبات auth 40 بایت به bundle افزود. بنابراین یک انتقال shielded روی شبکه از chain 18 به بعد، از جمله chain 20، حدود 2.88 MB است، که تقریباً همه آن اثبات است، صرف‌نظر از اینکه کدام دارایی را جابه‌جا کند: حدود 1.5 MB اثبات bundle و 1.37 MB اثبات auth. تا chain 15 حدود 1.43 MB و با یک اثبات بود. بلوک‌ها به 2,000 تراکنش (MAX_BLOCK_TXS) و به max_block_bytes زنجیره برای بایت‌های تراکنش محدودند، که هر دو قاعده اجماع‌اند: به‌طور پیش‌فرض 4 MiB (MAX_BLOCK_BYTES)، که یک انتقال از این دست را در خود جا می‌دهد، و 20 MiB در genesis زنجیره chain 20، که جای هفت انتقال دارد. genesis دارای hc_auth باید max_block_bytes را دست‌کم سه برابر max_proof_bytes به‌علاوه 1 MiB تعیین کند، چون یک تراکنش می‌تواند سه اثبات (bundle، auth، call) حمل کند؛ از این رو پیش‌فرض 4 MiB آنجا رد می‌شود. تجمیع در سطح بلوک راه‌حل در صف است؛ اثبات‌ها را ببینید.

خواندن یک تراکنش

rand_getTransaction(hash) تا زمانی که تراکنش نهایی شود null برمی‌گرداند، سپس جایگاه آن در بلوک و فقط فیلدهای عمومی را:

{
  "height": 192, "index": 0, "block_hash": "63f6…08",
  "tx": {
    "hash": "4f2c…e7", "chain_id": 20,
    "bundle": {
      "anchor": "6b1d…c4",
      "nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"],
      "commitments": ["2a9f…07", "b310…88", "77c1…0e", "5d20…b3"],
      "fee": "1000000", "burn_a": "0", "burn_r": "0", "burn_asset": 0, "time": 5,
      "proof_len": 1497544, "envelope_len": [1860, 1860, 1860, 1860],
      "auth_commit": "0bab…e8a1", "auth_proof_bytes": 1370827
    },
    "action": { "kind": "none" }
  }
}

در یک انتقال هیچ فیلد from، to، nonce، مقدار (amount) یا asset وجود ندارد و در بلاک ذخیره‌شده نیز چیزی از این‌ها نیست: گره چیزی برای پنهان‌سازی بیشتر ندارد. auth_commit و auth_proof_bytes (طول اثبات احراز) از chain 17 در آن حضور دارند. بسته همیشه چهار جایگاه دارد، از جمله جایگاه‌های ساختگی، و انتقال RAND و انتقال هر توکن RPL هر دو "kind": "none" هستند. هر مقدار u64 از chain 14 به‌صورت یک رشته‌ی دهدهی است (fee، burn_a، burn_r و amount هر کنش)؛ اندیس‌ها، nonceها، ارتفاع‌ها و طول‌ها همچنان عدد JSON می‌مانند. انواع دیگر کنش، فیلدهای عمومی خود را نمایش می‌دهند — mint، deploy، call، bond، unbond، withdraw، bridge_attest، bridge_burn، register_token، token_mint، set_authority، token_burn، pause_mints، unpause_mints، register_bridged_token، list_backing، rotate_pq_guardians، rotate_pause_key، چهار نوع vesting یعنی claim_vested، revoke_vesting، bond_vested و unbond_vested، و از v0.6.8 admit_validator، slash_equivocation، rotate_pq_guardians_v2، rotate_pause_key_v2، cancel_rotation و invoke — همان‌طور که در مرجع JSON-RPC فهرست شده‌اند.

آخرین بار با کامیت c0878c3f از fullnode (زنجیرهٔ 21) در تاریخ 2026-10-12 بررسی شد. هرجا کد و این صفحه با هم اختلاف دارند، حق با کد است؛ به ما اطلاع دهید.