اہم مواد پر جائیں
Change page

Simple serialize

صفحہ کی آخری تازہ کاری: 1 فروری، 2026

Simple serialize (SSZ) وہ سیریلائزیشن طریقہ ہے جو Beacon Chain پر استعمال ہوتا ہے۔ یہ consensus layer میں ہر جگہ execution layer پر استعمال ہونے والی RLP سیریلائزیشن کی جگہ لیتا ہے، سوائے peer discovery protocol کے۔ RLP سیریلائزیشن کے بارے میں مزید جاننے کے لیے، Recursive-length prefix (RLP) دیکھیں۔ SSZ کو اس طرح ڈیزائن کیا گیا ہے کہ یہ متعین (deterministic) ہو اور مؤثر طریقے سے Merkleize بھی کر سکے۔ SSZ کو دو اجزاء پر مشتمل سمجھا جا سکتا ہے: ایک سیریلائزیشن اسکیم اور ایک Merkleization اسکیم جسے سیریلائزڈ ڈیٹا اسٹرکچر کے ساتھ مؤثر طریقے سے کام کرنے کے لیے ڈیزائن کیا گیا ہے۔

SSZ کیسے کام کرتا ہے؟

سیریلائزیشن

SSZ ایک سیریلائزیشن اسکیم ہے جو خود اپنی وضاحت نہیں کرتی - بلکہ یہ ایک ایسے اسکیما پر انحصار کرتی ہے جسے پہلے سے معلوم ہونا ضروری ہے۔ SSZ سیریلائزیشن کا مقصد کسی بھی پیچیدگی کی اشیاء (objects) کی نمائندگی بائٹس (bytes) کی اسٹرنگز کے طور پر کرنا ہے۔ یہ "بنیادی اقسام" (basic types) کے لیے ایک بہت آسان عمل ہے۔ عنصر (element) کو سیدھے سادھے ہیکسا ڈیسیمل بائٹس میں تبدیل کر دیا جاتا ہے۔ بنیادی اقسام میں شامل ہیں:

  • غیر دستخط شدہ انٹیجرس (unsigned integers)
  • بولینز (Booleans)

پیچیدہ "مرکب" اقسام (composite types) کے لیے، سیریلائزیشن زیادہ پیچیدہ ہے کیونکہ مرکب قسم میں متعدد عناصر ہوتے ہیں جن کی اقسام یا سائز مختلف ہو سکتے ہیں، یا دونوں۔ جہاں ان تمام اشیاء (objects) کی لمبائی مقرر (fixed) ہوتی ہے (یعنی، عناصر کا سائز ان کی اصل قدروں سے قطع نظر ہمیشہ مستقل رہے گا)، سیریلائزیشن صرف مرکب قسم کے ہر عنصر کو little-endian بائٹ اسٹرنگز میں ترتیب دے کر تبدیل کرنا ہے۔ ان بائٹ اسٹرنگز کو ایک ساتھ جوڑ دیا جاتا ہے۔ سیریلائزڈ آبجیکٹ میں مقررہ لمبائی والے عناصر کی بائٹ لسٹ نمائندگی اسی ترتیب میں ہوتی ہے جس میں وہ ڈی سیریلائزڈ آبجیکٹ میں ظاہر ہوتے ہیں۔

متغیر لمبائی والی اقسام کے لیے، سیریلائزڈ آبجیکٹ میں اس عنصر کی پوزیشن پر اصل ڈیٹا کو ایک "آفسیٹ" قدر سے بدل دیا جاتا ہے۔ اصل ڈیٹا کو سیریلائزڈ آبجیکٹ کے آخر میں ایک ہیپ (heap) میں شامل کیا جاتا ہے۔ آفسیٹ قدر ہیپ میں اصل ڈیٹا کے آغاز کے لیے انڈیکس ہے، جو متعلقہ بائٹس کے لیے ایک پوائنٹر کے طور پر کام کرتا ہے۔

نیچے دی گئی مثال وضاحت کرتی ہے کہ ایک ایسے کنٹینر کے لیے آفسیٹنگ کیسے کام کرتی ہے جس میں مقررہ اور متغیر لمبائی والے دونوں عناصر ہوں:

1
2 struct Dummy {
3
4 number1: u64,
5 number2: u64,
6 vector: Vec<u8>,
7 number3: u64
8 }
9
10 dummy = Dummy{
11
12 number1: 37,
13 number2: 55,
14 vector: vec![1,2,3,4],
15 number3: 22,
16 }
17
18 serialized = ssz.serialize(dummy)
19
سب دکھائیں

serialized کی ساخت درج ذیل ہوگی (یہاں صرف 4 بٹس تک پیڈ کیا گیا ہے، حقیقت میں 32 بٹس تک پیڈ کیا جاتا ہے، اور وضاحت کے لیے int نمائندگی کو برقرار رکھا گیا ہے):

1[37, 0, 0, 0, 55, 0, 0, 0, 16, 0, 0, 0, 22, 0, 0, 0, 1, 2, 3, 4]
2------------ ----------- ----------- ----------- ----------
3 | | | | |
4 number1 number2 vector کے number 3 vector کی
5 لیے آفسیٹ قدر
6

وضاحت کے لیے لائنوں میں تقسیم کیا گیا ہے:

1[
2 37, 0, 0, 0, # `number1` کی little-endian انکوڈنگ۔
3 55, 0, 0, 0, # `number2` کی little-endian انکوڈنگ۔
4 16, 0, 0, 0, # وہ "آفسیٹ" جو بتاتا ہے کہ `vector` کی قدر کہاں سے شروع ہوتی ہے (little-endian 16)۔
5 22, 0, 0, 0, # `number3` کی little-endian انکوڈنگ۔
6 1, 2, 3, 4, # `vector` میں اصل قدریں۔
7]

یہ اب بھی ایک سادہ شکل ہے - اوپر دیے گئے خاکوں میں موجود انٹیجرس اور زیروز دراصل بائٹ لسٹس کے طور پر اسٹور کیے جائیں گے، اس طرح:

1[
2 10100101000000000000000000000000 # `number1` کی little-endian انکوڈنگ
3 10110111000000000000000000000000 # `number2` کی little-endian انکوڈنگ۔
4 10010000000000000000000000000000 # وہ "آفسیٹ" جو بتاتا ہے کہ `vector` کی قدر کہاں سے شروع ہوتی ہے (little-endian 16)۔
5 10010110000000000000000000000000 # `number3` کی little-endian انکوڈنگ۔
6 10000001100000101000001110000100 # `bytes` فیلڈ کی اصل قدر۔
7]

لہذا متغیر لمبائی والی اقسام کی اصل قدریں سیریلائزڈ آبجیکٹ کے آخر میں ایک ہیپ میں اسٹور کی جاتی ہیں اور ان کے آفسیٹس کو فیلڈز کی ترتیب شدہ فہرست میں ان کی صحیح پوزیشنوں پر اسٹور کیا جاتا ہے۔

کچھ خاص معاملات بھی ہیں جن کے لیے مخصوص برتاؤ کی ضرورت ہوتی ہے، جیسے کہ BitList قسم جس میں سیریلائزیشن کے دوران لمبائی کی حد (length cap) شامل کرنے اور ڈی سیریلائزیشن کے دوران اسے ہٹانے کی ضرورت ہوتی ہے۔ مکمل تفصیلات SSZ spec (opens in a new tab) میں دستیاب ہیں۔

ڈی سیریلائزیشن

اس آبجیکٹ کو ڈی سیریلائز کرنے کے لیے اسکیما کی ضرورت ہوتی ہے۔ اسکیما سیریلائزڈ ڈیٹا کی عین ترتیب کی وضاحت کرتا ہے تاکہ ہر مخصوص عنصر کو بائٹس کے ایک blob سے ایک ایسے بامعنی آبجیکٹ میں ڈی سیریلائز کیا جا سکے جس میں عناصر کی قسم، قدر، سائز اور پوزیشن صحیح ہو۔ یہ اسکیما ہی ہے جو ڈی سیریلائزر کو بتاتا ہے کہ کون سی قدریں اصل ہیں اور کون سی آفسیٹ ہیں۔ جب کسی آبجیکٹ کو سیریلائز کیا جاتا ہے تو تمام فیلڈ کے نام غائب ہو جاتے ہیں، لیکن ڈی سیریلائزیشن پر اسکیما کے مطابق انہیں دوبارہ بنایا جاتا ہے۔

اس پر ایک انٹرایکٹو وضاحت کنندہ کے لیے ssz.dev (opens in a new tab) دیکھیں۔

مرکلائزیشن

اس SSZ سیریلائزڈ آبجیکٹ کو پھر مرکلائز کیا جا سکتا ہے - یعنی اسی ڈیٹا کی Merkle-tree نمائندگی میں تبدیل کیا جا سکتا ہے۔ سب سے پہلے، سیریلائزڈ آبجیکٹ میں 32-بائٹ کے چنکس (chunks) کی تعداد کا تعین کیا جاتا ہے۔ یہ درخت کے "پتے" (leaves) ہیں۔ پتوں کی کل تعداد 2 کی طاقت (power of 2) ہونی چاہیے تاکہ پتوں کو ایک ساتھ ہیش کرنے پر آخر کار ایک واحد ہیش-ٹری-روٹ (hash-tree-root) بن جائے۔ اگر قدرتی طور پر ایسا نہیں ہے، تو 32 بائٹس کے زیروز پر مشتمل اضافی پتے شامل کیے جاتے ہیں۔ خاکے کے طور پر:

1 ہیش ٹری روٹ
2 / \
3 / \
4 / \
5 / \
6 پتے 1 اور 2 کا ہیش پتے 3 اور 4 کا ہیش
7 / \ / \
8 / \ / \
9 / \ / \
10 پتا 1 پتا 2 پتا 3 پتا 4
سب دکھائیں

ایسے معاملات بھی ہیں جہاں درخت کے پتے قدرتی طور پر اس طرح یکساں طور پر تقسیم نہیں ہوتے جیسا کہ اوپر کی مثال میں ہے۔ مثال کے طور پر، پتا 4 ایک ایسا کنٹینر ہو سکتا ہے جس میں متعدد عناصر ہوں جن کے لیے Merkle ٹری میں اضافی "گہرائی" (depth) شامل کرنے کی ضرورت ہوتی ہے، جس سے ایک غیر مساوی درخت بنتا ہے۔

درخت کے ان عناصر کو پتا X، نوڈ X وغیرہ کہنے کے بجائے، ہم انہیں عمومی انڈیکس دے سکتے ہیں، جس کی شروعات روٹ = 1 سے ہوتی ہے اور ہر سطح پر بائیں سے دائیں گنتی کی جاتی ہے۔ یہ اوپر بیان کردہ عمومی انڈیکس ہے۔ سیریلائزڈ فہرست میں ہر عنصر کا ایک عمومی انڈیکس ہوتا ہے جو 2**depth + idx کے برابر ہوتا ہے، جہاں idx سیریلائزڈ آبجیکٹ میں اس کی زیرو-انڈیکسڈ پوزیشن ہے اور depth مرکل ٹری میں سطحوں کی تعداد ہے, جس کا تعین عناصر (پتوں) کی تعداد کے بیس-ٹو لاگرتھم کے طور پر کیا جا سکتا ہے۔

عمومی انڈیکس

ایک عمومی انڈیکس ایک انٹیجر ہے جو ایک بائنری Merkle ٹری میں ایک نوڈ کی نمائندگی کرتا ہے جہاں ہر نوڈ کا ایک عمومی انڈیکس 2 ** depth + index in row ہوتا ہے۔

1 1 --گہرائی = 0 2**0 + 0 = 1
2 2 3 --گہرائی = 1 2**1 + 0 = 2, 2**1+1 = 3
3 4 5 6 7 --گہرائی = 2 2**2 + 0 = 4, 2**2 + 1 = 5...
4

یہ نمائندگی Merkle ٹری میں موجود ڈیٹا کے ہر ٹکڑے کے لیے ایک نوڈ انڈیکس فراہم کرتی ہے۔

ملٹی پروفس

کسی مخصوص عنصر کی نمائندگی کرنے والے عمومی انڈیکس کی فہرست فراہم کرنا ہمیں اسے ہیش-ٹری-روٹ کے خلاف تصدیق کرنے کی اجازت دیتا ہے۔ یہ روٹ حقیقت کا ہمارا قبول شدہ ورژن ہے۔ ہمیں فراہم کردہ کسی بھی ڈیٹا کی اس حقیقت کے خلاف تصدیق کی جا سکتی ہے، اسے Merkle ٹری میں صحیح جگہ پر داخل کرکے (جس کا تعین اس کے عمومی انڈیکس سے ہوتا ہے) اور یہ دیکھ کر کہ روٹ مستقل رہتا ہے۔ اسپیک میں یہاں (opens in a new tab) فنکشنز ہیں جو عمومی انڈیکس کے ایک خاص سیٹ کے مواد کی تصدیق کے لیے درکار نوڈز کے کم سے کم سیٹ کا حساب لگانے کا طریقہ دکھاتے ہیں۔

مثال کے طور پر، نیچے دیے گئے ٹری میں انڈیکس 9 پر ڈیٹا کی تصدیق کرنے کے لیے، ہمیں انڈیکس 8، 9، 5، 3، 1 پر موجود ڈیٹا کے ہیش کی ضرورت ہے۔ (8,9) کا ہیش، ہیش (4) کے برابر ہونا چاہیے، جو 5 کے ساتھ ہیش ہو کر 2 بناتا ہے، جو 3 کے ساتھ ہیش ہو کر ٹری روٹ 1 بناتا ہے۔ اگر 9 کے لیے غلط ڈیٹا فراہم کیا گیا، تو روٹ بدل جائے گا - ہم اس کا پتہ لگا لیں گے اور برانچ کی تصدیق میں ناکام ہو جائیں گے۔

1* = پروف بنانے کے لیے درکار ڈیٹا
2
3 1*
4 2 3*
5 4 5* 6 7
68* 9* 10 11 12 13 14 15
7

مزید پڑھیں

کیا یہ آرٹیکل کارآمد تھا؟