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

آف لائن ڈیٹا کی سالمیت کے لیے مرکل پروفس

اسٹوریج
ترقی
Ori Pomerantz
30 دسمبر، 2021
12 منٹ کی پڑھائی

تعارف

مثالی طور پر ہم ہر چیز کو Ethereum اسٹوریج میں اسٹور کرنا چاہیں گے، جو ہزاروں کمپیوٹرز میں اسٹور ہوتا ہے اور اس میں انتہائی زیادہ دستیابی (ڈیٹا کو سنسر نہیں کیا جا سکتا) اور سالمیت (ڈیٹا کو غیر مجاز طریقے سے تبدیل نہیں کیا جا سکتا) ہوتی ہے، لیکن 32-بائٹ ورڈ کو اسٹور کرنے پر عام طور پر 20,000 گیس خرچ ہوتی ہے۔ جب میں یہ لکھ رہا ہوں، تو یہ لاگت $6.60 کے برابر ہے۔ 21 سینٹ فی بائٹ پر یہ بہت سے استعمالات کے لیے بہت مہنگا ہے۔

اس مسئلے کو حل کرنے کے لیے Ethereum ایکو سسٹم نے ڈیٹا کو وکندریقرت طریقے سے اسٹور کرنے کے بہت سے متبادل طریقے تیار کیے ہیں۔ عام طور پر ان میں دستیابی اور قیمت کے درمیان ایک سمجھوتہ شامل ہوتا ہے۔ تاہم، سالمیت کو عام طور پر یقینی بنایا جاتا ہے۔

اس مضمون میں آپ سیکھیں گے کہ مرکل پروفسopens in a new tab کا استعمال کرتے ہوئے، بلاک چین پر ڈیٹا اسٹور کیے بغیر ڈیٹا کی سالمیت کو کیسے یقینی بنایا جائے۔

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

نظریاتی طور پر ہم صرف ڈیٹا کا ہیش آن چین اسٹور کر سکتے ہیں، اور تمام ڈیٹا کو ان ٹرانزیکشنز میں بھیج سکتے ہیں جن میں اس کی ضرورت ہوتی ہے۔ تاہم، یہ اب بھی بہت مہنگا ہے۔ ایک ٹرانزیکشن میں ایک بائٹ ڈیٹا کی لاگت تقریباً 16 گیس ہوتی ہے، جو فی الحال تقریباً آدھا سینٹ، یا تقریباً 5 ڈالر فی کلو بائٹ ہے۔ 5000 ڈالر فی میگا بائٹ پر، یہ بہت سے استعمالات کے لیے اب بھی بہت مہنگا ہے، یہاں تک کہ ڈیٹا کو ہیش کرنے کی اضافی لاگت کے بغیر بھی۔

اس کا حل یہ ہے کہ ڈیٹا کے مختلف سب سیٹس کو بار بار ہیش کیا جائے، تاکہ جس ڈیٹا کو آپ کو بھیجنے کی ضرورت نہیں ہے اس کے لیے آپ صرف ایک ہیش بھیج سکیں۔ آپ یہ ایک مرکل ٹری کا استعمال کرتے ہوئے کرتے ہیں، جو ایک ٹری ڈیٹا اسٹرکچر ہے جہاں ہر نوڈ اس کے نیچے موجود نوڈس کا ہیش ہوتا ہے:

مرکل ٹری

روٹ ہیش واحد حصہ ہے جسے آن چین اسٹور کرنے کی ضرورت ہے۔ ایک خاص قدر کو ثابت کرنے کے لیے، آپ وہ تمام ہیش فراہم کرتے ہیں جنہیں روٹ حاصل کرنے کے لیے اس کے ساتھ ملانے کی ضرورت ہوتی ہے۔ مثال کے طور پر، C کو ثابت کرنے کے لیے آپ D، H(A-B)، اور H(E-H) فراہم کرتے ہیں۔

C کی قدر کا ثبوت

عمل درآمد

نمونہ کوڈ یہاں فراہم کیا گیا ہےopens in a new tab۔

آف چین کوڈ

اس مضمون میں ہم آف چین حسابات کے لیے JavaScript کا استعمال کرتے ہیں۔ زیادہ تر وکندریقرت ایپلی کیشنز کا آف چین جزو JavaScript میں ہوتا ہے۔

مرکل روٹ بنانا

سب سے پہلے ہمیں چین کو مرکل روٹ فراہم کرنے کی ضرورت ہے۔

1const ethers = require("ethers")

ہم ethers پیکیج سے ہیش فنکشن کا استعمال کرتے ہیںopens in a new tab۔

1// The raw data whose integrity we have to verify. The first two bytes a
2// are a user identifier, and the last two bytes the amount of tokens the
3// user owns at present.
4const dataArray = [
5 0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060,
6 0xface0070, 0xbad00080, 0x060d0091,
7]

ہر اندراج کو ایک 256-بٹ انٹیجر میں انکوڈ کرنے سے، مثال کے طور پر JSON استعمال کرنے کے مقابلے میں، کم پڑھنے کے قابل کوڈ بنتا ہے۔ تاہم، اس کا مطلب ہے کہ کنٹریکٹ میں ڈیٹا کو بازیافت کرنے کے لیے کافی کم پروسیسنگ کی ضرورت ہوتی ہے، اس لیے گیس کی لاگت بہت کم ہوتی ہے۔ آپ آن چین پر JSON پڑھ سکتے ہیںopens in a new tab، لیکن اگر اس سے بچا جا سکے تو یہ ایک برا خیال ہے۔

1// The array of hash values, as BigInts
2const hashArray = dataArray

اس معاملے میں ہمارا ڈیٹا شروع میں 256-بٹ ویلیوز ہے، اس لیے کسی پروسیسنگ کی ضرورت نہیں ہے۔ اگر ہم زیادہ پیچیدہ ڈیٹا اسٹرکچر، جیسے کہ اسٹرنگز، استعمال کرتے ہیں، تو ہمیں یہ یقینی بنانا ہوگا کہ ہم ہیشز کا ایک ایرے حاصل کرنے کے لیے پہلے ڈیٹا کو ہیش کریں۔ نوٹ کریں کہ یہ اس لیے بھی ہے کہ ہمیں اس بات کی پرواہ نہیں ہے کہ آیا صارفین دوسرے صارفین کی معلومات جانتے ہیں۔ بصورت دیگر ہمیں ہیش کرنا پڑتا تاکہ صارف 1 کو صارف 0 کی قدر معلوم نہ ہو، صارف 2 کو صارف 3 کی قدر معلوم نہ ہو، وغیرہ۔

1// Convert between the string the hash function expects and the
2// BigInt we use everywhere else.
3const hash = (x) =>
4 BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0)))

ethers ہیش فنکشن ایک ہیکساڈیسیمل نمبر کے ساتھ ایک JavaScript اسٹرنگ حاصل کرنے کی توقع رکھتا ہے، جیسے 0x60A7، اور اسی ساخت کے ساتھ ایک اور اسٹرنگ کے ساتھ جواب دیتا ہے۔ تاہم، باقی کوڈ کے لیے BigInt کا استعمال کرنا آسان ہے، اس لیے ہم ایک ہیکساڈیسیمل اسٹرنگ میں تبدیل کرتے ہیں اور پھر واپس۔

1// Symmetrical hash of a pair so we won't care if the order is reversed.
2const pairHash = (a, b) => hash(hash(a) ^ hash(b))

یہ فنکشن سیمیٹریکل ہے (a xoropens in a new tab b کا ہیش)۔ اس کا مطلب ہے کہ جب ہم مرکل پروف کی جانچ کرتے ہیں تو ہمیں اس بارے میں فکر کرنے کی ضرورت نہیں ہے کہ پروف سے حاصل کردہ قدر کو حسابی قدر سے پہلے رکھنا ہے یا بعد میں۔ مرکل پروف کی جانچ آن چین کی جاتی ہے، لہذا ہمیں وہاں جتنا کم کام کرنے کی ضرورت ہوگی، اتنا ہی بہتر ہے۔

انتباہ: کرپٹوگرافی جتنی نظر آتی ہے اس سے زیادہ مشکل ہے۔ اس مضمون کے ابتدائی ورژن میں ہیش فنکشن hash(a^b) تھا۔ یہ ایک برا خیال تھا کیونکہ اس کا مطلب یہ تھا کہ اگر آپ کو a اور b کی جائز قدریں معلوم ہوتیں تو آپ کسی بھی مطلوبہ a' قدر کو ثابت کرنے کے لیے b' = a^b^a' کا استعمال کر سکتے تھے۔ اس فنکشن کے ساتھ آپ کو b' کا حساب لگانا ہوگا تاکہ hash(a') ^ hash(b') ایک معلوم قدر (روٹ کے راستے پر اگلی شاخ) کے برابر ہو، جو کہ بہت زیادہ مشکل ہے۔

1// The value to denote that a certain branch is empty, doesn't
2// have a value
3const empty = 0n

جب قدروں کی تعداد دو کی انٹیجر پاور نہیں ہوتی ہے تو ہمیں خالی شاخوں کو ہینڈل کرنے کی ضرورت ہوتی ہے۔ یہ پروگرام جس طرح سے یہ کرتا ہے وہ یہ ہے کہ صفر کو ایک پلیس ہولڈر کے طور پر رکھتا ہے۔

غائب شاخوں کے ساتھ مرکل ٹری

1// Calculate one level up the tree of a hash array by taking the hash of
2// each pair in sequence
3const oneLevelUp = (inputArray) => {
4 var result = []
5 var inp = [...inputArray] // To avoid over writing the input // Add an empty value if necessary (we need all the leaves to be // paired)
6
7 if (inp.length % 2 === 1) inp.push(empty)
8
9 for (var i = 0; i < inp.length; i += 2)
10 result.push(pairHash(inp[i], inp[i + 1]))
11
12 return result
13} // oneLevelUp
سب دکھائیں

یہ فنکشن موجودہ لیئر پر قدروں کے جوڑوں کو ہیش کرکے مرکل ٹری میں ایک لیول "اوپر چڑھتا" ہے۔ نوٹ کریں کہ یہ سب سے موثر عمل درآمد نہیں ہے، ہم ان پٹ کو کاپی کرنے سے بچ سکتے تھے اور لوپ میں مناسب ہونے پر صرف hashEmpty شامل کر سکتے تھے، لیکن یہ کوڈ پڑھنے کی اہلیت کے لیے بہتر بنایا گیا ہے۔

1const getMerkleRoot = (inputArray) => {
2 var result
3
4 result = [...inputArray] // Climb up the tree until there is only one value, that is the // root. // // If a layer has an odd number of entries the // code in oneLevelUp adds an empty value, so if we have, for example, // 10 leaves we'll have 5 branches in the second layer, 3 // branches in the third, 2 in the fourth and the root is the fifth
5
6 while (result.length > 1) result = oneLevelUp(result)
7
8 return result[0]
9}
سب دکھائیں

روٹ حاصل کرنے کے لیے، اس وقت تک چڑھیں جب تک کہ صرف ایک قدر باقی نہ رہ جائے۔

مرکل پروف بنانا

مرکل پروف وہ قدریں ہیں جنہیں ثابت کی جانے والی قدر کے ساتھ ہیش کرکے مرکل روٹ واپس حاصل کیا جاتا ہے۔ ثابت کرنے کی قدر اکثر دوسرے ڈیٹا سے دستیاب ہوتی ہے، اس لیے میں اسے کوڈ کے حصے کے طور پر فراہم کرنے کے بجائے الگ سے فراہم کرنا پسند کرتا ہوں۔

1// A merkle proof consists of the value of the list of entries to
2// hash with. Because we use a symmetrical hash function, we don't
3// need the item's location to verify the proof, only to create it
4const getMerkleProof = (inputArray, n) => {
5    var result = [], currentLayer = [...inputArray], currentN = n
6
7    // Until we reach the top
8    while (currentLayer.length > 1) {
9        // No odd length layers
10        if (currentLayer.length % 2)
11            currentLayer.push(empty)
12
13        result.push(currentN % 2
14               // If currentN is odd, add with the value before it to the proof
15            ? currentLayer[currentN-1]
16               // If it is even, add the value after it
17            : currentLayer[currentN+1])
18
سب دکھائیں

ہم (v[0],v[1])، (v[2],v[3])، وغیرہ کو ہیش کرتے ہیں۔ لہذا جفت قدروں کے لیے ہمیں اگلی قدر کی ضرورت ہے، اور طاق قدروں کے لیے پچھلی قدر کی۔

1        // Move to the next layer up
2        currentN = Math.floor(currentN/2)
3        currentLayer = oneLevelUp(currentLayer)
4    }   // while currentLayer.length > 1
5
6    return result
7}   // getMerkleProof

آن چین کوڈ

آخر میں ہمارے پاس وہ کوڈ ہے جو پروف کی جانچ کرتا ہے۔ آن چین کوڈ Solidityopens in a new tab میں لکھا گیا ہے۔ یہاں آپٹیمائزیشن بہت زیادہ اہم ہے کیونکہ گیس نسبتاً مہنگی ہے۔

1//SPDX-License-Identifier: Public Domain
2pragma solidity ^0.8.0;
3
4import "hardhat/console.sol";

میں نے یہ Hardhat ڈیولپمنٹ انوائرمنٹopens in a new tab کا استعمال کرتے ہوئے لکھا ہے، جو ہمیں ڈیولپمنٹ کے دوران Solidity سے کنسول آؤٹ پٹopens in a new tab حاصل کرنے کی اجازت دیتا ہے۔

1
2contract MerkleProof {
3    uint merkleRoot;
4
5    function getRoot() public view returns (uint) {
6      return merkleRoot;
7    }
8
9    // Extremely insecure, in production code access to
10    // this function MUST BE strictly limited, probably to an
11    // owner
12    function setRoot(uint _merkleRoot) external {
13      merkleRoot = _merkleRoot;
14    }   // setRoot
سب دکھائیں

مرکل روٹ کے لیے سیٹ اور گیٹ فنکشنز۔ پروڈکشن سسٹم میں ہر کسی کو مرکل روٹ کو اپ ڈیٹ کرنے کی اجازت دینا ایک انتہائی برا خیال ہے۔ میں یہاں نمونہ کوڈ کی سادگی کی خاطر ایسا کرتا ہوں۔ اسے ایسے سسٹم پر نہ کریں جہاں ڈیٹا کی سالمیت واقعی اہمیت رکھتی ہو۔

1    function hash(uint _a) internal pure returns(uint) {
2      return uint(keccak256(abi.encode(_a)));
3    }
4
5    function pairHash(uint _a, uint _b) internal pure returns(uint) {
6      return hash(hash(_a) ^ hash(_b));
7    }

یہ فنکشن ایک جوڑے کا ہیش بناتا ہے۔ یہ hash اور pairHash کے لیے JavaScript کوڈ کا صرف Solidity ترجمہ ہے۔

نوٹ: یہ پڑھنے کی اہلیت کے لیے آپٹیمائزیشن کا ایک اور معاملہ ہے۔ فنکشن کی تعریفopens in a new tab کی بنیاد پر، ڈیٹا کو bytes32opens in a new tab قدر کے طور پر اسٹور کرنا اور تبادلوں سے بچنا ممکن ہو سکتا ہے۔

1    // Verify a Merkle proof
2    function verifyProof(uint _value, uint[] calldata _proof)
3        public view returns (bool) {
4      uint temp = _value;
5      uint i;
6
7      for(i=0; i<_proof.length; i++) {
8        temp = pairHash(temp, _proof[i]);
9      }
10
11      return temp == merkleRoot;
12    }
13
14}  // MarkleProof
سب دکھائیں

ریاضیاتی اشارے میں مرکل پروف کی تصدیق اس طرح نظر آتی ہے: H(proof_n, H(proof_n-1, H(proof_n-2, ... H(proof_1, H(proof_0, value))...)))`۔ یہ کوڈ اسے نافذ کرتا ہے۔

مرکل پروفس اور رول اپس آپس میں نہیں ملتے

مرکل پروفس رول اپس کے ساتھ اچھی طرح کام نہیں کرتے ہیں۔ اس کی وجہ یہ ہے کہ رول اپس تمام ٹرانزیکشن ڈیٹا L1 پر لکھتے ہیں، لیکن L2 پر پروسیس کرتے ہیں۔ ایک ٹرانزیکشن کے ساتھ مرکل پروف بھیجنے کی لاگت اوسطاً 638 گیس فی لیئر ہے (فی الحال کال ڈیٹا میں ایک بائٹ کی لاگت 16 گیس ہے اگر یہ صفر نہیں ہے، اور 4 ہے اگر یہ صفر ہے)۔ اگر ہمارے پاس 1024 الفاظ کا ڈیٹا ہے، تو مرکل پروف کے لیے دس لیئرز، یا کل 6380 گیس کی ضرورت ہوتی ہے۔

مثال کے طور پر Optimismopens in a new tab کو دیکھیں، L1 گیس لکھنے پر تقریباً 100 gwei اور L2 گیس پر 0.001 gwei لاگت آتی ہے (یہ عام قیمت ہے، یہ بھیڑ کے ساتھ بڑھ سکتی ہے)۔ لہذا ایک L1 گیس کی لاگت پر ہم L2 پروسیسنگ پر ایک لاکھ گیس خرچ کر سکتے ہیں۔ یہ فرض کرتے ہوئے کہ ہم اسٹوریج کو اوور رائٹ نہیں کرتے ہیں، اس کا مطلب ہے کہ ہم ایک L1 گیس کی قیمت پر L2 پر اسٹوریج میں تقریباً پانچ الفاظ لکھ سکتے ہیں۔ ایک مرکل پروف کے لیے ہم پورے 1024 الفاظ اسٹوریج میں لکھ سکتے ہیں (یہ فرض کرتے ہوئے کہ انہیں شروع میں آن چین کیلکولیٹ کیا جا سکتا ہے، بجائے اس کے کہ ٹرانزیکشن میں فراہم کیا جائے) اور پھر بھی زیادہ تر گیس بچی رہے گی۔

نتیجہ

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

نوٹ کریں کہ جب مرکل پروفس سالمیت کو محفوظ رکھتے ہیں، وہ دستیابی کو محفوظ نہیں رکھتے ہیں۔ یہ جاننا کہ کوئی اور آپ کے اثاثے نہیں لے سکتا، یہ چھوٹی سی تسلی ہے اگر ڈیٹا اسٹوریج رسائی کو مسترد کرنے کا فیصلہ کرتا ہے اور آپ ان تک رسائی کے لیے مرکل ٹری بھی نہیں بنا سکتے ہیں۔ لہذا مرکل ٹریز کو کسی قسم کے وکندریقرت اسٹوریج، جیسے IPFS کے ساتھ بہترین طریقے سے استعمال کیا جاتا ہے۔

میرے مزید کام کے لیے یہاں دیکھیںopens in a new tab۔

صفحہ کی آخری تازہ کاری: 18 دسمبر، 2025

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