ప్రధాన కంటెంట్‌కి స్కిప్ చేయండి

మీరు కాష్ చేయగలవన్నీ

లేయర్ 2
కాషింగ్
స్టోరేజ్
మధ్యస్థ
Ori Pomerantz
15 సెప్టెంబర్, 2022
19 నిమిషం పఠనం

రోల్అప్స్ ఉపయోగిస్తున్నప్పుడు, లావాదేవీలో ఒక బైట్ ఖర్చు నిల్వ స్లాట్ ఖర్చు కంటే చాలా ఎక్కువ. అందువల్ల, వీలైనంత ఎక్కువ సమాచారాన్ని ఆన్‌చెయిన్‌లో కాష్ చేయడం అర్ధవంతంగా ఉంటుంది.

ఈ వ్యాసంలో మీరు కాషింగ్ కాంట్రాక్ట్‌ను సృష్టించడం మరియు ఉపయోగించడం ఎలాగో నేర్చుకుంటారు. దీని ద్వారా చాలాసార్లు ఉపయోగించబడే ఏవైనా పారామీటర్ విలువ కాష్ చేయబడుతుంది మరియు (మొదటిసారి తర్వాత) చాలా తక్కువ సంఖ్యలో బైట్లతో ఉపయోగం కోసం అందుబాటులో ఉంటుంది. అలాగే ఈ కాష్‌ను ఉపయోగించే ఆఫ్‌చెయిన్ కోడ్‌ను ఎలా రాయాలో కూడా నేర్చుకుంటారు.

మీరు వ్యాసాన్ని దాటవేసి, సోర్స్ కోడ్‌ను చూడాలనుకుంటే, అది ఇక్కడ ఉంది (opens in a new tab). డెవలప్‌మెంట్ స్టాక్ Foundry (opens in a new tab).

మొత్తం రూపకల్పన

సరళత కోసం అన్ని లావాదేవీ పారామితులు uint256, 32 బైట్స్ పొడవు ఉంటాయని మేము ఊహిస్తాము. మేము ఒక లావాదేవీని స్వీకరించినప్పుడు, ప్రతి పారామీటర్‌ను ఇలా పార్స్ చేస్తాము:

  1. మొదటి బైట్ 0xFF అయితే, తదుపరి 32 బైట్స్‌ను పారామీటర్ విలువగా తీసుకొని కాష్‌లో రాయండి.

  2. మొదటి బైట్ 0xFE అయితే, తదుపరి 32 బైట్స్‌ను పారామీటర్ విలువగా తీసుకోండి, కానీ దానిని కాష్‌లో రాయవద్దు.

  3. ఏదైనా ఇతర విలువ కోసం, అదనపు బైట్ల సంఖ్యగా మొదటి నాలుగు బిట్‌లను, మరియు కాష్ కీ యొక్క అత్యంత ముఖ్యమైన బిట్‌లుగా చివరి నాలుగు బిట్‌లను తీసుకోండి. ఇక్కడ కొన్ని ఉదాహరణలు ఉన్నాయి:

    కాల్‌డేటాలో బైట్స్కాష్ కీ
    0x0F0x0F
    0x10,0x100x10
    0x12,0xAC0x02AC
    0x2D,0xEA, 0xD60x0DEAD6

కాష్ మార్పు

కాష్ Cache.sol (opens in a new tab)లో అమలు చేయబడింది. దాన్ని లైన్ వారీగా చూద్దాం.

1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.13;
3
4
5contract Cache {
6
7 bytes1 public constant INTO_CACHE = 0xFF;
8 bytes1 public constant DONT_CACHE = 0xFE;

మేము మొత్తం సమాచారాన్ని అందించిన ప్రత్యేక సందర్భాలను అర్థం చేసుకోవడానికి ఈ స్థిరాంకాలు ఉపయోగించబడతాయి మరియు దానిని కాష్‌లో వ్రాయాలనుకుంటున్నామా లేదా అనేది తెలియజేయడానికి. కాష్ లోకి రాయడానికి ఇంతకుముందు ఉపయోగించని నిల్వ స్లాట్‌లలోకి రెండు SSTORE (opens in a new tab) ఆపరేషన్లు అవసరం, ఒక్కొక్క దానికి 22100 గ్యాస్ ఖర్చు అవుతుంది, కనుక మేము దీన్ని ఐచ్ఛికం చేసాము.

1
2 mapping(uint => uint) public val2key;

విలువలు మరియు వాటి కీలకు మధ్య ఒక మ్యాపింగ్ (opens in a new tab). మీరు లావాదేవీని పంపే ముందు విలువలను ఎన్కోడ్ చేయడానికి ఈ సమాచారం అవసరం.

1 // స్థానం n కి కీ n+1 కోసం విలువ ఉంటుంది, ఎందుకంటే మనం సున్నాను
2 // "కాష్‌లో లేదు" అని భద్రపరచాలి.
3 uint[] public key2val;

కీల నుండి విలువలకు మ్యాపింగ్ కోసం మేము ఒక శ్రేణిని ఉపయోగించవచ్చు, ఎందుకంటే మేము కీలను కేటాయిస్తాము మరియు సరళత కోసం మేము దానిని వరుసగా చేస్తాము.

1 function cacheRead(uint _key) public view returns (uint) {
2 require(_key <= key2val.length, "Reading uninitialize cache entry");
3 return key2val[_key-1];
4 } // cacheRead

కాష్ నుండి ఒక విలువను చదవండి.

1 // ఒక విలువ ఇప్పటికే కాష్‌లో లేకపోతే దాన్ని రాయండి
2 // పరీక్ష పని చేయడానికి మాత్రమే పబ్లిక్
3 function cacheWrite(uint _value) public returns (uint) {
4 // విలువ ఇప్పటికే కాష్‌లో ఉంటే, ప్రస్తుత కీని తిరిగి ఇవ్వండి
5 if (val2key[_value] != 0) {
6 return val2key[_value];
7 }

ఒకే విలువను కాష్‌లో ఒకటి కంటే ఎక్కువసార్లు ఉంచడంలో అర్థం లేదు. విలువ ఇప్పటికే అక్కడ ఉంటే, ఇప్పటికే ఉన్న కీని తిరిగి ఇవ్వండి.

1 // 0xFE ఒక ప్రత్యేక సందర్భం కాబట్టి, కాష్ పట్టుకోగల అతిపెద్ద కీ
2 // 0x0D తర్వాత 15 0xFFలు ఉంటాయి. కాష్ పొడవు ఇప్పటికే అంత
3 // పెద్దగా ఉంటే, విఫలమవుతుంది.
4 // 1 2 3 4 5 6 7 8 9 A B C D E F
5 require(key2val.length+1 < 0x0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
6 "కాష్ ఓవర్‌ఫ్లో");

అంత పెద్ద కాష్ మనకు ఎప్పటికీ లభిస్తుందని నేను అనుకోను (సుమారు 1.8*1037 ఎంట్రీలు, దీనికి నిల్వ చేయడానికి సుమారు 1027 TB అవసరం). అయితే, "640kB ఎల్లప్పుడూ సరిపోతుంది" (opens in a new tab) అని గుర్తుంచుకోవడానికి నేను తగినంత పెద్దవాడిని. ఈ పరీక్ష చాలా చౌక.

1 // తదుపరి కీని ఉపయోగించి విలువను రాయండి
2 val2key[_value] = key2val.length+1;

రివర్స్ లుకప్‌ను జోడించండి (విలువ నుండి కీకి).

1 key2val.push(_value);

ఫార్వర్డ్ లుకప్‌ను జోడించండి (కీ నుండి విలువకి). మేము విలువలను వరుసగా కేటాయిస్తాము కాబట్టి, మేము దానిని చివరి శ్రేణి విలువ తర్వాత జోడించవచ్చు.

1 return key2val.length;
2 } // cacheWrite

key2val యొక్క కొత్త పొడవును తిరిగి ఇవ్వండి, ఇది కొత్త విలువ నిల్వ చేయబడిన సెల్.

1 function _calldataVal(uint startByte, uint length)
2 private pure returns (uint)

ఈ ఫంక్షన్ ఏకపక్ష పొడవు (32 బైట్ల వరకు, వర్డ్ సైజు) యొక్క కాల్‌డేటా నుండి ఒక విలువను చదువుతుంది.

1 {
2 uint _retVal;
3
4 require(length < 0x21,
5 "_calldataVal పొడవు పరిమితి 32 బైట్లు");
6 require(length + startByte <= msg.data.length,
7 "_calldataVal కాల్‌డేటాసైజ్ దాటి చదవడానికి ప్రయత్నిస్తోంది");

ఈ ఫంక్షన్ అంతర్గతమైనది, కాబట్టి మిగిలిన కోడ్ సరిగ్గా వ్రాయబడితే ఈ పరీక్షలు అవసరం లేదు. అయితే, వాటికి ఎక్కువ ఖర్చు కాదు కాబట్టి మనం వాటిని కలిగి ఉండవచ్చు.

1 assembly {
2 _retVal := calldataload(startByte)
3 }

ఈ కోడ్ Yul (opens in a new tab) లో ఉంది. ఇది కాల్‌డేటా నుండి 32 బైట్ విలువను చదువుతుంది. EVMలో ప్రారంభించని ఖాళీ సున్నాగా పరిగణించబడుతుంది కాబట్టి, కాల్‌డేటా startByte+32 కి ముందు ఆగిపోయినా ఇది పనిచేస్తుంది.

1 _retVal = _retVal >> (256-length*8);

మాకు తప్పనిసరిగా 32 బైట్ విలువ అవసరం లేదు. ఇది అదనపు బైట్లను తొలగిస్తుంది.

1 return _retVal;
2 } // _calldataVal
3
4
5 // _fromByte నుండి ప్రారంభించి, కాల్‌డేటా నుండి ఒకే పారామీటర్‌ను చదవండి
6 function _readParam(uint _fromByte) internal
7 returns (uint _nextByte, uint _parameterValue)
8 {

కాల్‌డేటా నుండి ఒకే పారామీటర్‌ను చదవండి. గమనించండి, మనం చదివిన విలువను మాత్రమే కాకుండా, తదుపరి బైట్ యొక్క స్థానాన్ని కూడా తిరిగి ఇవ్వాలి, ఎందుకంటే పారామితులు 1 బైట్ నుండి 33 బైట్ల వరకు ఉండవచ్చు.

1 // మొదటి బైట్ మిగిలిన వాటిని ఎలా అర్థం చేసుకోవాలో చెబుతుంది
2 uint8 _firstByte;
3
4 _firstByte = uint8(_calldataVal(_fromByte, 1));

Solidity సంభావ్య ప్రమాదకరమైన అంతర్లీన రకం మార్పిడులను (opens in a new tab) నిషేధించడం ద్వారా బగ్‌ల సంఖ్యను తగ్గించడానికి ప్రయత్నిస్తుంది. ఉదాహరణకు 256 బిట్స్ నుండి 8 బిట్స్ కి డౌన్‌గ్రేడ్ చేయడం స్పష్టంగా ఉండాలి.

1
2 // విలువను చదవండి, కానీ దాన్ని కాష్‌లో రాయవద్దు
3 if (_firstByte == uint8(DONT_CACHE))
4 return(_fromByte+33, _calldataVal(_fromByte+1, 32));
5
6 // విలువను చదవండి, మరియు దాన్ని కాష్‌లో రాయండి
7 if (_firstByte == uint8(INTO_CACHE)) {
8 uint _param = _calldataVal(_fromByte+1, 32);
9 cacheWrite(_param);
10 return(_fromByte+33, _param);
11 }
12
13 // మనం ఇక్కడకు వచ్చామంటే మనం కాష్ నుండి చదవాలి
14
15 // చదవడానికి అదనపు బైట్ల సంఖ్య
16 uint8 _extraBytes = _firstByte / 16;
అన్నీ చూపించు

తక్కువ నిబుల్ (opens in a new tab) తీసుకొని, కాష్ నుండి విలువను చదవడానికి ఇతర బైట్లతో కలపండి.

1 uint _key = (uint256(_firstByte & 0x0F) << (8*_extraBytes)) +
2 _calldataVal(_fromByte+1, _extraBytes);
3
4 return (_fromByte+_extraBytes+1, cacheRead(_key));
5
6 } // _readParam
7
8
9 // n పారామీటర్లను చదవండి (ఫంక్షన్లు తమకు ఎన్ని పారామీటర్లు కావాలో తెలుసుకుంటాయి)
10 function _readParams(uint _paramNum) internal returns (uint[] memory) {
అన్నీ చూపించు

మేము కాల్‌డేటా నుండే మనకు ఉన్న పారామితుల సంఖ్యను పొందవచ్చు, కానీ మాకు కాల్ చేసే ఫంక్షన్లకు వారు ఎన్ని పారామితులను ఆశిస్తున్నారో తెలుసు. వారు మాకు చెప్పనివ్వడం సులభం.

1 // మనం చదివే పారామీటర్లు
2 uint[] memory params = new uint[](_paramNum);
3
4 // పారామీటర్లు బైట్ 4 వద్ద ప్రారంభమవుతాయి, దానికి ముందు ఫంక్షన్ సిగ్నేచర్ ఉంటుంది
5 uint _atByte = 4;
6
7 for(uint i=0; i<_paramNum; i++) {
8 (_atByte, params[i]) = _readParam(_atByte);
9 }
అన్నీ చూపించు

మీకు అవసరమైన సంఖ్య వచ్చేవరకు పారామీటర్లను చదవండి. మనం కాల్‌డేటా చివరికి వెళితే, _readParams కాల్‌ను రద్దు చేస్తుంది.

1
2 return(params);
3 } // readParams
4
5 // _readParams ను పరీక్షించడానికి, నాలుగు పారామీటర్లను చదవడం పరీక్షించండి
6 function fourParam() public
7 returns (uint256,uint256,uint256,uint256)
8 {
9 uint[] memory params;
10 params = _readParams(4);
11 return (params[0], params[1], params[2], params[3]);
12 } // fourParam
అన్నీ చూపించు

ఫౌండ్రీ యొక్క ఒక పెద్ద ప్రయోజనం ఏమిటంటే, ఇది సాలిడిటీలో పరీక్షలను వ్రాయడానికి అనుమతిస్తుంది (క్రింద కాష్‌ని పరీక్షించడం చూడండి). ఇది యూనిట్ పరీక్షలను చాలా సులభం చేస్తుంది. ఇది నాలుగు పారామీటర్లను చదివి, వాటిని తిరిగి ఇచ్చే ఒక ఫంక్షన్, తద్వారా పరీక్ష వాటిని సరిగ్గా ఉన్నాయని ధృవీకరించగలదు.

1 // ఒక విలువను పొందండి, దానిని ఎన్కోడ్ చేసే బైట్లను తిరిగి ఇవ్వండి (సాధ్యమైతే కాష్ ఉపయోగించి)
2 function encodeVal(uint _val) public view returns(bytes memory) {

encodeVal అనేది కాష్‌ను ఉపయోగించే కాల్‌డేటాను సృష్టించడానికి సహాయపడటానికి ఆఫ్‌చెయిన్ కోడ్ కాల్ చేసే ఒక ఫంక్షన్. ఇది ఒకే విలువను స్వీకరించి, దానిని ఎన్కోడ్ చేసే బైట్లను తిరిగి ఇస్తుంది. ఈ ఫంక్షన్ ఒక view, కాబట్టి దీనికి లావాదేవీ అవసరం లేదు మరియు బాహ్యంగా కాల్ చేసినప్పుడు ఏ గ్యాస్ ఖర్చు కాదు.

1 uint _key = val2key[_val];
2
3 // విలువ ఇంకా కాష్‌లో లేదు, దాన్ని జోడించండి
4 if (_key == 0)
5 return bytes.concat(INTO_CACHE, bytes32(_val));

EVMలో ప్రారంభించని నిల్వ అంతా సున్నాలుగా భావించబడుతుంది. కాబట్టి, అక్కడ లేని విలువ కోసం కీని చూస్తే, మనకు సున్నా వస్తుంది. ఆ సందర్భంలో, దానిని ఎన్కోడ్ చేసే బైట్లు INTO_CACHE (కాబట్టి అది తదుపరిసారి కాష్ చేయబడుతుంది), ఆ తర్వాత వాస్తవ విలువ ఉంటుంది.

1 // కీ <0x10 అయితే, దానిని ఒకే బైట్‌గా తిరిగి ఇవ్వండి
2 if (_key < 0x10)
3 return bytes.concat(bytes1(uint8(_key)));

ఒకే బైట్లు చాలా సులభం. మేము bytes.concat (opens in a new tab) ను bytes<n> రకాన్ని ఏ పొడవునైనా ఉండే బైట్ శ్రేణిగా మార్చడానికి ఉపయోగిస్తాము. పేరు ఉన్నప్పటికీ, కేవలం ఒక ఆర్గ్యుమెంట్‌తో అందించినప్పుడు ఇది బాగా పనిచేస్తుంది.

1 // రెండు బైట్ల విలువ, 0x1vvv గా ఎన్కోడ్ చేయబడింది
2 if (_key < 0x1000)
3 return bytes.concat(bytes2(uint16(_key) | 0x1000));

మనకు 163 కన్నా తక్కువ ఉన్న కీ ఉన్నప్పుడు, దానిని రెండు బైట్లలో వ్యక్తీకరించవచ్చు. మేము మొదట 256 బిట్ విలువ అయిన _key ను 16 బిట్ విలువకు మార్చి, మొదటి బైట్‌కు అదనపు బైట్ల సంఖ్యను జోడించడానికి లాజికల్ ఆర్ ని ఉపయోగిస్తాము. అప్పుడు మనం దానిని bytes2 విలువలోకి మారుస్తాము, దానిని bytes గా మార్చవచ్చు.

1 // కింది లైన్లను లూప్‌గా చేయడానికి బహుశా ఒక తెలివైన మార్గం ఉంది,
2 // కానీ ఇది ఒక వీక్షణ ఫంక్షన్ కాబట్టి నేను ప్రోగ్రామర్ సమయం మరియు
3 // సరళత కోసం ఆప్టిమైజ్ చేస్తున్నాను.
4
5 if (_key < 16*256**2)
6 return bytes.concat(bytes3(uint24(_key) | (0x2 * 16 * 256**2)));
7 if (_key < 16*256**3)
8 return bytes.concat(bytes4(uint32(_key) | (0x3 * 16 * 256**3)));
9 .
10 .
11 .
12 if (_key < 16*256**14)
13 return bytes.concat(bytes15(uint120(_key) | (0xE * 16 * 256**14)));
14 if (_key < 16*256**15)
15 return bytes.concat(bytes16(uint128(_key) | (0xF * 16 * 256**15)));
అన్నీ చూపించు

ఇతర విలువలు (3 బైట్లు, 4 బైట్లు, మొదలైనవి) వివిధ ఫీల్డ్ పరిమాణాలతో ఒకే విధంగా నిర్వహించబడతాయి.

1 // మనం ఇక్కడకు వస్తే, ఏదో తప్పు జరిగింది.
2 revert("Error in encodeVal, should not happen");

మనం ఇక్కడకు వస్తే, మనకు 16*25615 కన్నా తక్కువ కాని కీ వచ్చింది అని అర్థం. కానీ cacheWrite కీలను పరిమితం చేస్తుంది కాబట్టి మనం 14*25616 వరకు కూడా పొందలేము (దీనికి మొదటి బైట్ 0xFE ఉంటుంది, కాబట్టి ఇది DONT_CACHE లాగా కనిపిస్తుంది). కానీ భవిష్యత్ ప్రోగ్రామర్ ఒక బగ్‌ను ప్రవేశపెట్టిన సందర్భంలో ఒక పరీక్షను జోడించడానికి మాకు ఎక్కువ ఖర్చు కాదు.

1 } // encodeVal
2
3} // Cache

కాష్‌ని పరీక్షించడం

ఫౌండ్రీ యొక్క ప్రయోజనాలలో ఒకటి ఏమిటంటే, ఇది సాలిడిటీలో పరీక్షలను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది (opens in a new tab), ఇది యూనిట్ పరీక్షలను వ్రాయడాన్ని సులభం చేస్తుంది. Cache తరగతికి సంబంధించిన పరీక్షలు ఇక్కడ (opens in a new tab) ఉన్నాయి. పరీక్షా కోడ్ పునరావృతమవుతుంది కాబట్టి, ఈ వ్యాసం కేవలం ఆసక్తికరమైన భాగాలను మాత్రమే వివరిస్తుంది.

1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.13;
3
4import "forge-std/Test.sol";
5
6
7// కన్సోల్ కోసం `forge test -vv` ను అమలు చేయాలి.
8import "forge-std/console.sol";

ఇది కేవలం పరీక్షా ప్యాకేజీని మరియు console.log ను ఉపయోగించడానికి అవసరమైన బాయిలర్‌ప్లేట్.

1import "src/Cache.sol";

మేము పరీక్షిస్తున్న కాంట్రాక్టు గురించి తెలుసుకోవాలి.

1contract CacheTest is Test {
2 Cache cache;
3
4 function setUp() public {
5 cache = new Cache();
6 }

setUp ఫంక్షన్ ప్రతి పరీక్షకు ముందు కాల్ చేయబడుతుంది. ఈ సందర్భంలో మేము కేవలం ఒక కొత్త కాష్‌ను సృష్టిస్తాము, తద్వారా మా పరీక్షలు ఒకదానిపై ఒకటి ప్రభావం చూపవు.

1 function testCaching() public {

పరీక్షలు అనేవి test తో మొదలయ్యే ఫంక్షన్లు. ఈ ఫంక్షన్ ప్రాథమిక కాష్ కార్యాచరణను, విలువలను వ్రాసి వాటిని మళ్ళీ చదవడాన్ని తనిఖీ చేస్తుంది.

1 for(uint i=1; i<5000; i++) {
2 cache.cacheWrite(i*i);
3 }
4
5 for(uint i=1; i<5000; i++) {
6 assertEq(cache.cacheRead(i), i*i);

assert... ఫంక్షన్లను (opens in a new tab) ఉపయోగించి మీరు అసలు పరీక్షను ఎలా చేస్తారో ఇది చూపిస్తుంది. ఈ సందర్భంలో, మేము వ్రాసిన విలువ చదివిన విలువతో సమానంగా ఉందని తనిఖీ చేస్తాము. కాష్ కీలు సరళంగా కేటాయించబడతాయని మాకు తెలుసు కాబట్టి cache.cacheWrite ఫలితాన్ని మనం విస్మరించవచ్చు.

1 }
2 } // testCaching
3
4
5 // ఒకే విలువను చాలాసార్లు కాష్ చేయండి, కీ అలాగే ఉండేలా చూసుకోండి
6 //
7 function testRepeatCaching() public {
8 for(uint i=1; i<100; i++) {
9 uint _key1 = cache.cacheWrite(i);
10 uint _key2 = cache.cacheWrite(i);
11 assertEq(_key1, _key2);
12 }
అన్నీ చూపించు

మొదట మనం ప్రతి విలువను కాష్‌లోకి రెండుసార్లు రాసి, కీలు ఒకేలా ఉన్నాయని నిర్ధారించుకుంటాము (అంటే రెండవ రైట్ నిజంగా జరగలేదు).

1 for(uint i=1; i<100; i+=3) {
2 uint _key = cache.cacheWrite(i);
3 assertEq(_key, i);
4 }
5 } // testRepeatCaching

సిద్ధాంతపరంగా, వరుసగా కాష్ రైట్‌లను ప్రభావితం చేయని బగ్ ఉండవచ్చు. కాబట్టి ఇక్కడ మనం వరుసగా లేని కొన్ని రైట్‌లను చేస్తాము మరియు విలువలు ఇప్పటికీ తిరిగి వ్రాయబడలేదని చూస్తాము.

1 // మెమరీ బఫర్ నుండి ఒక uint ను చదవండి (మనం పంపిన పారామీటర్లను తిరిగి పొందుతున్నామని నిర్ధారించుకోవడానికి
2 //)
3 function toUint256(bytes memory _bytes, uint256 _start) internal pure
4 returns (uint256)

ఒక bytes memory బఫర్ నుండి 256 బిట్ వర్డ్‌ను చదవండి. ఈ యుటిలిటీ ఫంక్షన్ కాష్‌ను ఉపయోగించే ఫంక్షన్ కాల్‌ను రన్ చేసినప్పుడు సరైన ఫలితాలను స్వీకరిస్తున్నామని ధృవీకరించడానికి మాకు సహాయపడుతుంది.

1 {
2 require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
3 uint256 tempUint;
4
5 assembly {
6 tempUint := mload(add(add(_bytes, 0x20), _start))
7 }

Yul uint256 కు మించిన డేటా స్ట్రక్చర్లకు మద్దతు ఇవ్వదు, కాబట్టి మీరు మెమరీ బఫర్ _bytes వంటి మరింత అధునాతన డేటా స్ట్రక్చర్‌ను సూచించినప్పుడు, మీరు ఆ స్ట్రక్చర్ యొక్క చిరునామాను పొందుతారు. Solidity పొడవును కలిగి ఉన్న 32 బైట్ల పదం తర్వాత అసలు బైట్లుగా bytes memory విలువలను నిల్వ చేస్తుంది, కాబట్టి _start బైట్ నంబర్ పొందడానికి మనం _bytes+32+_start ను లెక్కించాలి.

1
2 return tempUint;
3 } // toUint256
4
5 // fourParams() కోసం ఫంక్షన్ సిగ్నేచర్, సౌజన్యంతో
6 // https://www.4byte.directory/signatures/?bytes4_signature=0x3edc1e6d
7 bytes4 constant FOUR_PARAMS = 0x3edc1e6d;
8
9 // మనం సరైన విలువలను తిరిగి పొందుతున్నామని చూడటానికి కేవలం కొన్ని స్థిరమైన విలువలు
10 uint256 constant VAL_A = 0xDEAD60A7;
11 uint256 constant VAL_B = 0xBEEF;
12 uint256 constant VAL_C = 0x600D;
13 uint256 constant VAL_D = 0x600D60A7;
అన్నీ చూపించు

పరీక్ష కోసం మాకు కొన్ని స్థిరాంకాలు అవసరం.

1 function testReadParam() public {

పారామీటర్లను సరిగ్గా చదవగలమని పరీక్షించడానికి readParams ని ఉపయోగించే fourParams() ఫంక్షన్‌ను కాల్ చేయండి.

1 address _cacheAddr = address(cache);
2 bool _success;
3 bytes memory _callInput;
4 bytes memory _callOutput;

కాష్ ఉపయోగించి ఫంక్షన్‌ను కాల్ చేయడానికి మనం సాధారణ ABI మెకానిజంను ఉపయోగించలేము, కాబట్టి మనం తక్కువ స్థాయి <address>.call() (opens in a new tab) మెకానిజంను ఉపయోగించాలి. ఆ మెకానిజం ఇన్‌పుట్‌గా bytes memory తీసుకుంటుంది, మరియు దానిని (అలాగే ఒక బూలియన్ విలువను) అవుట్‌పుట్‌గా తిరిగి ఇస్తుంది.

1 // మొదటి కాల్, కాష్ ఖాళీగా ఉంది
2 _callInput = bytes.concat(
3 FOUR_PARAMS,

ఒకే కాంట్రాక్ట్ కాష్ చేసిన ఫంక్షన్‌లకు (లావాదేవీల నుండి నేరుగా కాల్‌ల కోసం) మరియు నాన్-కాష్ చేసిన ఫంక్షన్‌లకు (ఇతర స్మార్ట్ కాంట్రాక్ట్‌ల నుండి కాల్‌ల కోసం) మద్దతు ఇవ్వడం ఉపయోగకరం. అది చేయడానికి మనం ప్రతిదీ ఒక fallback ఫంక్షన్‌లో (opens in a new tab) ఉంచడానికి బదులుగా, సరైన ఫంక్షన్‌ను కాల్ చేయడానికి సాలిడిటీ మెకానిజంపై ఆధారపడాలి. దీన్ని చేయడం వల్ల కంపోజబిలిటీ చాలా సులభం అవుతుంది. చాలా సందర్భాలలో ఫంక్షన్‌ను గుర్తించడానికి ఒకే బైట్ సరిపోతుంది, కాబట్టి మనం మూడు బైట్లను వృధా చేస్తున్నాము (16*3=48 గ్యాస్). అయితే, నేను ఇది రాస్తున్నప్పుడు ఆ 48 గ్యాస్ 0.07 సెంట్లు ఖర్చవుతుంది, ఇది సరళమైన, తక్కువ బగ్ ప్రూన్ కోడ్ యొక్క సరైన ఖర్చు.

1 // మొదటి విలువ, దాన్ని కాష్‌లోకి జోడించండి
2 cache.INTO_CACHE(),
3 bytes32(VAL_A),

మొదటి విలువ: ఇది కాష్‌లోకి వ్రాయవలసిన పూర్తి విలువ అని చెప్పే ఒక ఫ్లాగ్, ఆ తర్వాత విలువ యొక్క 32 బైట్లు. ఇతర మూడు విలువలు కూడా ఇలాంటివే, అయితే VAL_B కాష్‌లోకి వ్రాయబడదు మరియు VAL_C మూడవ మరియు నాల్గవ పారామీటర్ రెండూ.

1 .
2 .
3 .
4 );
5 (_success, _callOutput) = _cacheAddr.call(_callInput);

ఇక్కడే మనం అసలు Cache కాంట్రాక్ట్‌ను కాల్ చేస్తాము.

1 assertEq(_success, true);

కాల్ విజయవంతం అవుతుందని మేము ఆశిస్తున్నాము.

1 assertEq(cache.cacheRead(1), VAL_A);
2 assertEq(cache.cacheRead(2), VAL_C);

మనం ఖాళీ కాష్‌తో ప్రారంభించి, ఆ తర్వాత VAL_A తర్వాత VAL_C ని జోడిస్తాము. మొదటిదానికి కీ 1, మరియు రెండవదానికి 2 ఉంటుందని మేము ఆశిస్తాము.

1 assertEq(toUint256(_callOutput,0), VAL_A);
2 assertEq(toUint256(_callOutput,32), VAL_B);
3 assertEq(toUint256(_callOutput,64), VAL_C);
4 assertEq(toUint256(_callOutput,96), VAL_C);

అవుట్‌పుట్ నాలుగు పారామీటర్లు. ఇక్కడ అది సరైనదని మేము ధృవీకరిస్తాము.

1 // రెండవ కాల్, మనం కాష్‌ను ఉపయోగించవచ్చు
2 _callInput = bytes.concat(
3 FOUR_PARAMS,
4
5 // కాష్‌లో మొదటి విలువ
6 bytes1(0x01),

16 కంటే తక్కువ ఉన్న కాష్ కీలు కేవలం ఒక బైట్.

1 // రెండవ విలువ, దాన్ని కాష్‌లోకి జోడించవద్దు
2 cache.DONT_CACHE(),
3 bytes32(VAL_B),
4
5 // మూడవ మరియు నాల్గవ విలువలు, ఒకే విలువ
6 bytes1(0x02),
7 bytes1(0x02)
8 );
9 .
10 .
11 .
12 } // testReadParam
అన్నీ చూపించు

కాల్ తర్వాత పరీక్షలు మొదటి కాల్ తర్వాత పరీక్షలతో సమానంగా ఉంటాయి.

1 function testEncodeVal() public {

ఈ ఫంక్షన్ testReadParam కు సమానమైనది, అయితే పారామీటర్లను స్పష్టంగా వ్రాయడానికి బదులుగా మనం encodeVal() ని ఉపయోగిస్తాము.

1 .
2 .
3 .
4 _callInput = bytes.concat(
5 FOUR_PARAMS,
6 cache.encodeVal(VAL_A),
7 cache.encodeVal(VAL_B),
8 cache.encodeVal(VAL_C),
9 cache.encodeVal(VAL_D)
10 );
11 .
12 .
13 .
14 assertEq(_callInput.length, 4+1*4);
15 } // testEncodeVal
అన్నీ చూపించు

testEncodeVal() లో ఏకైక అదనపు పరీక్ష _callInput పొడవు సరైనదని ధృవీకరించడం. మొదటి కాల్ కోసం ఇది 4+33_4. రెండవది, ప్రతి విలువ ఇప్పటికే కాష్‌లో ఉంది, ఇది 4+1_4.

1 // కీ ఒకటి కంటే ఎక్కువ బైట్లు ఉన్నప్పుడు encodeVal ను పరీక్షించండి
2 // గరిష్టంగా మూడు బైట్లు ఎందుకంటే కాష్‌ను నాలుగు బైట్లకు నింపడానికి
3 // చాలా సమయం పడుతుంది.
4 function testEncodeValBig() public {
5 // కాష్‌లో కొన్ని విలువలను ఉంచండి.
6 // విషయాలను సరళంగా ఉంచడానికి, n విలువ కోసం కీ n ను ఉపయోగించండి.
7 for(uint i=1; i<0x1FFF; i++) {
8 cache.cacheWrite(i);
9 }
అన్నీ చూపించు

పై testEncodeVal ఫంక్షన్ కాష్‌లోకి నాలుగు విలువలను మాత్రమే రాస్తుంది, కాబట్టి మల్టీ-బైట్ విలువలతో వ్యవహరించే ఫంక్షన్ భాగం (opens in a new tab) తనిఖీ చేయబడదు. కానీ ఆ కోడ్ సంక్లిష్టంగా మరియు లోపాలతో కూడుకున్నది.

ఈ ఫంక్షన్ యొక్క మొదటి భాగం 1 నుండి 0x1FFF వరకు ఉన్న అన్ని విలువలను వరుసగా కాష్‌లోకి రాసే లూప్, కాబట్టి మనం ఆ విలువలను ఎన్కోడ్ చేయగలము మరియు అవి ఎక్కడికి వెళ్తున్నాయో తెలుసుకోగలము.

1 .
2 .
3 .
4
5 _callInput = bytes.concat(
6 FOUR_PARAMS,
7 cache.encodeVal(0x000F), // ఒక బైట్ 0x0F
8 cache.encodeVal(0x0010), // రెండు బైట్లు 0x1010
9 cache.encodeVal(0x0100), // రెండు బైట్లు 0x1100
10 cache.encodeVal(0x1000) // మూడు బైట్లు 0x201000
11 );
అన్నీ చూపించు

ఒక బైట్, రెండు బైట్, మరియు మూడు బైట్ విలువలను పరీక్షించండి. దానికంటే మించి మనం పరీక్షించము ఎందుకంటే తగినంత స్టాక్ ఎంట్రీలు (కనీసం 0x10000000, సుమారు పావు బిలియన్) రాయడానికి చాలా సమయం పడుతుంది.

1 .
2 .
3 .
4 .
5 } // testEncodeValBig
6
7
8 // చాలా చిన్న బఫర్‌తో మనం ఒక తిరస్కరణ పొందుతామని పరీక్షించండి
9 function testShortCalldata() public {
అన్నీ చూపించు

తగినన్ని పారామీటర్లు లేని అసాధారణ సందర్భంలో ఏమి జరుగుతుందో పరీక్షించండి.

1 .
2 .
3 .
4 (_success, _callOutput) = _cacheAddr.call(_callInput);
5 assertEq(_success, false);
6 } // testShortCalldata

ఇది తిరస్కరిస్తుంది కాబట్టి, మనం పొందవలసిన ఫలితం false.

1 // అక్కడ లేని కాష్ కీలతో కాల్ చేయండి
2 function testNoCacheKey() public {
3 .
4 .
5 .
6 _callInput = bytes.concat(
7 FOUR_PARAMS,
8
9 // మొదటి విలువ, దాన్ని కాష్‌లోకి జోడించండి
10 cache.INTO_CACHE(),
11 bytes32(VAL_A),
12
13 // రెండవ విలువ
14 bytes1(0x0F),
15 bytes2(0x1234),
16 bytes11(0xA10102030405060708090A)
17 );
అన్నీ చూపించు

ఈ ఫంక్షన్ నాలుగు పూర్తిగా సరైన పారామీటర్లను పొందుతుంది, అయితే కాష్ ఖాళీగా ఉంది కాబట్టి అక్కడ చదవడానికి విలువలు లేవు.

1 .
2 .
3 .
4 // చాలా పొడవైన బఫర్‌తో ప్రతిదీ ఫైల్ పనిచేస్తుందని పరీక్షించండి
5 function testLongCalldata() public {
6 address _cacheAddr = address(cache);
7 bool _success;
8 bytes memory _callInput;
9 bytes memory _callOutput;
10
11 // మొదటి కాల్, కాష్ ఖాళీగా ఉంది
12 _callInput = bytes.concat(
13 FOUR_PARAMS,
14
15 // మొదటి విలువ, దాన్ని కాష్‌లోకి జోడించండి
16 cache.INTO_CACHE(), bytes32(VAL_A),
17
18 // రెండవ విలువ, దాన్ని కాష్‌లోకి జోడించండి
19 cache.INTO_CACHE(), bytes32(VAL_B),
20
21 // మూడవ విలువ, దాన్ని కాష్‌లోకి జోడించండి
22 cache.INTO_CACHE(), bytes32(VAL_C),
23
24 // నాల్గవ విలువ, దాన్ని కాష్‌లోకి జోడించండి
25 cache.INTO_CACHE(), bytes32(VAL_D),
26
27 // మరియు "మంచి అదృష్టం" కోసం మరొక విలువ
28 bytes4(0x31112233)
29 );
అన్నీ చూపించు

ఈ ఫంక్షన్ ఐదు విలువలను పంపుతుంది. ఐదవ విలువ విస్మరించబడుతుందని మాకు తెలుసు ఎందుకంటే ఇది చెల్లుబాటు అయ్యే కాష్ ఎంట్రీ కాదు, ఇది చేర్చబడకపోతే ఒక తిరస్కరణకు కారణమయ్యేది.

1 (_success, _callOutput) = _cacheAddr.call(_callInput);
2 assertEq(_success, true);
3 .
4 .
5 .
6 } // testLongCalldata
7
8} // CacheTest
9
అన్నీ చూపించు

ఒక నమూనా యాప్

సాలిడిటీలో పరీక్షలు రాయడం చాలా మంచిది, కానీ చివరికి ఒక డాప్ ఉపయోగకరంగా ఉండటానికి చైన్ వెలుపల నుండి అభ్యర్థనలను ప్రాసెస్ చేయగలగాలి. ఈ వ్యాసం WORM తో ఒక డాప్‌లో కాషింగ్ ఎలా ఉపయోగించాలో ప్రదర్శిస్తుంది, ఇది "ఒకసారి రాయండి, చాలాసార్లు చదవండి" అని సూచిస్తుంది. ఒక కీ ఇంకా వ్రాయబడకపోతే, మీరు దానికి ఒక విలువను వ్రాయవచ్చు. కీ ఇప్పటికే వ్రాయబడితే, మీకు ఒక తిరస్కరణ లభిస్తుంది.

ఒప్పందం

ఇది కాంట్రాక్ట్ (opens in a new tab). ఇది ఎక్కువగా Cache మరియు CacheTest తో మనం ఇప్పటికే చేసిన దానిని పునరావృతం చేస్తుంది, కాబట్టి మేము ఆసక్తికరమైన భాగాలను మాత్రమే కవర్ చేస్తాము.

1import "./Cache.sol";
2
3contract WORM is Cache {

Cache ను ఉపయోగించడానికి సులభమైన మార్గం దానిని మన స్వంత కాంట్రాక్ట్‌లో వారసత్వంగా పొందడం.

1 function writeEntryCached() external {
2 uint[] memory params = _readParams(2);
3 writeEntry(params[0], params[1]);
4 } // writeEntryCached

ఈ ఫంక్షన్ పై CacheTest లోని fourParam కు సమానమైనది. మేము ABI స్పెసిఫికేషన్లను అనుసరించడం లేదు కాబట్టి, ఫంక్షన్‌లో ఏ పారామీటర్లను ప్రకటించకపోవడం ఉత్తమం.

1 // మమ్మల్ని కాల్ చేయడం సులభం చేయండి
2 // writeEntryCached() కోసం ఫంక్షన్ సిగ్నేచర్, సౌజన్యంతో
3 // https://www.4byte.directory/signatures/?bytes4_signature=0xe4e4f2d3
4 bytes4 constant public WRITE_ENTRY_CACHED = 0xe4e4f2d3;

writeEntryCached ని కాల్ చేసే బాహ్య కోడ్ మాన్యువల్‌గా కాల్‌డేటాను నిర్మించవలసి ఉంటుంది, worm.writeEntryCached ని ఉపయోగించడానికి బదులుగా, ఎందుకంటే మేము ABI స్పెసిఫికేషన్లను అనుసరించడం లేదు. ఈ స్థిరమైన విలువను కలిగి ఉండటం దానిని వ్రాయడాన్ని సులభం చేస్తుంది.

మనం WRITE_ENTRY_CACHED ను స్టేట్ వేరియబుల్‌గా నిర్వచించినప్పటికీ, దానిని బాహ్యంగా చదవడానికి దాని గెట్టర్ ఫంక్షన్, worm.WRITE_ENTRY_CACHED() ను ఉపయోగించడం అవసరం.

1 function readEntry(uint key) public view
2 returns (uint _value, address _writtenBy, uint _writtenAtBlock)

రీడ్ ఫంక్షన్ ఒక view, కాబట్టి దీనికి లావాదేవీ అవసరం లేదు మరియు గ్యాస్ ఖర్చు కాదు. ఫలితంగా, పారామీటర్ కోసం కాష్‌ను ఉపయోగించడం వల్ల ఎటువంటి ప్రయోజనం లేదు. వీక్షణ ఫంక్షన్లతో సరళమైన ప్రామాణిక మెకానిజంను ఉపయోగించడం ఉత్తమం.

పరీక్షా కోడ్

ఇది కాంట్రాక్ట్ కోసం పరీక్షా కోడ్ (opens in a new tab). మళ్ళీ, ఆసక్తికరంగా ఉన్నదాన్ని మాత్రమే చూద్దాం.

1 function testWReadWrite() public {
2 worm.writeEntry(0xDEAD, 0x60A7);
3
4 vm.expectRevert(bytes("entry already written"));
5 worm.writeEntry(0xDEAD, 0xBEEF);

ఇది (vm.expectRevert) (opens in a new tab) మనం ఒక ఫౌండ్రీ పరీక్షలో తదుపరి కాల్ విఫలం కావాలని, మరియు విఫలం కావడానికి నివేదించబడిన కారణాన్ని ఎలా పేర్కొంటామో చూపిస్తుంది. ఇది మనం సింటాక్స్ <contract>.<function name>() ను ఉపయోగించినప్పుడు వర్తిస్తుంది, కాల్‌డేటాను నిర్మించి, తక్కువ స్థాయి ఇంటర్‌ఫేస్ (<contract>.call(), మొదలైనవి) ఉపయోగించి కాంట్రాక్ట్‌ను కాల్ చేయడానికి బదులుగా.

1 function testReadWriteCached() public {
2 uint cacheGoat = worm.cacheWrite(0x60A7);

ఇక్కడ మనం cacheWrite కాష్ కీని తిరిగి ఇస్తుందనే వాస్తవాన్ని ఉపయోగిస్తాము. ఇది మనం ఉత్పత్తిలో ఉపయోగించాలని ఆశించే విషయం కాదు, ఎందుకంటే cacheWrite స్థితిని మారుస్తుంది, మరియు అందువల్ల కేవలం ఒక లావాదేవీ సమయంలో మాత్రమే కాల్ చేయబడుతుంది. లావాదేవీలకు రిటర్న్ విలువలు ఉండవు, వాటికి ఫలితాలు ఉంటే ఆ ఫలితాలు ఈవెంట్‌లుగా విడుదల చేయబడతాయి. కాబట్టి cacheWrite రిటర్న్ విలువ కేవలం ఆన్‌చెయిన్ కోడ్ నుండి మాత్రమే అందుబాటులో ఉంటుంది, మరియు ఆన్‌చెయిన్ కోడ్‌కు పారామీటర్ కాషింగ్ అవసరం లేదు.

1 (_success,) = address(worm).call(_callInput);

ఇది <contract address>.call() కు రెండు రిటర్న్ విలువలు ఉన్నప్పటికీ, మనం కేవలం మొదటిదానిపై మాత్రమే శ్రద్ధ వహిస్తున్నామని Solidity కి ఎలా చెబుతామో చూపిస్తుంది.

1 (_success,) = address(worm).call(_callInput);
2 assertEq(_success, false);

మనం తక్కువ స్థాయి <address>.call() ఫంక్షన్‌ను ఉపయోగిస్తున్నాము కాబట్టి, మనం vm.expectRevert() ను ఉపయోగించలేము మరియు కాల్ నుండి మనం పొందే బూలియన్ సక్సెస్ విలువను చూడాలి.

1 event EntryWritten(uint indexed key, uint indexed value);
2
3 .
4 .
5 .
6
7 _callInput = bytes.concat(
8 worm.WRITE_ENTRY_CACHED(), worm.encodeVal(a), worm.encodeVal(b));
9 vm.expectEmit(true, true, false, false);
10 emit EntryWritten(a, b);
11 (_success,) = address(worm).call(_callInput);
అన్నీ చూపించు

ఫౌండ్రీలో కోడ్ ఒక ఈవెంట్‌ను సరిగ్గా విడుదల చేస్తుందని (opens in a new tab) మనం ధృవీకరించే మార్గం ఇది.

క్లయింట్

Solidity పరీక్షలతో మీరు పొందలేని ఒక విషయం ఏమిటంటే, మీరు మీ స్వంత అప్లికేషన్‌లో కట్ చేసి పేస్ట్ చేయగల జావాస్క్రిప్ట్ కోడ్. ఆ కోడ్ వ్రాయడానికి నేను WORM ను Optimism Goerli (opens in a new tab), Optimism's (opens in a new tab) కొత్త టెస్టునెట్‌కు triển khai చేసాను. ఇది చిరునామా 0xd34335b1d818cee54e3323d3246bd31d94e6a78a (opens in a new tab) వద్ద ఉంది.

మీరు ఇక్కడ ఖాతాదారుల కోసం జావాస్క్రిప్ట్ కోడ్ చూడవచ్చు (opens in a new tab). దీన్ని ఉపయోగించడానికి:

  1. git రిపోజిటరీని క్లోన్ చేయండి:

    1git clone https://github.com/qbzzt/20220915-all-you-can-cache.git
  2. అవసరమైన ప్యాకేజీలను ఇన్‌స్టాల్ చేయండి:

    1cd javascript
    2yarn
  3. కాన్ఫిగరేషన్ ఫైల్‌ను కాపీ చేయండి:

    1cp .env.example .env
  4. మీ కాన్ఫిగరేషన్ కోసం .env ని సవరించండి:

    పరామితివిలువ
    MNEMONICలావాదేవీ కోసం చెల్లించడానికి తగినంత ETH ఉన్న ఖాతా కోసం మ్నెమోనిక్. మీరు Optimism Goerli నెట్‌వర్క్ కోసం ఉచిత ETH ను ఇక్కడ పొందవచ్చు (opens in a new tab).
    OPTIMISM_GOERLI_URLOptimism Goerli కు URL. పబ్లిక్ ఎండ్‌పాయింట్, https://goerli.optimism.io, రేట్ లిమిటెడ్ కానీ ఇక్కడ మనకు అవసరమైన దానికి సరిపోతుంది
  5. index.js ను అమలు చేయండి.

    1node index.js

    ఈ నమూనా అప్లికేషన్ మొదట WORM కి ఒక ఎంట్రీని వ్రాస్తుంది, కాల్‌డేటా మరియు Etherscan పై లావాదేవీకి ఒక లింక్‌ను ప్రదర్శిస్తుంది. అప్పుడు అది ఆ ఎంట్రీని తిరిగి చదువుతుంది, మరియు అది ఉపయోగించే కీ మరియు ఎంట్రీలోని విలువలను (విలువ, బ్లాక్ నంబర్, మరియు రచయిత) ప్రదర్శిస్తుంది.

ఖాతాదారుల చాలా వరకు సాధారణ డాప్ జావాస్క్రిప్ట్. కాబట్టి మళ్ళీ మనం కేవలం ఆసక్తికరమైన భాగాలను మాత్రమే చూస్తాము.

1.
2.
3.
4const main = async () => {
5 const func = await worm.WRITE_ENTRY_CACHED()
6
7 // ప్రతిసారీ కొత్త కీ అవసరం
8 const key = await worm.encodeVal(Number(new Date()))

ఒక ఇచ్చిన స్లాట్‌లో ఒకసారి మాత్రమే వ్రాయవచ్చు, కాబట్టి మనం స్లాట్‌లను పునఃఉపయోగించకుండా చూసుకోవడానికి టైమ్‌స్టాంప్‌ను ఉపయోగిస్తాము.

1const val = await worm.encodeVal("0x600D")
2
3// ఒక ఎంట్రీని వ్రాయండి
4const calldata = func + key.slice(2) + val.slice(2)

Ethers కాల్ డేటా ఒక హెక్స్ స్ట్రింగ్, 0x తర్వాత ఒక సరి సంఖ్యలో హెక్సాడెసిమల్ అంకెలు ఉండాలని ఆశిస్తుంది. key మరియు val రెండూ 0x తో మొదలవుతాయి కాబట్టి, మనం ఆ హెడర్‌లను తొలగించాలి.

1const tx = await worm.populateTransaction.writeEntryCached()
2tx.data = calldata
3
4sentTx = await wallet.sendTransaction(tx)

Solidity పరీక్షా కోడ్‌తో లాగే, మనం ఒక కాష్ చేసిన ఫంక్షన్‌ను సాధారణంగా కాల్ చేయలేము. బదులుగా, మనం తక్కువ స్థాయి మెకానిజంను ఉపయోగించాలి.

1 .
2 .
3 .
4 // ఇప్పుడే వ్రాసిన ఎంట్రీని చదవండి
5 const realKey = '0x' + key.slice(4) // FF ఫ్లాగ్‌ను తొలగించండి
6 const entryRead = await worm.readEntry(realKey)
7 .
8 .
9 .
అన్నీ చూపించు

ఎంట్రీలను చదవడానికి మనం సాధారణ మెకానిజంను ఉపయోగించవచ్చు. view ఫంక్షన్లతో పారామీటర్ కాషింగ్ ఉపయోగించాల్సిన అవసరం లేదు.

ముగింపు

ఈ వ్యాసంలోని కోడ్ ఒక భావన యొక్క రుజువు, ఉద్దేశ్యం ఆలోచనను సులభంగా అర్థం చేసుకోవడం. ఉత్పత్తికి సిద్ధంగా ఉన్న సిస్టమ్ కోసం మీరు కొన్ని అదనపు కార్యాచరణలను అమలు చేయాలనుకోవచ్చు:

  • uint256 కాని విలువలను నిర్వహించండి. ఉదాహరణకు, స్ట్రింగ్‌లు.

  • గ్లోబల్ కాష్‌కు బదులుగా, బహుశా వినియోగదారులు మరియు కాష్‌ల మధ్య ఒక మ్యాపింగ్ ఉండవచ్చు. వివిధ వినియోగదారులు వివిధ విలువలను ఉపయోగిస్తారు.

  • చిరునామాల కోసం ఉపయోగించే విలువలు ఇతర ప్రయోజనాల కోసం ఉపయోగించే వాటి నుండి విభిన్నంగా ఉంటాయి. కేవలం చిరునామాల కోసం ప్రత్యేక కాష్ కలిగి ఉండటం అర్ధవంతంగా ఉండవచ్చు.

  • ప్రస్తుతం, కాష్ కీలు "మొదట వచ్చిన వారికి, చిన్న కీ" అల్గారిథమ్‌పై ఉన్నాయి. మొదటి పదహారు విలువలను ఒకే బైట్‌గా పంపవచ్చు. తదుపరి 4080 విలువలను రెండు బైట్లుగా పంపవచ్చు. తదుపరి సుమారు మిలియన్ విలువలు మూడు బైట్లు, మొదలైనవి. ఒక ప్రొడక్షన్ సిస్టమ్ కాష్ ఎంట్రీలపై వినియోగ కౌంటర్లను ఉంచుకోవాలి మరియు వాటిని పునర్వ్యవస్థీకరించాలి, తద్వారా పదహారు అత్యంత సాధారణ విలువలు ఒక బైట్, తదుపరి 4080 అత్యంత సాధారణ విలువలు రెండు బైట్లు, మొదలైనవి ఉంటాయి.

    అయితే, అది సంభావ్యంగా ప్రమాదకరమైన ఆపరేషన్. కింది సంఘటనల క్రమాన్ని ఊహించండి:

    1. నోమ్ అమాయకుడు తన టోకెన్లను పంపాలనుకుంటున్న చిరునామాను ఎన్కోడ్ చేయడానికి encodeVal ను కాల్ చేస్తాడు. ఆ చిరునామా అప్లికేషన్‌లో మొదట ఉపయోగించిన వాటిలో ఒకటి, కాబట్టి ఎన్కోడ్ చేయబడిన విలువ 0x06. ఇది ఒక లావాదేవీ కాదు, ఒక view ఫంక్షన్, కాబట్టి ఇది నోమ్ మరియు అతను ఉపయోగించే నోడ్ మధ్య ఉంటుంది, మరియు దాని గురించి ఎవరికీ తెలియదు

    2. ఓవెన్ ఓనర్ కాష్ రీఆర్డరింగ్ ఆపరేషన్‌ను రన్ చేస్తాడు. చాలా తక్కువ మంది ప్రజలు ఆ చిరునామాను ఉపయోగిస్తారు, కాబట్టి ఇది ఇప్పుడు 0x201122 గా ఎన్కోడ్ చేయబడింది. ఒక విభిన్న విలువ, 1018, కు 0x06 కేటాయించబడింది.

    3. నోమ్ అమాయకుడు తన టోకెన్లను 0x06 కు పంపుతాడు. అవి చిరునామా 0x0000000000000000000000000de0b6b3a7640000 కు వెళ్తాయి, మరియు ఆ చిరునామా కోసం ప్రైవేట్ కీ ఎవరికీ తెలియదు కాబట్టి, అవి అక్కడే చిక్కుకుపోతాయి. నోమ్ సంతోషంగా లేడు.

    ఈ సమస్యను పరిష్కరించడానికి మార్గాలు ఉన్నాయి, మరియు కాష్ రీఆర్డర్ సమయంలో మెమ్‌పూల్‌లో ఉన్న లావాదేవీల సంబంధిత సమస్య, కానీ మీరు దాని గురించి తెలుసుకోవాలి.

నేను ఇక్కడ ఆప్టిమిజంతో కాషింగ్‌ను ప్రదర్శించాను, ఎందుకంటే నేను ఆప్టిమిజం ఉద్యోగిని మరియు ఇది నాకు బాగా తెలిసిన రోల్అప్. కానీ ఇది అంతర్గత ప్రాసెసింగ్ కోసం కనీస ఖర్చును వసూలు చేసే ఏ రోల్అప్‌తోనైనా పని చేయాలి, తద్వారా పోల్చి చూస్తే లావాదేవీ డేటాను L1 కి వ్రాయడం ప్రధాన ఖర్చు.

నా మరిన్ని పనుల కోసం ఇక్కడ చూడండి (opens in a new tab).

పేజీ చివరి అప్‌డేట్: 25 ఫిబ్రవరి, 2026

ఈ ట్యుటోరియల్ ఉపయోగపడిందా?