इस पृष्ठ का उपयोग किसी लेनदेन को बनाने, सबमिट करने या डिकोड करने के लिए करें। इसमें बताया गया है कि rand_sendTransaction लेनदेन को किस वायर रूप में लेता है, id की गणना कैसे होती है, सभी एक्शन की पूरी सूची उनके बंडल और शुल्क नियमों के साथ क्या है, और स्वीकृति का वह क्रम क्या है जिससे तय होता है कि आपको सबसे पहले कौन-सी त्रुटि मिलेगी।
लेनदेन
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 आठ little-endian u32 शब्द हैं, जिन्हें 64 लोअरकेस हेक्स वर्णों के रूप में दिखाया जाता है। वायर रूप bincode(Transaction) है, जिसमें bincode के डिफ़ॉल्ट लागू होते हैं: नियत-चौड़ाई वाले पूर्णांक, 8-बाइट लंबाई उपसर्ग और u32 enum टैग। rand_sendTransaction इसे हेक्स के रूप में लेता है। लेनदेन पर कोई हस्ताक्षर नहीं होता और कोई प्रेषक फ़ील्ड नहीं होता। बंडल अपने प्रूफ़ से स्वयं को अधिकृत करता है: बंडल प्रूफ़ से और, चेन 17 से, उसके साथ रखे auth प्रूफ़ से। जिस एक्शन पर हस्ताक्षर होता है, वह हस्ताक्षर उसी एक्शन के भीतर रहता है। रीप्ले से सुरक्षा इस प्रकार होती है: बंडलों के लिए न्यूलिफ़ायर सेट, हस्ताक्षरित स्टेकिंग एक्शन के लिए रजिस्टर में हर वैलिडेटर का अपना nonce, TokenMint और SetAuthority के लिए हर टोकन का अपना mint_nonce, और ब्रिज के गवर्नेंस एक्शन के लिए ब्रिज के pause_nonce और list_nonce।
एक लेनदेन में अधिकतम एक बंडल होता है, और हर वह एक्शन जो बंडल-रहित नहीं है, ठीक एक बंडल रखता है। बंडल के स्लॉट 0–1 एक निजी एसेट रखते हैं और स्लॉट 2–3 RAND। इस तरह एक ही प्रूफ़ टोकन को स्थानांतरित करता है और उसका RAND शुल्क भी चुकाता है। पिछले डिज़ाइन का दो-बंडल वाला ब्रिज बर्न अब नहीं है: एक बर्न एक ही बंडल है (नोट)।
rand वॉलेट यह सब randprotocol_client::wallet::{send, submit} के माध्यम से बनाता है। क्रम यह है: इनपुट चुनना, एंकर प्राप्त करना, ट्री की वॉलेट की अपनी प्रति से विटनेस की गणना करना, आउटपुट बनाकर चारों एन्वेलप सील करना, लेनदेन की बाइंडिंग लेना, बंडल और auth प्रूफ़ सिद्ध करना, बंडल प्रूफ़ का प्रकाशित डाइजेस्ट जाँचना, और सबमिट करना।
लेनदेन की बाइंडिंग
लेनदेन पर कोई हस्ताक्षर नहीं होता, और बंडल का प्रूफ़ केवल बंडल के अपने डाइजेस्ट के प्रति प्रतिबद्ध होता है: एंकर, न्यूलिफ़ायर, कमिटमेंट, शुल्क, बर्न फ़ील्ड और time। चेन 13 तक कोई भी चीज़ सिद्ध किए गए बंडल को उसके साथ जाने वाले एक्शन या उसके एन्वेलप से नहीं जोड़ती थी। इसलिए लेनदेन देखने वाला कोई भी व्यक्ति प्रूफ़ का हर बाइट रखकर बाकी सब बदल सकता था: किसी BridgeBurn का गंतव्य, किसी Bond का वैलिडेटर, या कोई एन्वेलप। चेन 14 से हर बंडल प्रूफ़ लेनदेन की बाइंडिंग पर बनाया जाता है और उसी के विरुद्ध सत्यापित किया जाता है:
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, चेन 17 से bundle.auth_proof, और किसी Aggregate का proof। बाकी सब इसमें शामिल रहता है: auth_commit, चारों एन्वेलप, हर बर्न फ़ील्ड, गंतव्य, वैलिडेटर, प्राप्तकर्ता, हस्ताक्षर, गार्जियन अटेस्टेशन और उसके Dilithium2 सह-हस्ताक्षर, और किसी Call का अपना प्रूफ़। ये आठ शब्द प्रूफ़ के पब्लिक इनपुट खंड में जाते हैं, जिसके डाइजेस्ट H_PUB को सर्किट पहले से बाँधता है। इसलिए इस सुधार के लिए गेस्ट या सर्किट में कोई बदलाव नहीं करना पड़ा। नोड Machine::verify_public(hc_bundle, binding, proof) चलाता है और ऐसे बंडल प्रूफ़ को अस्वीकार कर देता है जो कोई दूसरी पब्लिक ऊँचाई घोषित करता है (public height not the transaction binding's)। Transaction::binding वही एक फ़ंक्शन है जिसे वॉलेट सिद्ध करने से पहले और लेजर सत्यापन से पहले कॉल करते हैं। चेन 17 से auth प्रूफ़ भी इन्हीं आठ शब्दों पर बनाया और सत्यापित किया जाता है, इसलिए उसे भी किसी दूसरे लेनदेन पर नहीं ले जाया जा सकता।
चेन 20 जेनेसिस हैश को बाँधती है। चेन 19 तक बाइंडिंग और बंडल-रहित एक्शन के हस्ताक्षरित संदेश केवल चेन id को कवर करते थे। इसलिए एक ही चेन id वाली दो चेनें (कोई री-कट या कोई निजी प्रति) एक-दूसरे के प्रूफ़ और हस्ताक्षर स्वीकार कर लेतीं। चेन 20 का जेनेसिस binding_domain: 1 सेट करता है (v0.6.8, audit 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), एग्रीगेटर एक्शन और ब्रिज के गवर्नेंस संदेशों पर भी लागू होता है। ऐसी चेन पर वैलिडेटर पंजीकरण हमेशा v2 संदेश होता है (जेनेसिस, चेन, पता, भुगतान)। एक चेन के लिए सिद्ध या हस्ताक्षरित कुछ भी दूसरी चेन पर सत्यापित नहीं होता, चाहे उसकी चेन id कुछ भी हो। बाइंडिंग शब्द पब्लिक इनपुट हैं, इसलिए कोई गेस्ट या वेरिफ़ायर कुंजी नहीं बदली। v0.6.8 से पुराना वॉलेट अब भी चेन-id वाले रूप के विरुद्ध सिद्ध करता है और चेन 20 पर अस्वीकार कर दिया जाता है। rand_getLimits और rand_status binding_domain लौटाते हैं (चेन 20 पर 1, फ़ील्ड न होने पर 0)।
जेनेसिस hardening_v6 (चेन 16 से 20) के तहत किसी Call का अपना प्रूफ़ भी बाँधा जाता है। इसके लिए उसी संरचना का दूसरा डाइजेस्ट उसके अपने डोमेन के तहत बनता है, जिसमें बंडल के प्रूफ़ के साथ-साथ कॉल प्रूफ़ भी खाली कर दिया जाता है:
call_binding(tx) = blake3("rand-call-bind-1" || bincode(chain_id, bundle', action'')) as 8 LE u32 words
चेन 20 पर टैग rand-call-bind-2 है और ऊपर बताए अनुसार जेनेसिस हैश प्रीइमेज की शुरुआत में आता है। किसी Invoke का प्रूफ़ भी इसी तरह बाँधा जाता है, और उसका घोषित ट्रांज़िशन प्रीइमेज में रखा जाता है। इस तरह प्रूफ़ ठीक उसी स्थिति-परिवर्तन, भुगतानों और प्राप्तकर्ताओं के प्रति प्रतिबद्ध होता है जो लेनदेन में हैं।
कॉल प्रूफ़ इन शब्दों को अपने पब्लिक इनपुट खंड में रखता है। यदि प्रोग्राम का डिप्लॉय-समय का पब्लिक इनपुट है, तो ये शब्द उसके बाद आते हैं। इसलिए किसी दूसरे शुल्क बंडल से जोड़ी गई इसकी प्रति सत्यापित नहीं होती। दोनों बाइंडिंग एक-दूसरे के भीतर आती हैं। वॉलेट पहले शुल्क बंडल के नोट चुनता है और खाली प्रूफ़ के साथ लेनदेन बनाता है। फिर वह कॉल बाइंडिंग के विरुद्ध कॉल को सिद्ध करता है और वह प्रूफ़ लेनदेन में रखता है। इसके बाद ही वह लेनदेन की बाइंडिंग लेकर बंडल को सिद्ध करता है।
क्लाइंट के लिए इसका परिणाम यह है कि पूरा लेनदेन, यानी आउटपुट, एन्वेलप और एक्शन, सिद्ध करने से पहले बनाना होगा। फ़ोर्क से पहले बना वॉलेट खाली पब्लिक खंड के विरुद्ध सिद्ध करता है और अस्वीकार कर दिया जाता है। बंडल-रहित एक्शन के पास बाँधने के लिए कोई प्रूफ़ नहीं होता, इसलिए वे अपने हस्ताक्षरों पर निर्भर रहते हैं। बाइंडिंग लेनदेन id नहीं है, और लेनदेन id अपरिवर्तित रहती है।
लेनदेन id
txid = blake3("rand-txid-3" || bincode(view))
यहाँ view वह लेनदेन है जिसमें बंडल के proof बाइट को blake3(proof) (proof_hash) से और उसके auth_proof बाइट को blake3(auth_proof) (auth_proof_hash) से बदल दिया जाता है। auth_proof_hash, auth_commit के बाद आता है, और auth_commit जैसा है वैसा ही शामिल होता है। बाकी हर फ़ील्ड ज्यों का त्यों रहता है। चेन 16 तक डोमेन rand-txid-2 था और उसमें दोनों auth फ़ील्ड नहीं थे। चेन 17 के साथ हर लेनदेन id बदल गई। प्रूफ़ को उसके डाइजेस्ट से हैश करने के कारण ही प्रून किया गया बंडल अपने मूल रूप वाली id बनाए रखता है (ब्लॉक एग्रीगेशन, जो चेन 20 पर सक्रिय नहीं है)। प्रून किया गया बंडल अपना auth प्रूफ़ बनाए रखता है। बंडल-रहित लेनदेन अपने बाइट को जैसे हैं वैसे ही हैश करता है। यही id मेमपूल, रसीदों और हर RPC लुकअप की कुंजी है।
हर एक्शन
Action एक bincode enum है, और उसके टैग घोषणा के क्रम में हैं:
| टैग | क्रिया | फ़ील्ड | बंडल | न्यूनतम शुल्क |
|---|---|---|---|---|
| 0 | None | — | आवश्यक | BUNDLE_BASE = 0.001 RAND |
| 1 | Mint | cm, pk, time, r, envelope, amount, minter: PublicKey, signature | अनुपस्थित होना चाहिए | 0 (वैलिडेटर द्वारा हस्ताक्षरित; केवल फ़ॉसेट चेन पर, राशि ≤ 100 RAND) |
| 2 | Deploy | base_pc: u32, words: Vec<u32>, public: Vec<u32> | आवश्यक | आधार + प्रोग्राम के प्रत्येक शब्द और प्रत्येक सार्वजनिक शब्द पर 100,000 इकाइयाँ |
| 3 | Call | program, proof, input_envelope: Option<CallEnvelope> | आवश्यक | चेन 20 पर, आधार + gas_price × प्रूफ़ का घोषित GAS_LIMIT + प्रूफ़ और इनपुट एनवेलप के प्रति KiB byte_price; gas खंड से रहित चेन पर आधार + call_fee(tier) |
| 4 | Bond | validator, amount, registration: Option<Registration> | आवश्यक, और burn_r == amount | आधार |
| 5 | Unbond | validator, amount, nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 6 | Withdraw | validator, amount, nonce, time, r, envelope, signature | अनुपस्थित होना चाहिए | वायर पर 0; आधार amount में से काटा जाता है |
| 7 | BridgeAttest | attestation, recipient: ShieldedAddress, r, time, asset, envelope, pq_signatures: Vec<PqSignature> | आवश्यक | आधार |
| 8 | BridgeBurn | asset, amount, relayer_fee, to_chain: u16, token: [u8; 32], to: [u8; 32] | आवश्यक, burn_asset == asset और burn_a == amount के साथ | BRIDGE_BURN_FEE = 0.01 RAND |
| 9 | RegisterAggregator | registration | आवश्यक, burn_r = बॉन्ड | आधार |
| 10 | UnbondAggregator | aggregator, nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 11 | WithdrawAggregator | aggregator, nonce, time, r, envelope, signature | अनुपस्थित होना चाहिए | 0 |
| 12 | SlashAggregator | दो हस्ताक्षरित एग्रीगेट हेडर | अनुपस्थित होना चाहिए | 0 |
| 13 | Aggregate | covers, proof, aggregator, nonce, time, r, envelope, signature | अनुपस्थित होना चाहिए | 0 |
| 14 | RegisterToken | name, symbol, decimals: u8, authority: MintAuthority, initial: Option<InitialMint>, salt: [u8; 32], index: u32 | आवश्यक | आधार + रजिस्ट्री का registration_fee (चेन 20 पर 1 RAND, बर्न किया जाता है) |
| 15 | TokenMint | asset, amount, recipient: ShieldedAddress, r, time, envelope, nonce, signature | आवश्यक | आधार |
| 16 | SetAuthority | asset, new: Option<PublicKey>, nonce, signature | आवश्यक | आधार |
| 17 | TokenBurn | asset, amount | आवश्यक, burn_asset == asset और burn_a == amount के साथ | आधार |
| 18 | PauseMints | nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 19 | UnpauseMints | nonce, pq_signatures | अनुपस्थित होना चाहिए | 0 |
| 20 | RegisterBridgedToken | name, symbol, salt, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | आवश्यक | आधार + registration_fee |
| 21 | ListBacking | token_index, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | आवश्यक | आधार |
| 22 | RotatePqGuardians | new_pq_guardians: Vec<PublicKey>, nonce, pq_signatures | अनुपस्थित होना चाहिए | 0 |
| 23 | RotatePauseKey | new_pause_key: PublicKey, nonce, pq_signatures | अनुपस्थित होना चाहिए | 0 |
| 24 | ClaimVested | entry: [u8; 32], amount, nonce, to: ShieldedAddress, time, r, envelope, signature | अनुपस्थित होना चाहिए | वायर पर 0; आधार amount में से काटा जाता है |
| 25 | RevokeVesting | entry: [u8; 32], unvested, nonce, to: ShieldedAddress, time, r, envelope, signatures (v0.6.8 से प्रविष्टि के निरस्तकर्ताओं का थ्रेशोल्ड, और भुगतान उस ट्रेज़री को जिसका नाम उसकी जेनेसिस प्रविष्टि में है; इससे पहले एक signature) | अनुपस्थित होना चाहिए | वायर पर 0; आधार unvested में से काटा जाता है |
| 26 | BondVested | entry: [u8; 32], validator, amount, registration: Option<Registration>, nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 27 | UnbondVested | entry: [u8; 32], amount, nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 28 | AdmitValidator | candidate: PublicKey, signatures: Vec<(PublicKey, Signature)> | अनुपस्थित होना चाहिए | 0 |
| 29 | SlashEquivocation | first, second: एक ही व्यू के लिए एक ही लीडर के दो हस्ताक्षरित ब्लॉक हेडर | अनुपस्थित होना चाहिए | 0 |
| 30 | RotatePqGuardiansV2 | new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>, nonce, pq_signatures | अनुपस्थित होना चाहिए | 0 |
| 31 | RotatePauseKeyV2 | new_pause_key: PublicKey, possession: Vec<u8>, nonce, pq_signatures | अनुपस्थित होना चाहिए | 0 |
| 32 | CancelRotation | kind: u8, nonce, signature | अनुपस्थित होना चाहिए | 0 |
| 33 | Invoke | program, proof, input_envelope: Option<CallEnvelope>, transition: Transition | आवश्यक; burn_r, और burn_asset का burn_a, वही हैं जो प्रोग्राम में आता है | किसी कॉल का न्यूनतम शुल्क + प्रत्येक बनाए गए सेल पर 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
चेन 20 पर टैग 0 से 8, 14 से 21, 28 और 30 से 33 उपयोग योग्य हैं। इसके जेनेसिस में rules_v2 सहित एक bridge खंड, एक tokens खंड, staking.admission_by_vote (टैग 28), bridge.rotation (टैग 30 से 32) और program_state (टैग 33) हैं। वहाँ टैग 22 और 23 अस्वीकार किए जाते हैं: bridge.rotation.needs_possession के अंतर्गत रोटेशन का …V2 रूप में होना अनिवार्य है, जो प्रत्येक नई कुंजी का अपना हस्ताक्षर साथ रखता है। चेन 19 पर, जिसमें इनमें से कोई खंड नहीं था, टैग 0 से 8 और 14 से 23 उपयोग योग्य थे (चेन 14, rules_v2 के बिना, 22 और 23 को अस्वीकार करती थी)। SlashEquivocation के लिए staking.slashing खंड आवश्यक है, और चेन 20 का यह खंड null है। पाँच एग्रीगेशन क्रियाओं के लिए aggregation खंड आवश्यक है, जो चेन 20 में नहीं है (जब तक एग्रीगेशन को फिर से मापा नहीं जाता, कोई भी बिल्ड ऐसा खंड रखने वाले जेनेसिस पर शुरू होने से इनकार करता है)। नोड इन सभी को किसी भी अन्य जाँच से पहले गेट पर ही अस्वीकार कर देता है, फिर भी ये अपने bincode टैग घेरे रहती हैं, इसलिए टैग संख्याएँ हार्ड-कोड करने वाले एन्कोडर को इन्हें गिनना होगा। एग्रीगेशन को प्रूफ़ में आगामी सुविधा के रूप में प्रलेखित किया गया है। चार जेनेसिस-वेस्टिंग क्रियाओं (टैग 24 से 27, v0.5.11 से बिल्ड में) के लिए vesting खंड आवश्यक है, जो किसी भी चेन में नहीं रहा, चेन 20 में भी नहीं, और उसके बिना वे प्रवेश पर ही अस्वीकार हो जाती हैं। टैग 28 से 33 v0.6.8 में नए हैं और अंत में जोड़े गए हैं, ताकि हर पिछली चेन का इतिहास अब भी डिकोड हो सके।
चार RPL क्रियाएँ (टैग 14 से 17) एक टोकन पंजीकृत करती हैं, Key-अधिकृत टोकन की अतिरिक्त मात्रा मिंट करती हैं, उसकी मिंट अथॉरिटी सौंपती या त्यागती हैं, और किसी होल्डिंग को बर्न करती हैं; Bridge या Program अथॉरिटी नामित करने वाला RegisterToken अस्वीकार किया जाता है। टोकन-ट्रांसफ़र की कोई क्रिया नहीं है: टोकन ट्रांसफ़र एक None है, जिसे RAND भुगतान से अलग नहीं पहचाना जा सकता। देखें RPL टोकन। चार ब्रिज गवर्नेंस क्रियाएँ (टैग 18 से 21) हैं: मिंट पर रोक, उस रोक को हटाना, और जेनेसिस के बाद किसी ब्रिज्ड टोकन या किसी अतिरिक्त बैकिंग की लिस्टिंग। PauseMints पर जेनेसिस पॉज़ कुंजी के हस्ताक्षर होते हैं, जो केवल रोक लगा सकती है; शेष तीन के लिए Dilithium2 (PQ) गार्जियनों का कोरम आवश्यक है। BridgeAttest भी अपने ECDSA कोरम के साथ ऐसा ही एक कोरम रखता है। ब्रिज rules_v2 के अंतर्गत टैग 22 और 23 PQ गार्जियन समूह या पॉज़ कुंजी को बदलते हैं, प्रत्येक वर्तमान समूह के PQ कोरम और ब्रिज के rotation_nonce के अंतर्गत। टैग 30 और 31 bridge.rotation के अंतर्गत यही करते हैं और साथ में रोटेशन संदेश पर प्रत्येक नई कुंजी का अपना Dilithium2 हस्ताक्षर भी रखते हैं, जो इस बात का प्रमाण है कि कुंजी धारक के पास है; चेन 20 पर रोटेशन लंबित के रूप में दर्ज होता है और delay_secs, यानी 86,400 s, के बाद प्रभावी होता है। CancelRotation (टैग 32), जिस पर वर्तमान पॉज़ कुंजी के हस्ताक्षर होते हैं, किसी लंबित रोटेशन को प्रभावी होने से पहले रद्द कर देता है। देखें ब्रिज।
AdmitValidator (टैग 28) किसी नई कुंजी को प्रवेश देने के लिए वैलिडेटर समूह का मत है: स्टेक के दो-तिहाई से अधिक भाग धारण करने वाले वैलिडेटरों के हस्ताक्षर, पतों के आरोही क्रम में। staking.admission_by_vote (चेन 20) के अंतर्गत नया वैलिडेटर पंजीकृत करने वाला Bond तब तक अस्वीकार किया जाता है जब तक उसके पते को इस प्रकार प्रवेश न दिया गया हो (स्टेकिंग)। Invoke (टैग 33, RPL-2) एक ऐसी कॉल है जिसका प्रूफ़ प्रोग्राम के एक घोषित स्टेट transition की पुष्टि करता है: पढ़े और लिखे गए सेल, बंडल के बर्न फ़ील्ड प्रोग्राम के वॉल्ट में क्या लाते हैं, और वे नोट जिनका भुगतान वॉल्ट करता है या जिन्हें प्रोग्राम मिंट करता है। यदि हर घोषित रीड अब भी मान्य है और दोनों प्रूफ़ सत्यापित होते हैं, तो लेजर उस ट्रांज़िशन को लागू करता है (प्रोग्राम)।
Deploy और Call सामान्य शील्डेड लेनदेन हैं जिनका बंडल शून्य राशि का स्व-हस्तांतरण होता है: इसका उद्देश्य कॉलर के अपने नोटों से न्यूनतम शुल्क चुकाना है, ताकि चेन को कभी पता न चले कि वे नोट किसके थे। RAND पूल से केवल burn_r के ज़रिए बाहर जाता है — Bond का स्टेक, RegisterAggregator का बॉन्ड, किसी प्रोग्राम वॉल्ट में Invoke की जमा राशि — और कोई टोकन केवल burn_a के ज़रिए, जब burn_asset उसे नामित करता हो, TokenBurn, BridgeBurn या Invoke पर। अन्य कोई भी क्रिया कुछ भी बर्न नहीं करती, और burn_asset = 0 तथा शून्येतर burn_a वाला बंडल हर जगह अस्वीकार किया जाता है।
प्रवेश क्रम
Ledger::validate_inner सस्ती जाँचें महँगी जाँचों से पहले, इस क्रम में करता है; मेमपूल गॉसिप करने से पहले यही जाँच चलाता है, ताकि खराब लेनदेन एक ही बार, किनारे पर ही, अस्वीकार हो जाए।
- आकार सीमाएँ — पूरा ट्रांज़ैक्शन ≤ चेन का
max_block_bytes; बंडल के चारों एन्वेलप में से प्रत्येक, और ऐक्शन के साथ आने वाला कोई भी नोट एन्वेलप, ठीक जेनेसिसenvelope_bytesके बराबर (चेन 20 पर 1,860; इस फ़ील्ड के बिना वाली चेन पर अधिकतम 2,048 बाइट); बंडल प्रूफ़, ऑथ प्रूफ़ और कॉल प्रूफ़ प्रत्येक ≤max_proof_bytes(डिफ़ॉल्ट रूप से 2 MiB, चेन 20 के जेनेसिस में 4 MiB; चेन 13 से 16 पर यह 8 MiB था); प्रोग्राम ≤ चेन की प्रोग्राम सीमा (डिफ़ॉल्ट रूप से 4,096 वर्ड, चेन 20 के जेनेसिस में 65,535); कॉल एन्वेलप ≤max_call_envelope_bytes(डिफ़ॉल्ट रूप से 18,432 बाइट, चेन 20 पर 65,536); अटेस्टेशन ≤ 16 KiB। सील्ड (प्रून्ड) मार्कर रूप वाला बंडल प्रूफ़ सील्ड-रूप सिंक के बाहर यहीं अस्वीकार कर दिया जाता है (PrunedFormOutsideSync), किसी भी प्रूफ़ कार्य से पहले। - चेन id मेल खाता है।
- संरचना और न्यूनतम शुल्क — बंडल-रहित ऐक्शन में कोई बंडल नहीं होता और बाकी हर ऐक्शन में ठीक एक बंडल होना चाहिए;
check_burn_shapeतय करता है कि ऐक्शन तीन बर्न फ़ील्डों में से किन्हें सेट कर सकता है:TokenBurnयाBridgeBurnअपनाassetऔरamountburn_assetऔरburn_aके माध्यम से बर्न करता है,BondयाRegisterAggregatorअपनी राशिburn_rके माध्यम से बर्न करता है, और कोई अन्य ऐक्शन कुछ भी बर्न नहीं करता;fee ≥ fee_floor(action)।gasसेक्शन वाली चेन (चेन 20) पर इस चरण मेंCallयाInvokeका न्यूनतम शुल्क 1 की घोषित सीमा पर गैस नियम है, यानी उसके प्रूफ़ और इनपुट एन्वेलप परBUNDLE_BASE + gas_price + byte_price × ⌈bytes / 1024⌉, जो किसी भी सत्यापन से पहले ज्ञात होते हैं। - एंकर — चेन 20 पर अंतिम 1,024 ब्लॉक-एंड रूट में से एक, जिसका जेनेसिस
proof_window_blocks: 1024सेट करता है (v0.6.8); इस फ़ील्ड के बिना वाली चेन पर अंतिम 256 में से एक, जैसा चेन 19 तक था। - समय — चेन 20 पर
[height − 1024, height]के भीतर; फ़ील्ड के बिना[height − 256, height]। जेनेसिस का एक ही मान दोनों विंडो तय करता है। - नलीफ़ायर और कमिटमेंट — चारों नलीफ़ायर अलग-अलग हैं और कोई भी खर्च नहीं हुआ है; चारों कमिटमेंट अलग-अलग हैं और कोई भी पहले से लीफ़ नहीं है।
- ऐक्शन जाँचें — फ़ॉसेट सक्षम है, मिंट सीमा के भीतर है और वैलिडेटर द्वारा हस्ताक्षरित है, और चेन 20 पर वह जेनेसिस की
staking.faucet_recipientsअनुमति-सूची के किसी प्राप्तकर्ता को नामित करता है, युग केfaucet_budget_per_epochमें समाता है और जेनेसिस कीstaking.faucet_mintersसूची के किसी वैलिडेटर द्वारा हस्ताक्षरित है (चेन 16 से एक वैधता नियम); 2⁶³ या उससे अधिक का मिंट या डिपॉज़िट हर चेन पर अस्वीकार किया जाता है; प्रोग्राम डिकोड होता है; प्रोग्राम मौजूद है; स्टेकिंग नियम (पंजीकरण ठीक तभी मौजूद हो जब वैलिडेटर अज्ञात हो, नॉन्स, हस्ताक्षर, पर्याप्त स्टेक);BridgeAttestऔरBridgeBurnतथा पॉज़ और लिस्टिंग ऐक्शनों के लिए ब्रिज के अपने नियम; RPL के चारों ऐक्शनों के लिए उसके नियम, पंजीकरण शुल्क सहित; चेन 20 पर प्रवेश मतदान का कोरम, औरInvokeके लिए ट्रांज़िशन की संरचना, संग्रहीत सेलों के विरुद्ध घोषित रीड, हर भुगतान के लिए वॉल्ट का कवर और सेल शुल्क। यह सब बंडल के प्रूफ़ से पहले चलता है, इसलिए जो ट्रांज़ैक्शन लागू नहीं हो सकता उस पर कोई STARK सत्यापन खर्च नहीं होता।hardening_v6के अंतर्गत (चेन 16 से 20) यहाँ दो और अस्वीकृतियाँ होती हैं: ऐसे प्रोग्राम काDeployजिसे कोई कॉल धारण न कर सके (ProgramUncallable), और ऐसा बंडल या कॉल प्रूफ़ जिसके हेडर या ट्रांसक्रिप्ट में ऐसा फ़ील्ड हो जो ईमानदार प्रूवर नहीं लिखता (non-canonical proof: …), जिसे किसी भी प्रूफ़ के सत्यापन से पहले अस्वीकार किया जाता है। v0.6.8 से, जिस प्रूफ़ का हेडर सबसे ऊँची तालिका की अधिकतम सीमा से परे डिग्री घोषित करता है, उसे इसी निर्णय के अंतर्गत सबसे पहले अस्वीकार किया जाता है (audit v6 ZKV-4), जाँच द्वारा उसे पढ़े जाने से पहले। - बंडल डाइजेस्ट — विभाजित-प्राधिकरण वाली चेन (चेन 17 से हर चेन) पर ऑथ फ़ील्ड पहले जाँचे जाते हैं: ऑथ प्रूफ़ के बिना बंडल अस्वीकार किया जाता है, ऑथ प्रूफ़ द्वारा प्रकाशित
c(पढ़ा गया, अभी सत्यापित नहीं)auth_commitके बराबर होना चाहिए, और चेन 20 पर ऑथ प्रूफ़ को ठीक 1,279 काGAS_LIMITघोषित करना चाहिए। फिर लेजर बंडल के प्रकाशित फ़ील्डों का डाइजेस्ट पुनः गणना करता है, जिनमेंauth_commitभी है, और वह प्रूफ़ द्वारा प्रकाशित मान के बराबर होना चाहिए। चेन 20 पर बंडल प्रूफ़ को भी ठीक जेनेसिसbundle_gas_limit, 20,479, काGAS_LIMITघोषित करना चाहिए। - बंडल प्रूफ़ — जेनेसिस में निर्धारित
hc_bundleऔर ट्रांज़ैक्शन की बाइंडिंग के विरुद्धMachine::verify_public। विभाजित-प्राधिकरण वाली चेन पर इसके बाद ऑथ प्रूफ़ सत्यापित किया जाता है, जेनेसिस में निर्धारितhc_authऔर उसी बाइंडिंग के विरुद्ध। - कॉल प्रूफ़ और उसका टियर शुल्क —
CallयाInvokeके लिए पहले प्रूफ़ का हेडर निर्धारित किया जाता है (टियर अधिकतम 14, keccak तालिका अधिकतम 2¹², sha256 तालिका अधिकतम 2¹³, डिप्लॉय किए गए प्रोग्राम की ऊँचाई,hardening_v6के अंतर्गत न्यूनतम 2⁷ पंक्तियाँ, और चेन 20 पर घोषितGAS_LIMITहेडर की अधिकतम सीमाgas_maxसे अधिक नहीं), फिर प्रोग्राम केcode_hashऔर,hardening_v6के अंतर्गत, ट्रांज़ैक्शन की कॉल बाइंडिंग के विरुद्ध कॉल का STARK। शुल्क सबसे अंत में जाँचा जाता है, क्योंकि वह प्रूफ़ डिकोड होने के बाद ही ज्ञात होता है: चेन 20 पर लागू कीमतों परfee ≥ BUNDLE_BASE + gas_price × GAS_LIMIT + byte_price × ⌈bytes / 1024⌉, साथ हीInvokeके लिए प्रति निर्मित सेलcell_fee(शुल्क);gasसेक्शन के बिना वाली चेन परfee ≥ BUNDLE_BASE + call_fee(tier)।
त्रुटियाँ rand_sendTransaction से कोड -32000 और कारण के साथ लौटती हैं: nullifier already spent, anchor is not one of the last 1024 roots (proof_window_blocks के बिना वाली चेन पर 256), 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, और चेन 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, चेन 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, तथा चेन 20 पर envelope is 1348 bytes, this chain requires exactly 1860 और bundle proof declares gas limit …, this chain requires exactly 20479। स्वीकृति का अर्थ कमिटमेंट नहीं है: rand_getTransaction को तब तक पोल करें जब तक वह कोई ब्लॉक न लौटाए।
जिस ब्लॉक में कोई भी अमान्य ट्रांज़ैक्शन हो, वह ब्लॉक अमान्य है। मेमपूल हर लंबित नलीफ़ायर और कमिटमेंट को इंडेक्स करता है और किसी भी टकराव को सीधे अस्वीकार कर देता है, इसलिए एक ही नोट खर्च करने वाले दो बंडल कभी एक ब्लॉक में नहीं आते।
आकार
| भाग | बाइट |
|---|---|
Word8 | 32 |
एक Envelope | चेन 18 से 1,892 एन्कोडेड (1,860 सिफ़रटेक्स्ट); चेन 17 तक 1,380 (1,348) |
दोनों प्रूफ़ के बिना Bundle | 7,936 (चार नलीफ़ायर, चार कमिटमेंट, चार एन्वेलप, auth_commit) |
| दोनों प्रूफ़ के बिना ट्रांसफ़र ट्रांज़ैक्शन | 7,949 |
| दोनों प्रूफ़ के बिना डिप्लॉय ट्रांज़ैक्शन, 100-वर्ड प्रोग्राम, कोई सार्वजनिक इनपुट नहीं | 8,369 |
| तीनों प्रूफ़ के बिना कॉल ट्रांज़ैक्शन, कोई इनपुट एन्वेलप नहीं | 7,990 |
| मिंट ट्रांज़ैक्शन (कोई बंडल नहीं) | 5,761 (1,312-बाइट Dilithium2 कुंजी और 2,420-बाइट हस्ताक्षर) |
| एक बंडल प्रूफ़, टियर 14, प्रोडक्शन प्रोफ़ाइल | चेन 18 पर पढ़े गए 1,497,544 और 1,504,260 (ऊँचाई 97,821 पर एक डिप्लॉय और 98,335 पर एक कॉल, 2026-09-30); चेन 14 पर मापे गए 1,422,116 और 1,423,170 (zUSD पंजीकरण और एक बैकिंग लिस्टिंग, 2026-09-20) |
| एक ऑथ प्रूफ़, टियर 10, प्रोडक्शन प्रोफ़ाइल | चेन 18 के उन्हीं दो ट्रांज़ैक्शनों में 1,370,827 और 1,367,303 |
पहली छह पंक्तियाँ चेन 18 से लागू एन्कोडिंग हैं: मेमो फ़ील्ड ने हर एन्वेलप में 512 बाइट जोड़े, और विभाजित प्राधिकरण ने ऑथ प्रूफ़ के अपने बाइटों के अलावा बंडल में 40 बाइट जोड़े। इसलिए चेन 18 से, चेन 20 सहित, नेटवर्क पर एक शील्डेड ट्रांसफ़र लगभग 2.88 MB का है, जो लगभग पूरा प्रूफ़ है, चाहे वह कोई भी एसेट ले जाए: लगभग 1.5 MB बंडल प्रूफ़ और 1.37 MB ऑथ प्रूफ़। चेन 15 तक, एक प्रूफ़ के साथ, यह लगभग 1.43 MB था। ब्लॉक 2,000 ट्रांज़ैक्शनों (MAX_BLOCK_TXS) और ट्रांज़ैक्शन बाइटों के लिए चेन के max_block_bytes तक सीमित हैं, दोनों सहमति नियम हैं: डिफ़ॉल्ट रूप से 4 MiB (MAX_BLOCK_BYTES), जिसमें ऐसा एक ट्रांसफ़र समाएगा, और चेन 20 के जेनेसिस में 20 MiB, जिसमें सात के लिए जगह है। hc_auth वाले जेनेसिस को max_block_bytes कम से कम max_proof_bytes के तीन गुने और 1 MiB के योग पर सेट करना होगा, क्योंकि एक ट्रांज़ैक्शन तीन प्रूफ़ (बंडल, ऑथ, कॉल) ले जा सकता है, इसलिए वहाँ 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, न राशि और न ही asset फ़ील्ड, और संग्रहीत ब्लॉक में भी इनमें से कुछ नहीं है: नोड के पास छिपाने के लिए और कुछ बचता ही नहीं। chain 17 से auth_commit और auth_proof_bytes (auth प्रूफ़ की लंबाई) मौजूद हैं। बंडल में हमेशा चार स्लॉट होते हैं, जिनमें डमी स्लॉट भी शामिल हैं। RAND का ट्रांसफ़र और किसी भी RPL टोकन का ट्रांसफ़र, दोनों "kind": "none" होते हैं। chain 14 से हर u64 राशि एक दशमलव स्ट्रिंग है (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, वेस्टिंग के चार प्रकार 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 संदर्भ में सूचीबद्ध हैं।