ఆఫ్లైన్ డేటా సమగ్రత కోసం మెర్కల్ రుజువులు
పరిచయం
ఆదర్శవంతంగా మనం ప్రతిదీ ఎథీరియం నిల్వలో నిల్వ చేయాలనుకుంటాము, ఇది వేలాది కంప్యూటర్లలో నిల్వ చేయబడుతుంది మరియు అత్యంత అధిక లభ్యత (డేటాను సెన్సార్ చేయలేము) మరియు సమగ్రతను (డేటాను అనధికారిక పద్ధతిలో సవరించలేము) కలిగి ఉంటుంది, కానీ 32-బైట్ పదాన్ని నిల్వ చేయడానికి సాధారణంగా 20,000 గ్యాస్ ఖర్చవుతుంది. నేను దీనిని వ్రాస్తున్నప్పుడు, ఆ ఖర్చు $6.60కి సమానం. బైట్కు 21 సెంట్ల చొప్పున ఇది అనేక ఉపయోగాలకు చాలా ఖరీదైనది.
ఈ సమస్యను పరిష్కరించడానికి ఎథీరియం పర్యావరణ వ్యవస్థ వికేంద్రీకృత పద్ధతిలో డేటాను నిల్వ చేయడానికి అనేక ప్రత్యామ్నాయ మార్గాలను అభివృద్ధి చేసింది. సాధారణంగా అవి లభ్యత మరియు ధర మధ్య రాజీని కలిగి ఉంటాయి. అయితే, సమగ్రత సాధారణంగా హామీ ఇవ్వబడుతుంది.
ఈ కథనంలో మీరు మెర్కల్ రుజువులు (opens in a new tab) ఉపయోగించి, బ్లాక్చైన్లో డేటాను నిల్వ చేయకుండా డేటా సమగ్రతను ఎలా నిర్ధారించాలో నేర్చుకుంటారు.
ఇది ఎలా పనిచేస్తుంది?
సిద్ధాంతపరంగా మనం డేటా యొక్క హాష్ను ఆన్చైన్లో నిల్వ చేయవచ్చు మరియు అవసరమైన లావాదేవీలలో మొత్తం డేటాను పంపవచ్చు. అయితే, ఇది ఇప్పటికీ చాలా ఖరీదైనది. లావాదేవీకి ఒక బైట్ డేటాకు సుమారు 16 గ్యాస్ ఖర్చవుతుంది, ప్రస్తుతం ఇది సుమారు అర సెంట్ లేదా కిలోబైట్కు సుమారు $5. మెగాబైట్కు $5000 చొప్పున, డేటాను హాషింగ్ చేసే అదనపు ఖర్చు లేకుండా కూడా ఇది అనేక ఉపయోగాలకు చాలా ఖరీదైనది.
దీనికి పరిష్కారం డేటా యొక్క విభిన్న ఉపసమితులను పదేపదే హాష్ చేయడం, కాబట్టి మీరు పంపాల్సిన అవసరం లేని డేటా కోసం మీరు కేవలం హాష్ను పంపవచ్చు. మీరు దీనిని మెర్కిల్ వృక్షం ఉపయోగించి చేస్తారు, ఇది ఒక ట్రీ డేటా స్ట్రక్చర్, ఇక్కడ ప్రతి నోడ్ దాని క్రింద ఉన్న నోడ్ల హాష్ అవుతుంది:
రూట్ హాష్ మాత్రమే ఆన్చైన్లో నిల్వ చేయాల్సిన ఏకైక భాగం. ఒక నిర్దిష్ట విలువను నిరూపించడానికి, రూట్ను పొందడానికి దానితో కలపాల్సిన అన్ని హాష్లను మీరు అందిస్తారు. ఉదాహరణకు, C నిరూపించడానికి మీరు D, H(A-B), మరియు H(E-H) అందిస్తారు.
అమలు
నమూనా కోడ్ ఇక్కడ అందించబడింది (opens in a new tab).
ఆఫ్చైన్ కోడ్
ఈ కథనంలో మనం ఆఫ్చైన్ గణనల కోసం JavaScript ఉపయోగిస్తాము. చాలా వికేంద్రీకృత అప్లికేషన్లు వాటి ఆఫ్చైన్ భాగాన్ని JavaScriptలో కలిగి ఉంటాయి.
మెర్కల్ రూట్ను సృష్టించడం
ముందుగా మనం చైన్కు మెర్కల్ రూట్ను అందించాలి.
const ethers = require("ethers")
మనం ethers ప్యాకేజీ నుండి హాష్ ఫంక్షన్ను ఉపయోగిస్తాము (opens in a new tab).
// మనం సమగ్రతను ధృవీకరించాల్సిన ముడి డేటా. మొదటి రెండు బైట్లు ఒక
// వినియోగదారు ఐడెంటిఫైయర్, మరియు చివరి రెండు బైట్లు
// వినియోగదారు ప్రస్తుతం కలిగి ఉన్న టోకెన్ల మొత్తం.
const dataArray = [
0x0bad0010, 0x60a70020, 0xbeef0030, 0xdead0040, 0xca110050, 0x0e660060,
0xface0070, 0xbad00080, 0x060d0091,
]
ప్రతి ఎంట్రీని ఒకే 256-బిట్ పూర్ణాంకంగా ఎన్కోడ్ చేయడం వలన ఉదాహరణకు, JSON ఉపయోగించడం కంటే తక్కువ చదవగలిగే కోడ్ వస్తుంది. అయితే, దీని అర్థం కాంట్రాక్ట్లో డేటాను తిరిగి పొందడానికి గణనీయంగా తక్కువ ప్రాసెసింగ్ అవసరం, కాబట్టి గ్యాస్ ఖర్చులు చాలా తక్కువగా ఉంటాయి. మీరు ఆన్చైన్లో JSON చదవగలరు (opens in a new tab), కానీ వీలైతే దానిని నివారించడం మంచిది.
// హాష్ విలువల శ్రేణి, BigIntలుగా
const hashArray = dataArray
ఈ సందర్భంలో మన డేటా ప్రారంభించడానికి 256-బిట్ విలువలు, కాబట్టి ఎటువంటి ప్రాసెసింగ్ అవసరం లేదు. మనం స్ట్రింగ్స్ వంటి మరింత సంక్లిష్టమైన డేటా స్ట్రక్చర్ను ఉపయోగిస్తే, హాష్ల శ్రేణిని పొందడానికి ముందుగా డేటాను హాష్ చేసేలా చూసుకోవాలి. వినియోగదారులకు ఇతర వినియోగదారుల సమాచారం తెలిసినా మనం పట్టించుకోము కాబట్టి ఇది కూడా గమనించండి. లేకపోతే వినియోగదారు 1కి వినియోగదారు 0 విలువ తెలియకుండా, వినియోగదారు 2కి వినియోగదారు 3 విలువ తెలియకుండా, మొదలైన విధంగా మనం హాష్ చేయాల్సి వచ్చేది.
// హాష్ ఫంక్షన్ ఆశించే స్ట్రింగ్ మరియు
// మనం మిగతా అన్ని చోట్లా ఉపయోగించే BigInt మధ్య మార్చండి.
const hash = (x) =>
BigInt(ethers.utils.keccak256("0x" + x.toString(16).padStart(64, 0)))
ethers హాష్ ఫంక్షన్ 0x60A7 వంటి హెక్సాడెసిమల్ సంఖ్యతో కూడిన JavaScript స్ట్రింగ్ను పొందుతుందని ఆశిస్తుంది మరియు అదే నిర్మాణంతో ఉన్న మరొక స్ట్రింగ్తో ప్రతిస్పందిస్తుంది. అయితే, మిగిలిన కోడ్ కోసం BigInt ఉపయోగించడం సులభం, కాబట్టి మనం హెక్సాడెసిమల్ స్ట్రింగ్గా మార్చి మళ్లీ వెనక్కి మారుస్తాము.
// ఒక జత యొక్క సుష్ట హాష్, కాబట్టి క్రమం తారుమారైనా మనం పట్టించుకోము.
const pairHash = (a, b) => hash(hash(a) ^ hash(b))
ఈ ఫంక్షన్ సౌష్టవంగా ఉంటుంది (a xor (opens in a new tab) b యొక్క హాష్). దీని అర్థం మనం మెర్కల్ రుజువును తనిఖీ చేసినప్పుడు, రుజువు నుండి విలువను లెక్కించిన విలువకు ముందు ఉంచాలా లేదా తర్వాత ఉంచాలా అని మనం చింతించాల్సిన అవసరం లేదు. మెర్కల్ రుజువు తనిఖీ ఆన్చైన్లో చేయబడుతుంది, కాబట్టి మనం అక్కడ ఎంత తక్కువ చేస్తే అంత మంచిది.
హెచ్చరిక:
గూఢలిపి శాస్త్రం కనిపించే దానికంటే కష్టమైనది.
ఈ కథనం యొక్క ప్రారంభ వెర్షన్లో hash(a^b) హాష్ ఫంక్షన్ ఉంది.
అది ఒక చెడ్డ ఆలోచన ఎందుకంటే మీకు a మరియు b యొక్క చట్టబద్ధమైన విలువలు తెలిస్తే, మీరు కోరుకున్న ఏదైనా a' విలువను నిరూపించడానికి b' = a^b^a' ఉపయోగించవచ్చని దీని అర్థం.
ఈ ఫంక్షన్తో మీరు hash(a') ^ hash(b') తెలిసిన విలువకు (రూట్కు వెళ్లే మార్గంలో తదుపరి శాఖ) సమానంగా ఉండేలా b' లెక్కించాల్సి ఉంటుంది, ఇది చాలా కష్టం.
// ఒక నిర్దిష్ట శాఖ ఖాళీగా ఉందని సూచించే విలువ, దానికి
// విలువ లేదు
const empty = 0n
విలువల సంఖ్య రెండు యొక్క పూర్ణాంక ఘాతం కానప్పుడు మనం ఖాళీ శాఖలను నిర్వహించాలి. ఈ ప్రోగ్రామ్ దీన్ని చేసే విధానం ఏమిటంటే సున్నాను ప్లేస్ హోల్డర్గా ఉంచడం.
// హాష్ శ్రేణి యొక్క వృక్షంలో ఒక స్థాయి పైకి లెక్కించండి, దీని కోసం
// ప్రతి జత యొక్క హాష్ను వరుసగా తీసుకోవాలి
const oneLevelUp = (inputArray) => {
var result = []
var inp = [...inputArray] // ఇన్పుట్ను ఓవర్రైట్ చేయడాన్ని నివారించడానికి // అవసరమైతే ఖాళీ విలువను జోడించండి (ఆకులన్నీ // జత చేయబడాలి)
if (inp.length % 2 === 1) inp.push(empty)
for (var i = 0; i < inp.length; i += 2)
result.push(pairHash(inp[i], inp[i + 1]))
return result
} // oneLevelUp
ఈ ఫంక్షన్ ప్రస్తుత లేయర్లోని విలువల జతలను హాషింగ్ చేయడం ద్వారా మెర్కిల్ వృక్షంలో ఒక స్థాయిని "ఎక్కుతుంది". ఇది అత్యంత సమర్థవంతమైన అమలు కాదని గమనించండి, మనం ఇన్పుట్ను కాపీ చేయడాన్ని నివారించి ఉండవచ్చు మరియు లూప్లో తగినప్పుడు hashEmpty జోడించి ఉండవచ్చు, కానీ ఈ కోడ్ చదవడానికి వీలుగా ఆప్టిమైజ్ చేయబడింది.
const getMerkleRoot = (inputArray) => {
var result
result = [...inputArray] // ఒకే ఒక విలువ, అంటే రూట్ మిగిలే వరకు వృక్షం పైకి ఎక్కండి. // // ఒక లేయర్లో బేసి సంఖ్యలో ఎంట్రీలు ఉంటే // oneLevelUp లోని కోడ్ ఖాళీ విలువను జోడిస్తుంది, కాబట్టి ఉదాహరణకు, మనకు // 10 ఆకులు ఉంటే రెండవ లేయర్లో 5 శాఖలు, మూడవ లేయర్లో 3 // శాఖలు, నాల్గవ లేయర్లో 2 మరియు ఐదవది రూట్ అవుతుంది
while (result.length > 1) result = oneLevelUp(result)
return result[0]
}
రూట్ను పొందడానికి, ఒకే ఒక విలువ మిగిలి ఉండే వరకు ఎక్కండి.
మెర్కల్ రుజువును సృష్టించడం
మెర్కల్ రుజువు అనేది మెర్కల్ రూట్ను తిరిగి పొందడానికి నిరూపించబడుతున్న విలువతో పాటు హాష్ చేయాల్సిన విలువలు. నిరూపించాల్సిన విలువ తరచుగా ఇతర డేటా నుండి అందుబాటులో ఉంటుంది, కాబట్టి నేను దానిని కోడ్లో భాగంగా కాకుండా విడిగా అందించడానికి ఇష్టపడతాను.
// ఒక మెర్కల్ రుజువు హాష్ చేయడానికి ఎంట్రీల జాబితా యొక్క విలువను
// కలిగి ఉంటుంది. మనం సుష్ట హాష్ ఫంక్షన్ను ఉపయోగిస్తున్నందున, రుజువును ధృవీకరించడానికి
// ఐటెమ్ యొక్క స్థానం మనకు అవసరం లేదు, దానిని సృష్టించడానికి మాత్రమే అవసరం
const getMerkleProof = (inputArray, n) => {
var result = [], currentLayer = [...inputArray], currentN = n
// మనం పైకి చేరుకునే వరకు
while (currentLayer.length > 1) {
// బేసి పొడవు గల లేయర్లు లేవు
if (currentLayer.length % 2)
currentLayer.push(empty)
result.push(currentN % 2
// currentN బేసి సంఖ్య అయితే, దానికి ముందున్న విలువతో రుజువుకు జోడించండి
? currentLayer[currentN-1]
// అది సరి సంఖ్య అయితే, దాని తర్వాత ఉన్న విలువను జోడించండి
: currentLayer[currentN+1])
మనం (v[0],v[1]), (v[2],v[3]), మొదలైన వాటిని హాష్ చేస్తాము. కాబట్టి సరి విలువల కోసం మనకు తదుపరిది అవసరం, బేసి విలువల కోసం మునుపటిది అవసరం.
// పైనున్న తదుపరి లేయర్కు వెళ్లండి
currentN = Math.floor(currentN/2)
currentLayer = oneLevelUp(currentLayer)
} // currentLayer.length > 1 ఉన్నంత వరకు
return result
} // getMerkleProof
ఆన్చైన్ కోడ్
చివరగా మనకు రుజువును తనిఖీ చేసే కోడ్ ఉంది. ఆన్చైన్ కోడ్ Solidity (opens in a new tab)లో వ్రాయబడింది. గ్యాస్ సాపేక్షంగా ఖరీదైనది కాబట్టి ఇక్కడ ఆప్టిమైజేషన్ చాలా ముఖ్యం.
//SPDX-License-Identifier: Public Domain
pragma solidity ^0.8.0;
import "hardhat/console.sol";
నేను దీనిని Hardhat డెవలప్మెంట్ ఎన్విరాన్మెంట్ (opens in a new tab) ఉపయోగించి వ్రాసాను, ఇది అభివృద్ధి చేస్తున్నప్పుడు Solidity నుండి కన్సోల్ అవుట్పుట్ (opens in a new tab) కలిగి ఉండటానికి అనుమతిస్తుంది.
contract MerkleProof {
uint merkleRoot;
function getRoot() public view returns (uint) {
return merkleRoot;
}
// అత్యంత అసురక్షితమైనది, ప్రొడక్షన్ కోడ్లో దీనికి యాక్సెస్
// ఈ ఫంక్షన్ ఖచ్చితంగా పరిమితం చేయబడాలి, బహుశా ఒక
// యజమానికి
function setRoot(uint _merkleRoot) external {
merkleRoot = _merkleRoot;
} // setRoot
మెర్కల్ రూట్ కోసం సెట్ మరియు గెట్ ఫంక్షన్లు. ప్రొడక్షన్ సిస్టమ్లో మెర్కల్ రూట్ను అప్డేట్ చేయడానికి ప్రతి ఒక్కరినీ అనుమతించడం అనేది చాలా చెడ్డ ఆలోచన. నమూనా కోడ్ కోసం సరళత కోసం నేను ఇక్కడ అలా చేస్తున్నాను. డేటా సమగ్రత వాస్తవానికి ముఖ్యమైన సిస్టమ్లో అలా చేయవద్దు.
function hash(uint _a) internal pure returns(uint) {
return uint(keccak256(abi.encode(_a)));
}
function pairHash(uint _a, uint _b) internal pure returns(uint) {
return hash(hash(_a) ^ hash(_b));
}
ఈ ఫంక్షన్ పెయిర్ హాష్ను ఉత్పత్తి చేస్తుంది. ఇది కేవలం hash మరియు pairHash కోసం JavaScript కోడ్ యొక్క Solidity అనువాదం.
గమనిక: ఇది చదవడానికి వీలుగా ఆప్టిమైజేషన్ చేసిన మరొక సందర్భం. ఫంక్షన్ నిర్వచనం (opens in a new tab) ఆధారంగా, డేటాను bytes32 (opens in a new tab) విలువగా నిల్వ చేయడం మరియు మార్పిడులను నివారించడం సాధ్యం కావచ్చు.
// మెర్కల్ రుజువును ధృవీకరించండి
function verifyProof(uint _value, uint[] calldata _proof)
public view returns (bool) {
uint temp = _value;
uint i;
for(i=0; i<_proof.length; i++) {
temp = pairHash(temp, _proof[i]);
}
return temp == merkleRoot;
}
} // MarkleProof
గణిత సంజ్ఞామానంలో మెర్కల్ రుజువు ధృవీకరణ ఇలా కనిపిస్తుంది: H(proof_n, H(proof_n-1, H(proof_n-2, ... H(proof_1, H(proof_0, value))...))). ఈ కోడ్ దానిని అమలు చేస్తుంది.
మెర్కల్ రుజువులు మరియు రోల్అప్లు కలవవు
మెర్కల్ రుజువులు రోల్అప్లుతో సరిగ్గా పనిచేయవు. దీనికి కారణం రోల్అప్లు మొత్తం లావాదేవీ డేటాను లేయర్ 1 (l1)లో వ్రాస్తాయి, కానీ లేయర్ 2 (l2)లో ప్రాసెస్ చేస్తాయి. లావాదేవీతో మెర్కల్ రుజువును పంపడానికి అయ్యే ఖర్చు సగటున లేయర్కు 638 గ్యాస్ (ప్రస్తుతం కాల్ డేటాలో ఒక బైట్ సున్నా కాకపోతే 16 గ్యాస్, మరియు సున్నా అయితే 4 గ్యాస్ ఖర్చవుతుంది). మనకు 1024 పదాల డేటా ఉంటే, మెర్కల్ రుజువుకు పది లేయర్లు లేదా మొత్తం 6380 గ్యాస్ అవసరం.
ఉదాహరణకు Optimism (opens in a new tab)ని పరిశీలిస్తే, లేయర్ 1 (l1) గ్యాస్ వ్రాయడానికి సుమారు 100 Gwei ఖర్చవుతుంది మరియు లేయర్ 2 (l2) గ్యాస్ ఖర్చు 0.001 Gwei (అది సాధారణ ధర, రద్దీతో ఇది పెరగవచ్చు). కాబట్టి ఒక లేయర్ 1 (l1) గ్యాస్ ఖర్చుతో మనం లేయర్ 2 (l2) ప్రాసెసింగ్పై లక్ష గ్యాస్ ఖర్చు చేయవచ్చు. మనం నిల్వను ఓవర్రైట్ చేయమని ఊహిస్తే, ఒక లేయర్ 1 (l1) గ్యాస్ ధరకు మనం లేయర్ 2 (l2)లో నిల్వ చేయడానికి సుమారు ఐదు పదాలను వ్రాయగలమని దీని అర్థం. ఒకే మెర్కల్ రుజువు కోసం మనం మొత్తం 1024 పదాలను నిల్వ చేయడానికి వ్రాయవచ్చు (వాటిని లావాదేవీలో అందించడానికి బదులుగా, ప్రారంభించడానికి ఆన్చైన్లో లెక్కించవచ్చని ఊహిస్తే) మరియు ఇప్పటికీ చాలా గ్యాస్ మిగిలి ఉంటుంది.
ముగింపు
నిజ జీవితంలో మీరు ఎప్పుడూ మీ స్వంతంగా మెర్కిల్ వృక్షాలను అమలు చేయకపోవచ్చు. మీరు ఉపయోగించగల ప్రసిద్ధ మరియు ఆడిట్ చేయబడిన లైబ్రరీలు ఉన్నాయి మరియు సాధారణంగా చెప్పాలంటే మీ స్వంతంగా క్రిప్టోగ్రాఫిక్ ప్రిమిటివ్లను అమలు చేయకపోవడమే ఉత్తమం. కానీ ఇప్పుడు మీరు మెర్కల్ రుజువులను బాగా అర్థం చేసుకున్నారని మరియు వాటిని ఎప్పుడు ఉపయోగించడం విలువైనదో నిర్ణయించగలరని నేను ఆశిస్తున్నాను.
మెర్కల్ రుజువులు సమగ్రతను సంరక్షిస్తున్నప్పటికీ, అవి లభ్యతను సంరక్షించవని గమనించండి. డేటా నిల్వ యాక్సెస్ను అనుమతించకూడదని నిర్ణయించుకుంటే మరియు వాటిని యాక్సెస్ చేయడానికి మీరు మెర్కిల్ వృక్షాన్ని నిర్మించలేకపోతే, మీ ఆస్తులను మరెవరూ తీసుకోలేరని తెలుసుకోవడం చిన్న ఓదార్పు మాత్రమే. కాబట్టి IPFS వంటి ఒక రకమైన వికేంద్రీకృత నిల్వతో మెర్కిల్ వృక్షాలను ఉత్తమంగా ఉపయోగిస్తారు.


