वायर प्रोटोकॉल
Zycord नोड आपस में क्या कहते हैं, और वे नियम जो किसी पीयर प्रोटोकॉल को एम्प्लिफ़ायर बनने से रोकते हैं। यह मानक विशिष्टि की मार्गदर्शिका है, उसका विकल्प नहीं।
विशिष्टि spec/ है: पैरामीटर फ़ाइलें और वेक्टर संग्रह। जहाँ यह पृष्ठ और ट्री असहमत हों, ट्री सही है — कोई कार्यान्वयन तब अनुरूप है जब वह genesis id दोबारा बनाए और वेक्टर पास कर ले, और यहाँ लिखी कोई बात इसे नहीं बदलती। आगे जो है वह इस बात का नक़्शा है कि आवश्यकताएँ कहाँ हैं।
ट्रांसपोर्ट#
TCP, फिर TLS 1.3। पीयर पहचान एक Ed25519 कुंजी है जो स्व-हस्ताक्षरित X.509 प्रमाणपत्र में ढोई जाती है, और दोनों पक्ष उसे प्रस्तुत करते हैं। प्रमाणपत्र श्रृंखलाओं का सत्यापन नहीं होता — यहाँ कोई प्रमाणपत्र प्राधिकारी नहीं है और उसके लिए गवाही देने को कुछ नहीं — इसलिए सत्यापन के लिए ठीक एक प्रमाणपत्र चाहिए, उसकी सार्वजनिक कुंजी का Ed25519 होना ज़रूरी है, और वही कुंजी पीयर की पहचान के रूप में निकाली जाती है।
जो गुण मिलता है वह है किसी कुंजी से चैनल का बंधन, प्राधिकार नहीं: TLS यह गारंटी देता है कि जिसने भी हैंडशेक पूरा किया उसके पास प्रस्तुत पहचान की निजी कुंजी है, और यह कि धारा को रास्ते में पढ़ा या बदला नहीं जा सकता। यह इस बारे में कुछ नहीं कहता कि वह पीयर ईमानदार है या नहीं। प्रोटोकॉल में कुछ भी पहचान के आधार पर अधिकार नहीं देता।
प्रमाणपत्र की वैधता तिथियाँ जानबूझकर वर्तमान समय के इर्द-गिर्द की खिड़की के बजाय नियत स्थिरांक हैं: सापेक्ष खिड़की प्रमाणपत्र की स्वीकृति को घड़ियों की सहमति पर निर्भर बना देती है, और जो नेटवर्क घड़ी के अंतर पर बँटता है वह ऐसे कारण से बँटता है जिसका consensus से कोई संबंध नहीं।
कार्यान्वयन को किसी अन्य परत की कुंजी को पीयर पहचान के रूप में दोबारा इस्तेमाल नहीं करना चाहिए। नोड हर बार शुरू होने पर एक नई कुंजी बनाता है और उसे कभी डिस्क पर नहीं लिखता।
फ़्रेमिंग#
offset size field
0 4 length uint32, little-endian - payload length, excluding this header
4 1 kind uint8 - see below
5 n payload
lengthको किसी भी आवंटन से पहलेMaxMessageBytes= 8 MiB के विरुद्ध जाँचा जाना चाहिए। जो प्राप्तकर्ता दावा की गई लंबाई पर आवंटन कर देता है उसमें एक दूरस्थ मेमोरी-क्षय बग है, चाहे आगे कुछ भी हो।kind[1, 9]में होना चाहिए। शून्य और ज्ञात उच्चतम प्रकार से ऊपर की हर चीज़ प्रोटोकॉल का उल्लंघन है, कोई अज्ञात विस्तार नहीं: प्रोटोकॉल 1 पर आगे-संगतता का कोई निकास द्वार नहीं है, क्योंकि हैंडशेक पर जाँचा गया संस्करण फ़ील्ड उसकी ज़रूरत ही नहीं छोड़ता।
संदेश प्रकार#
| मान | नाम | दिशा | पेलोड |
|---|---|---|---|
| 1 | hello | दोनों, सबसे पहले | हैंडशेक |
| 2 | certificate | gossip | ssz(Certificate) |
| 3 | block-announce | gossip | हेडर और सर्टिफिकेट id |
| 4 | get-block | अनुरोध | 32-बाइट ब्लॉक id ‖ u32 chunk इंडेक्स |
| 5 | block | प्रतिक्रिया | ssz(Block) का एक chunk |
| 6 | get-headers | अनुरोध | Locator |
| 7 | headers | प्रतिक्रिया | हेडर शृंखला |
| 8 | get-peers | अनुरोध | ख़ाली |
| 9 | peers | प्रतिक्रिया | पता सूची |
किसी consensus वस्तु का कोई नेटवर्क-विशिष्ट एन्कोडिंग नहीं है, और यही "जो मुझे मिला उसकी id" को एक जाँचने योग्य कथन बनाता है।
हर इनबाउंड संदेश की क़ीमत उसका भेजने वाला चुकाता है#
विशिष्टि का यही हिस्सा पूरा पढ़ने लायक सबसे ज़्यादा है, क्योंकि यहीं पीयर प्रोटोकॉल आमतौर पर रिसते हैं। इसे दो नियम संचालित करते हैं।
लागत-क्रम का नियम#
काम लागत के बढ़ते क्रम में किया जाता है, और संदेश का हिसाब उसके बाद आने वाले महँगे चरण से पहले लगाया जाता है। जो प्राप्तकर्ता पहले सत्यापन करता है और अंक बाद में देता है उसने एक एम्प्लिफ़ायर बना लिया है: जो भेजने में सस्ता है वही जाँचने में महँगा है।
हर परिणाम की क़ीमत तय है#
कोई बिना नाम वाला Free नहीं है। हर संदेश प्रकार, उसके हर संभावित
परिणाम के साथ, एक तालिका में अंक सहित दर्ज है। जो परिणाम बिना हिसाब लगे तालिका से फिसल जाए वही वह
खामी है जिसे रोकने के लिए यह नियम मौजूद है — और व्यवहार में उस तक बिना-क़ीमत वाली पंक्ति से भी सँकरे
रास्तों से पहुँचा गया है: एक कार्यान्वयन ने ख़राब फ़्रेम को उसके इकलौते अंकदाता से हटा दिया, जिससे वह फ़्रेम
तब तक मुफ़्त रहा जब तक कोई अनुरोध बकाया था, और किसी तालिका में एक भी पंक्ति नहीं जुड़ी।
सेवा देने का हिसाब भी लगता है। ब्लॉक बाइट्स ही वह इकलौती प्रतिक्रिया है जो उसे माँगने वाले अनुरोध से परिमाण की कोटियों में बड़ी होती है, इसलिए वे अनुरोध की गिनती के ऊपर अपना अलग बाइट बजट ढोते हैं।
कनेक्शन प्रबंधन, और eclipse बचाव#
यह अनुभाग किसी भी अन्य से ज़्यादा मापी हुई विफलताएँ ढोता है, और नीचे दी गई हर अपेक्षा इसलिए मौजूद है क्योंकि उसके बिना बना कोई कार्यान्वयन किसी माप में eclipse हो गया था।
- बाहर जाने वाले लक्ष्य पते की विविधता के साथ चुने जाने चाहिए, ताकि कोई एक होस्टिंग रेंज किसी नोड के आउटबाउंड slot न भर सके।
- बाहर जाने वाले लक्ष्यों की प्रति gossip स्रोत सीमा भी होनी चाहिए। अकेली पता-विविधता काफ़ी नहीं है, और इसका कारण निर्णय नहीं बल्कि अंकगणित है: पता समूह उस पते का गुण है जिसका हमलावर स्वामी है, पर जो पता कोई पीयर दावा करता है वह उसके गढ़े हुए बाइट हैं, और कोई भी चार बाइट एक valid IPv4 होस्ट हैं। जिस हमलावर के पास एक भी पता न हो वह एक फ़्रेम की क़ीमत पर प्रति स्ट्रिंग एक नया विविधता समूह गढ़ लेता है। जो वह नहीं गढ़ सकता वह है वह कनेक्शन जिस पर दावा आया।
- जिस सॉकेट पर कोई इनबाउंड कनेक्शन आया वह आउटबाउंड लक्ष्य नहीं बनना चाहिए। वह पीयर का स्रोत पता है — एक क्षणिक पोर्ट जो उसके OS ने चुना, ऐसा कुछ नहीं जिस पर वह सुनता हो — इसलिए उसे डायल करने में ख़र्च हुआ slot एक ऐसे पते पर ख़र्च हुआ slot है जो उत्तर दे ही नहीं सकता।
- दोनों सीमाएँ उन कनेक्शनों के विरुद्ध गिनी जानी चाहिए जो नोड थामे हुए है, न कि किसी एक चयन कॉल के विरुद्ध। वरना जो डायल लूप पहले से जुड़े पीयर को छोड़ देता है वह हर दौर में दोनों बजट फिर से पूरे भर देता है, और सीमा हमलावर को बाँधने के बजाय प्रति छूट एक दौर भर टालती है। मापा गया: एक ही बताने वाले ने चार दौरों में 8 में से पहले 2, फिर 4, फिर 6, फिर 8 आउटबाउंड slot ले लिए।
- पीयर स्टोर स्थायी होना चाहिए, और सीमित होना चाहिए। जो नोड हर बार पुनः शुरू होने पर ख़ाली शुरू होता है वह हमलावर को हर बार नया मौक़ा देता है; और जो हमलावर की सूची के साथ शुरू होता है वह उसे वही मौक़ा स्थायी रूप से दे देता है।
- सीमित स्टोर को किसी सुगठित पते को इसलिए मना नहीं करना चाहिए कि वह भरा हुआ है; उसे उसके लिए जगह ख़ाली करनी चाहिए। कभी संपर्क न किया गया ईमानदार पता कभी संपर्क न किए गए गढ़े हुए पते से बेहतर नहीं होता, इसलिए जो हमलावर स्टोर पहले भर देता है वह उसे बाद में दी गई हर चीज़ के लिए बंद कर देता है — ऑपरेटर की अपनी बूटस्ट्रैप सूची समेत।
- निष्कासन क्या चुनता है यह इस बात से ज़्यादा मायने रखता है कि वह होता है। "सबसे बड़ी आबादी से शिकार लो" ऐसा पढ़ा जाता है मानो बाढ़ ख़ुद को ही विस्थापित करती है, और यह तभी तक सच है जब तक बाढ़ स्टोर की सबसे बड़ी चीज़ हो। जो नोड एक मददगार पीयर से बूटस्ट्रैप हुआ हो, उस पर सबसे बड़ी आबादी उसी पीयर की पता-पुस्तिका है। इसी क्रम वाले कार्यान्वयन पर मापा गया: एक ही स्रोत से आए 200 गढ़े हुए पतों ने 200 ईमानदार पते हटा दिए, और हमलावर को कोई क़ीमत नहीं पड़ी।
- अभेद्य प्रविष्टियों के बीच अंतिम बराबरी-भंजक ऐसा कुछ नहीं होना चाहिए जिसे gossip करने वाला पीयर चुनता हो — पता भी नहीं — और यह चयन पर ठीक वैसे ही लागू होता है जैसे निष्कासन पर। जिस कार्यान्वयन का चयनकर्ता आख़िर में पता स्ट्रिंग पर आ गिरा उस पर मापा गया: 8 ईमानदार पतों के मुक़ाबले 8 गढ़े हुए पतों ने 8 में से 8 गढ़े हुए लौटाए, और दस दौरों में शून्य ईमानदार आउटबाउंड कनेक्शन।
जानबूझकर क्या अनुपस्थित है#
| अनुपस्थित | क्यों |
|---|---|
| NAT traversal | लागत बता दी गई है और दोबारा खोलने की शर्त मान लेने के बजाय मापी जाती है: सार्वजनिक testnet पर dialable नोड का अनुपात इस निर्णय को दोबारा खोलने की पहली शर्त है। |
| संपीड़न | हमलावर के नियंत्रण वाली धारा पर लगा compressor एक हमला-सतह है, और वह भी ऐसी बैंडविड्थ बचत के लिए जिसकी ज़रूरत किसी ने मापी ही नहीं। |
| संदेश-स्तरीय हस्ताक्षर | ट्रांसपोर्ट चैनल को प्रमाणित करता है; consensus वस्तुएँ अपने हस्ताक्षर ख़ुद ढोती हैं। तीसरी परत relay को प्रमाणित करती, और किसी निर्णय की उस पर निर्भरता नहीं है। |
| अनुरोध id | अनुरोध और प्रतिक्रिया कनेक्शन और क्रम से मिलाए जाते हैं, इसलिए sync अपने ही कनेक्शन पर चलता है — जिससे एक स्टेट मशीन हट जाती है। जहाँ पीयर dialable न हो, वहाँ sync किसी मौजूदा gossip कनेक्शन पर चल सकता है और तब उसे प्रतिक्रियाएँ सामग्री से मिलानी होंगी। |
| आगे-संगत विस्तार तंत्र | प्रोटोकॉल 1 हैंडशेक पर अपना संस्करण जाँचता है और बेमेल पर कनेक्शन तोड़ देता है। विस्तार प्रोटोकॉल 2 के रूप में आएँगे। |
सर्टिफिकेट id किसे शामिल नहीं करती#
यहाँ दोबारा कहने लायक है क्योंकि इससे एक relay नियम तय होता है। किसी सर्टिफिकेट की id उसी के प्रति प्रतिबद्ध होती है जिसे वह अधिकृत करता है, कभी उसके प्रति नहीं जिसे वह केवल प्रदर्शित करता है, इसलिए हस्ताक्षर id के preimage से बाहर रहते हैं। इससे साक्ष्य ऐसी चीज़ बन जाता है जिसे रास्ते में कोई भी बदल सकता है: एक सर्टिफिकेट लो, उसके हस्ताक्षर की जगह कचरा रख दो, और आगे बढ़ा दो — वही id, पर अब अमान्य exemplar।
दो नियम इसे बंद करते हैं। एक ब्लॉक उस साक्ष्य के प्रति प्रतिबद्ध होता है जिसे वह ढोता है, id के बजाय exemplar हैश पर बने एक root के ज़रिए। और relay exemplar से निपटता है, id से नहीं: जो नोड ऐसा exemplar पाता है जिसका साक्ष्य सत्यापन में विफल हो, वह उसी exemplar को id के प्रति बिना पूर्वाग्रह के त्याग देता है — id न चिह्नित होती है, न अमान्य के रूप में कैश होती है, और बाद में आया जो exemplar सत्यापित हो जाए वह सामान्य रूप से relay होता है। बिगाड़ी गई प्रति बिगाड़ने वाले को उसे भेजने की बैंडविड्थ की पड़ती है और सर्टिफिकेट को कुछ नहीं।
इसे शून्य से लागू करना#
जो स्वतंत्र कार्यान्वयन
golden vectors पास कर ले वह एक पीयर है, fork नहीं — इसे
इस तरह निर्दिष्ट करने का पूरा मक़सद यही है। consensus वस्तुओं के लिए
spec/README.md से शुरू करें, और फिर अपने काम की जाँच
zcd vectors से करें।