కాంట్రాక్ట్ పరిమాణ పరిమితిని ఎదుర్కోవడానికి కాంట్రాక్ట్లను తగ్గించడం
పరిమితి ఎందుకు ఉంది?
నవంబర్ 22, 2016 (కొత్త ట్యాబ్లో తెరవబడుతుంది)న Spurious Dragon హార్డ్-ఫోర్క్ EIP-170 (కొత్త ట్యాబ్లో తెరవబడుతుంది)ని ప్రవేశపెట్టింది, ఇది స్మార్ట్ కాంట్రాక్ట్ పరిమాణ పరిమితిని 24.576 kbకి చేర్చింది. ఒక Solidity డెవలపర్గా మీకు దీని అర్థం ఏమిటంటే, మీరు మీ కాంట్రాక్ట్కు మరింత కార్యాచరణను జోడించినప్పుడు, ఏదో ఒక సమయంలో మీరు పరిమితిని చేరుకుంటారు మరియు డిప్లాయ్మెంట్ చేస్తున్నప్పుడు ఈ లోపాన్ని చూస్తారు:
Warning: Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries.
డినియల్-ఆఫ్-సర్వీస్ (DOS) దాడులను నిరోధించడానికి ఈ పరిమితి ప్రవేశపెట్టబడింది. కాంట్రాక్ట్కు చేసే ఏ కాల్ అయినా గ్యాస్ పరంగా సాపేక్షంగా చౌకగా ఉంటుంది. అయితే, ఎథీరియం నోడ్ల కోసం కాంట్రాక్ట్ కాల్ ప్రభావం కాల్ చేయబడిన కాంట్రాక్ట్ కోడ్ పరిమాణాన్ని బట్టి అసమానంగా పెరుగుతుంది (డిస్క్ నుండి కోడ్ను చదవడం, కోడ్ను ప్రీ-ప్రాసెస్ చేయడం, మెర్కల్ రుజువుకు డేటాను జోడించడం). ఇతరులకు ఎక్కువ పని కలిగించడానికి దాడి చేసే వ్యక్తికి తక్కువ వనరులు అవసరమయ్యే పరిస్థితి మీకు ఎదురైనప్పుడల్లా, మీకు DOS దాడుల సంభావ్యత ఏర్పడుతుంది.
వాస్తవానికి ఇది అంత పెద్ద సమస్య కాదు ఎందుకంటే ఒక సహజ కాంట్రాక్ట్ పరిమాణ పరిమితి బ్లాక్ గ్యాస్ పరిమితి. స్పష్టంగా, కాంట్రాక్ట్ యొక్క మొత్తం బైట్కోడ్ను కలిగి ఉన్న లావాదేవీలో కాంట్రాక్ట్ డిప్లాయ్మెంట్ చేయబడాలి. మీరు ఆ ఒక్క లావాదేవీని మాత్రమే బ్లాక్లో చేర్చినట్లయితే, మీరు ఆ గ్యాస్ మొత్తాన్ని ఉపయోగించవచ్చు, కానీ అది అనంతం కాదు. లండన్ అప్గ్రేడ్ నుండి, నెట్వర్క్ డిమాండ్ను బట్టి బ్లాక్ గ్యాస్ పరిమితి 15M మరియు 30M యూనిట్ల మధ్య మారుతూ వస్తోంది.
కింది వాటిలో వాటి సంభావ్య ప్రభావం ఆధారంగా క్రమబద్ధీకరించబడిన కొన్ని పద్ధతులను మనం చూస్తాము. బరువు తగ్గడం పరంగా దీని గురించి ఆలోచించండి. ఎవరైనా వారి లక్ష్య బరువును (మన విషయంలో 24kb) చేరుకోవడానికి ఉత్తమ వ్యూహం ముందుగా పెద్ద ప్రభావం చూపే పద్ధతులపై దృష్టి పెట్టడం. చాలా సందర్భాలలో మీ ఆహారాన్ని సరిదిద్దుకోవడం ద్వారా మీరు అక్కడికి చేరుకుంటారు, కానీ కొన్నిసార్లు మీకు కొంచెం ఎక్కువ అవసరం కావచ్చు. అప్పుడు మీరు కొంత వ్యాయామం (మధ్యస్థ ప్రభావం) లేదా సప్లిమెంట్లను (చిన్న ప్రభావం) జోడించవచ్చు.
పెద్ద ప్రభావం
మీ కాంట్రాక్ట్లను వేరు చేయండి
ఇది ఎల్లప్పుడూ మీ మొదటి విధానం కావాలి. మీరు కాంట్రాక్ట్ను బహుళ చిన్నవిగా ఎలా వేరు చేయవచ్చు? ఇది సాధారణంగా మీ కాంట్రాక్ట్ల కోసం మంచి ఆర్కిటెక్చర్ను రూపొందించేలా మిమ్మల్ని బలవంతం చేస్తుంది. కోడ్ చదవడానికి వీలుగా ఉండే కోణం నుండి చిన్న కాంట్రాక్ట్లు ఎల్లప్పుడూ ప్రాధాన్యతనిస్తాయి. కాంట్రాక్ట్లను విభజించడం కోసం, మిమ్మల్ని మీరు ఇలా ప్రశ్నించుకోండి:
- ఏ ఫంక్షన్లు కలిసి ఉంటాయి? ప్రతి ఫంక్షన్ల సెట్ దాని స్వంత కాంట్రాక్ట్లో ఉత్తమంగా ఉండవచ్చు.
- ఏ ఫంక్షన్లకు కాంట్రాక్ట్ స్థితిని చదవడం అవసరం లేదు లేదా స్థితి యొక్క నిర్దిష్ట ఉపసమితి మాత్రమే అవసరం?
- మీరు నిల్వ మరియు కార్యాచరణను విభజించగలరా?
లైబ్రరీలు
కార్యాచరణ కోడ్ను నిల్వ నుండి దూరంగా తరలించడానికి ఒక సులభమైన మార్గం లైబ్రరీ (కొత్త ట్యాబ్లో తెరవబడుతుంది)ని ఉపయోగించడం. లైబ్రరీ ఫంక్షన్లను ఇంటర్నల్గా ప్రకటించవద్దు ఎందుకంటే అవి కంపైలేషన్ సమయంలో నేరుగా కాంట్రాక్ట్కు జోడించబడతాయి (కొత్త ట్యాబ్లో తెరవబడుతుంది). కానీ మీరు పబ్లిక్ ఫంక్షన్లను ఉపయోగిస్తే, అవి వాస్తవానికి ప్రత్యేక లైబ్రరీ కాంట్రాక్ట్లో ఉంటాయి. లైబ్రరీల వినియోగాన్ని మరింత సౌకర్యవంతంగా చేయడానికి using for (కొత్త ట్యాబ్లో తెరవబడుతుంది)ని పరిగణించండి.
ప్రాక్సీలు
మరింత అధునాతన వ్యూహం ప్రాక్సీ సిస్టమ్ కావచ్చు. లైబ్రరీలు వెనుక భాగంలో DELEGATECALLని ఉపయోగిస్తాయి, ఇది కాల్ చేసే కాంట్రాక్ట్ స్థితితో మరొక కాంట్రాక్ట్ ఫంక్షన్ను సులభంగా అమలు చేస్తుంది. ప్రాక్సీ సిస్టమ్ల గురించి మరింత తెలుసుకోవడానికి ఈ బ్లాగ్ పోస్ట్ను (కొత్త ట్యాబ్లో తెరవబడుతుంది) చూడండి. అవి మీకు మరింత కార్యాచరణను అందిస్తాయి, ఉదాహరణకు, అవి అప్గ్రేడ్ చేయగల సామర్థ్యాన్ని ప్రారంభిస్తాయి, కానీ అవి చాలా సంక్లిష్టతను కూడా జోడిస్తాయి. ఏదైనా కారణం చేత ఇది మీ ఏకైక ఎంపిక అయితే తప్ప, కాంట్రాక్ట్ పరిమాణాలను తగ్గించడానికి మాత్రమే నేను వాటిని జోడించను.
మధ్యస్థ ప్రభావం
ఫంక్షన్లను తీసివేయండి
ఇది స్పష్టంగా ఉండాలి. ఫంక్షన్లు కాంట్రాక్ట్ పరిమాణాన్ని కొంచెం పెంచుతాయి.
- బాహ్య (External): తరచుగా మనం సౌలభ్యం కోసం చాలా వ్యూ ఫంక్షన్లను జోడిస్తాము. మీరు పరిమాణ పరిమితిని చేరుకునే వరకు అది ఖచ్చితంగా సరిపోతుంది. అప్పుడు మీరు ఖచ్చితంగా అవసరమైనవి మినహా మిగతా వాటన్నింటినీ తీసివేయడం గురించి నిజంగా ఆలోచించవచ్చు.
- అంతర్గత (Internal): మీరు అంతర్గత/ప్రైవేట్ ఫంక్షన్లను కూడా తీసివేయవచ్చు మరియు ఫంక్షన్ ఒక్కసారి మాత్రమే కాల్ చేయబడినంత వరకు కోడ్ను ఇన్లైన్ చేయవచ్చు.
అదనపు వేరియబుల్స్ను నివారించండి
function get(uint id) returns (address,address) {
MyStruct memory myStruct = myStructs[id];
return (myStruct.addr1, myStruct.addr2);
}
function get(uint id) returns (address,address) {
return (myStructs[id].addr1, myStructs[id].addr2);
}
ఇలాంటి ఒక సాధారణ మార్పు 0.28kb వ్యత్యాసాన్ని చూపుతుంది. మీ కాంట్రాక్ట్లలో మీరు ఇలాంటి అనేక పరిస్థితులను కనుగొనే అవకాశాలు ఉన్నాయి మరియు అవి నిజంగా గణనీయమైన మొత్తాలకు చేరవచ్చు.
ఎర్రర్ సందేశాన్ని తగ్గించండి
పొడవైన రివర్ట్ సందేశాలు మరియు ముఖ్యంగా అనేక విభిన్న రివర్ట్ సందేశాలు కాంట్రాక్ట్ను ఉబ్బేలా చేస్తాయి. బదులుగా చిన్న ఎర్రర్ కోడ్లను ఉపయోగించండి మరియు వాటిని మీ కాంట్రాక్ట్లో డీకోడ్ చేయండి. పొడవైన సందేశం చాలా చిన్నదిగా మారవచ్చు:
require(msg.sender == owner, "Only the owner of this contract can call this function");
require(msg.sender == owner, "OW1");
ఎర్రర్ సందేశాలకు బదులుగా కస్టమ్ ఎర్రర్లను ఉపయోగించండి
కస్టమ్ ఎర్రర్లు Solidity 0.8.4 (కొత్త ట్యాబ్లో తెరవబడుతుంది)లో ప్రవేశపెట్టబడ్డాయి. మీ కాంట్రాక్ట్ల పరిమాణాన్ని తగ్గించడానికి అవి ఒక గొప్ప మార్గం, ఎందుకంటే అవి సెలెక్టర్లుగా (ఫంక్షన్ల మాదిరిగానే) ABI-ఎన్కోడ్ చేయబడతాయి.
error Unauthorized();
if (msg.sender != owner) {
revert Unauthorized();
}
ఆప్టిమైజర్లో తక్కువ రన్ విలువను పరిగణించండి
మీరు ఆప్టిమైజర్ సెట్టింగ్లను కూడా మార్చవచ్చు. డిఫాల్ట్ విలువ 200 అంటే, ఒక ఫంక్షన్ 200 సార్లు కాల్ చేయబడినట్లుగా బైట్కోడ్ను ఆప్టిమైజ్ చేయడానికి ఇది ప్రయత్నిస్తుందని అర్థం. మీరు దానిని 1కి మార్చినట్లయితే, ప్రతి ఫంక్షన్ను ఒక్కసారి మాత్రమే రన్ చేసే సందర్భం కోసం ఆప్టిమైజ్ చేయమని మీరు ప్రాథమికంగా ఆప్టిమైజర్కు చెబుతారు. ఒక్కసారి మాత్రమే రన్ చేయడానికి ఆప్టిమైజ్ చేయబడిన ఫంక్షన్ అంటే అది డిప్లాయ్మెంట్ కోసం ఆప్టిమైజ్ చేయబడిందని అర్థం. ఇది ఫంక్షన్లను రన్ చేయడానికి గ్యాస్ ఖర్చులను పెంచుతుందని గుర్తుంచుకోండి, కాబట్టి మీరు దీన్ని చేయకూడదనుకోవచ్చు.
చిన్న ప్రభావం
ఫంక్షన్లకు స్ట్రక్ట్లను పంపడాన్ని నివారించండి
మీరు ABIEncoderV2 (కొత్త ట్యాబ్లో తెరవబడుతుంది)ని ఉపయోగిస్తుంటే, ఫంక్షన్కు స్ట్రక్ట్లను పంపకుండా ఉండటం సహాయపడుతుంది. పారామీటర్ను స్ట్రక్ట్గా పంపడానికి బదులుగా, అవసరమైన పారామీటర్లను నేరుగా పంపండి. ఈ ఉదాహరణలో మనం మరో 0.1kb ఆదా చేసాము.
function get(uint id) returns (address,address) {
return _get(myStruct);
}
function _get(MyStruct memory myStruct) private view returns(address,address) {
return (myStruct.addr1, myStruct.addr2);
}
function get(uint id) returns(address,address) {
return _get(myStructs[id].addr1, myStructs[id].addr2);
}
function _get(address addr1, address addr2) private view returns(address,address) {
return (addr1, addr2);
}
ఫంక్షన్లు మరియు వేరియబుల్స్ కోసం సరైన విజిబిలిటీని ప్రకటించండి
- బయటి నుండి మాత్రమే కాల్ చేయబడే ఫంక్షన్లు లేదా వేరియబుల్స్? వాటిని
publicకి బదులుగాexternalగా ప్రకటించండి. - కాంట్రాక్ట్ లోపల నుండి మాత్రమే కాల్ చేయబడే ఫంక్షన్లు లేదా వేరియబుల్స్? వాటిని
publicకి బదులుగాprivateలేదాinternalగా ప్రకటించండి.
మాడిఫైయర్లను తీసివేయండి
మాడిఫైయర్లు, ముఖ్యంగా తీవ్రంగా ఉపయోగించినప్పుడు, కాంట్రాక్ట్ పరిమాణంపై గణనీయమైన ప్రభావాన్ని చూపుతాయి. వాటిని తీసివేయడాన్ని పరిగణించండి మరియు బదులుగా ఫంక్షన్లను ఉపయోగించండి.
modifier checkStuff() {}
function doSomething() checkStuff {}
function checkStuff() private {}
function doSomething() { checkStuff(); }
ఆ చిట్కాలు కాంట్రాక్ట్ పరిమాణాన్ని గణనీయంగా తగ్గించడంలో మీకు సహాయపడతాయి. మరోసారి, నేను ఎంత చెప్పినా తక్కువే, అతిపెద్ద ప్రభావం కోసం వీలైతే కాంట్రాక్ట్లను విభజించడంపై ఎల్లప్పుడూ దృష్టి పెట్టండి.