முக்கிய உள்ளடக்கத்திற்குச் செல்லவும்

ஒப்பந்த அளவின் வரம்பை எதிர்கொள்ள ஒப்பந்தங்களின் அளவைக் குறைத்தல்

Solidity
ஸ்மார்ட் ஒப்பந்தங்கள்
சேமிப்பகம்
இடைநிலையாளர்
மார்கஸ் வாஸ்
26 ஜூன், 2020
5 நிமிட வாசிப்பு

ஏன் ஒரு வரம்பு உள்ளது?

நவம்பர் 22, 2016 (opens in a new tab) அன்று ஸ்பூரியஸ் டிராகன் (Spurious Dragon) ஹார்ட்-ஃபோர்க் EIP-170 (opens in a new tab) ஐ அறிமுகப்படுத்தியது, இது ஸ்மார்ட் ஒப்பந்தத்தின் அளவு வரம்பை 24.576 kb ஆகச் சேர்த்தது. ஒரு Solidity டெவலப்பராக உங்களுக்கு இதன் அர்த்தம் என்னவென்றால், உங்கள் ஒப்பந்தத்தில் மேலும் மேலும் செயல்பாடுகளைச் சேர்க்கும்போது, ஒரு கட்டத்தில் நீங்கள் வரம்பை அடைவீர்கள், மேலும் பயன்படுத்தும்போது (deploying) இந்தப் பிழையைக் காண்பீர்கள்:

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) தாக்குதல்களைத் தடுக்க இந்த வரம்பு அறிமுகப்படுத்தப்பட்டது. ஒரு ஒப்பந்தத்திற்கான எந்தவொரு அழைப்பும் எரிவாயு (gas) அடிப்படையில் ஒப்பீட்டளவில் மலிவானது. இருப்பினும், அழைக்கப்படும் ஒப்பந்தக் குறியீட்டின் அளவைப் பொறுத்து (வட்டிலிருந்து குறியீட்டைப் படிப்பது, குறியீட்டை முன்கூட்டியே செயலாக்குவது, மெர்க்கல் (Merkle) சான்றில் தரவைச் சேர்ப்பது) Ethereum நோடுகளுக்கான ஒப்பந்த அழைப்பின் தாக்கம் விகிதாசாரமின்றி அதிகரிக்கிறது. தாக்குபவர் மற்றவர்களுக்கு அதிக வேலைப்பளுவை ஏற்படுத்தக் குறைந்த வளங்களே தேவைப்படும் இத்தகைய சூழ்நிலை ஏற்படும்போதெல்லாம், DOS தாக்குதல்களுக்கான சாத்தியக்கூறுகள் உருவாகின்றன.

ஆரம்பத்தில் இது ஒரு பெரிய பிரச்சனையாக இருக்கவில்லை, ஏனெனில் பிளாக் எரிவாயு வரம்பு (block gas limit) என்பது ஒரு இயற்கையான ஒப்பந்த அளவு வரம்பாகும். வெளிப்படையாக, ஒப்பந்தத்தின் அனைத்து பைட் குறியீடுகளையும் (bytecode) கொண்ட ஒரு பரிவர்த்தனைக்குள்ளேயே ஒரு ஒப்பந்தம் பயன்படுத்தப்பட வேண்டும். ஒரு பிளாக்கில் அந்த ஒரு பரிவர்த்தனையை மட்டும் நீங்கள் சேர்த்தால், அந்த எரிவாயு முழுவதையும் நீங்கள் பயன்படுத்தலாம், ஆனால் அது முடிவற்றது அல்ல. லண்டன் மேம்படுத்தலுக்குப் (London Upgrade) பிறகு, நெட்வொர்க் தேவையப் பொறுத்து பிளாக் எரிவாயு வரம்பு 15M முதல் 30M யூனிட்கள் வரை மாறுபடும் திறனைப் பெற்றுள்ளது.

பின்வருவனவற்றில், அவற்றின் சாத்தியமான தாக்கத்தின் அடிப்படையில் வரிசைப்படுத்தப்பட்ட சில முறைகளைப் பார்ப்போம். இதை உடல் எடையைக் குறைப்பது போல நினைத்துப் பாருங்கள். ஒருவர் தனது இலக்கு எடையை (நமது விஷயத்தில் 24kb) அடைவதற்கான சிறந்த உத்தி, முதலில் பெரிய தாக்கத்தை ஏற்படுத்தும் முறைகளில் கவனம் செலுத்துவதாகும். பெரும்பாலான சந்தர்ப்பங்களில் உங்கள் உணவைச் சரிசெய்வதே உங்களை அங்கு கொண்டு செல்லும், ஆனால் சில நேரங்களில் உங்களுக்கு இன்னும் கொஞ்சம் தேவைப்படும். பின்னர் நீங்கள் சில உடற்பயிற்சிகளை (நடுத்தர தாக்கம்) அல்லது சப்ளிமெண்ட்ஸைக் கூட (சிறிய தாக்கம்) சேர்க்கலாம்.

பெரிய தாக்கம்

உங்கள் ஒப்பந்தங்களைப் பிரிக்கவும்

இதுவே எப்போதும் உங்களின் முதல் அணுகுமுறையாக இருக்க வேண்டும். ஒப்பந்தத்தை எப்படிப் பல சிறிய ஒப்பந்தங்களாகப் பிரிக்கலாம்? இது பொதுவாக உங்கள் ஒப்பந்தங்களுக்கு ஒரு நல்ல கட்டமைப்பை உருவாக்க உங்களை வற்புறுத்துகிறது. குறியீட்டைப் படிக்கும் திறனின் (code readability) அடிப்படையில் சிறிய ஒப்பந்தங்களே எப்போதும் விரும்பப்படுகின்றன. ஒப்பந்தங்களைப் பிரிக்க, உங்களையே கேட்டுக்கொள்ளுங்கள்:

  • எந்தச் செயல்பாடுகள் ஒன்றாகச் சேர்ந்தவை? ஒவ்வொரு செயல்பாடுகளின் தொகுப்பும் அதற்கெனத் தனி ஒப்பந்தத்தில் இருப்பது சிறப்பாக இருக்கலாம்.
  • எந்தச் செயல்பாடுகளுக்கு ஒப்பந்த நிலையைப் (contract state) படிக்கத் தேவையில்லை அல்லது நிலையின் ஒரு குறிப்பிட்ட துணைக்குழு (subset) மட்டும் போதுமானது?
  • சேமிப்பகத்தையும் (storage) செயல்பாட்டையும் உங்களால் பிரிக்க முடியுமா?

லைப்ரரிகள்

செயல்பாட்டுக் குறியீட்டைச் சேமிப்பகத்திலிருந்து நகர்த்துவதற்கான ஒரு எளிய வழி லைப்ரரியைப் (library) (opens in a new tab) பயன்படுத்துவதாகும். லைப்ரரி செயல்பாடுகளை இன்டெர்னல் (internal) என அறிவிக்க வேண்டாம், ஏனெனில் அவை தொகுக்கப்படும்போது (compilation) நேரடியாக ஒப்பந்தத்தில் சேர்க்கப்படும் (opens in a new tab). ஆனால் நீங்கள் பப்ளிக் (public) செயல்பாடுகளைப் பயன்படுத்தினால், அவை உண்மையில் ஒரு தனி லைப்ரரி ஒப்பந்தத்தில் இருக்கும். லைப்ரரிகளின் பயன்பாட்டை மிகவும் வசதியாக மாற்ற using for (opens in a new tab) என்பதைப் பயன்படுத்துவதைக் கருத்தில் கொள்ளவும்.

ப்ராக்ஸிகள்

இன்னும் மேம்பட்ட உத்தி ப்ராக்ஸி (proxy) அமைப்பாக இருக்கும். லைப்ரரிகள் பின்புலத்தில் DELEGATECALL ஐப் பயன்படுத்துகின்றன, இது அழைக்கும் ஒப்பந்தத்தின் நிலையுடன் (state) மற்றொரு ஒப்பந்தத்தின் செயல்பாட்டைச் செயல்படுத்துகிறது. ப்ராக்ஸி அமைப்புகளைப் பற்றி மேலும் அறிய இந்த வலைப்பதிவு இடுகையைப் (blog post) (opens in a new tab) பார்க்கவும். அவை உங்களுக்குக் கூடுதல் செயல்பாட்டை வழங்குகின்றன, எ.கா., அவை மேம்படுத்தும் திறனை (upgradability) செயல்படுத்துகின்றன, ஆனால் அவை நிறையச் சிக்கல்களையும் சேர்க்கின்றன. எந்தவொரு காரணத்திற்காகவும் இது மட்டுமே உங்களின் ஒரே விருப்பமாக இருந்தாலொழிய, ஒப்பந்த அளவுகளைக் குறைப்பதற்காக மட்டுமே நான் அவற்றைச் சேர்க்க மாட்டேன்.

நடுத்தர தாக்கம்

செயல்பாடுகளை அகற்றுதல்

இது வெளிப்படையானதாக இருக்க வேண்டும். செயல்பாடுகள் ஒரு ஒப்பந்தத்தின் அளவைக் கணிசமாக அதிகரிக்கின்றன.

  • எக்ஸ்டெர்னல் (External): பல நேரங்களில் வசதிக்காக நாம் நிறைய வியூ (view) செயல்பாடுகளைச் சேர்க்கிறோம். நீங்கள் அளவு வரம்பை அடையும் வரை அது முற்றிலும் சரியானது. அதன் பிறகு, முற்றிலும் அவசியமானவற்றைத் தவிர மற்ற அனைத்தையும் அகற்றுவது பற்றி நீங்கள் தீவிரமாகச் சிந்திக்க வேண்டியிருக்கும்.
  • இன்டெர்னல் (Internal): நீங்கள் இன்டெர்னல்/பிரைவேட் (internal/private) செயல்பாடுகளையும் அகற்றலாம், மேலும் அந்தச் செயல்பாடு ஒருமுறை மட்டுமே அழைக்கப்பட்டால், குறியீட்டை இன்லைனில் (inline) வைக்கலாம்.

கூடுதல் மாறிகளைத் (variables) தவிர்த்தல்

1function get(uint id) returns (address,address) {
2 MyStruct memory myStruct = myStructs[id];
3 return (myStruct.addr1, myStruct.addr2);
4}
1function get(uint id) returns (address,address) {
2 return (myStructs[id].addr1, myStructs[id].addr2);
3}

இதுபோன்ற ஒரு எளிய மாற்றம் 0.28kb வித்தியாசத்தை ஏற்படுத்துகிறது. உங்கள் ஒப்பந்தங்களில் இது போன்ற பல சூழ்நிலைகளை நீங்கள் கண்டறிய வாய்ப்புள்ளது, மேலும் அவை அனைத்தும் சேர்ந்து கணிசமான அளவைக் குறைக்கலாம்.

பிழைச் செய்தியைச் சுருக்குதல்

நீண்ட ரிவர்ட் (revert) செய்திகள் மற்றும் குறிப்பாகப் பல வேறுபட்ட ரிவர்ட் செய்திகள் ஒப்பந்தத்தின் அளவைப் பெருக்கலாம். அதற்குப் பதிலாகச் சிறிய பிழைக் குறியீடுகளைப் (error codes) பயன்படுத்தி, அவற்றை உங்கள் ஒப்பந்தத்தில் டீகோட் (decode) செய்யவும். ஒரு நீண்ட செய்தி மிகவும் சிறியதாக மாறலாம்:

1require(msg.sender == owner, "Only the owner of this contract can call this function");
1require(msg.sender == owner, "OW1");

பிழைச் செய்திகளுக்குப் பதிலாகத் தனிப்பயன் பிழைகளைப் (custom errors) பயன்படுத்துதல்

தனிப்பயன் பிழைகள் Solidity 0.8.4 (opens in a new tab) இல் அறிமுகப்படுத்தப்பட்டுள்ளன. உங்கள் ஒப்பந்தங்களின் அளவைக் குறைக்க அவை ஒரு சிறந்த வழியாகும், ஏனெனில் அவை செலக்டர்களாக (selectors) ABI-குறியாக்கம் செய்யப்பட்டுள்ளன (செயல்பாடுகளைப் போலவே).

1error Unauthorized();
2
3if (msg.sender != owner) {
4 revert Unauthorized();
5}

ஆப்டிமைசரில் குறைந்த ரன் (run) மதிப்பைக் கருத்தில் கொள்ளுதல்

நீங்கள் ஆப்டிமைசர் அமைப்புகளையும் மாற்றலாம். 200 என்ற இயல்புநிலை மதிப்பு (default value) என்பது, ஒரு செயல்பாடு 200 முறை அழைக்கப்படுவது போல பைட் குறியீட்டை (bytecode) மேம்படுத்த முயற்சிக்கிறது என்பதாகும். நீங்கள் அதை 1 ஆக மாற்றினால், ஒவ்வொரு செயல்பாட்டையும் ஒரு முறை மட்டுமே இயக்குவதற்கான நிலைக்கு மேம்படுத்துமாறு ஆப்டிமைசருக்கு நீங்கள் அடிப்படையில் கூறுகிறீர்கள். ஒரு முறை மட்டுமே இயக்குவதற்கு மேம்படுத்தப்பட்ட செயல்பாடு என்பது, அது பயன்படுத்தப்படுவதற்கே (deployment) மேம்படுத்தப்பட்டுள்ளது என்பதாகும். இது செயல்பாடுகளை இயக்குவதற்கான எரிவாயுச் செலவுகளை (gas costs) அதிகரிக்கிறது என்பதை நினைவில் கொள்ளவும், எனவே நீங்கள் இதைச் செய்ய விரும்பாமல் இருக்கலாம்.

சிறிய தாக்கம்

செயல்பாடுகளுக்கு ஸ்ட்ரக்ட்களை (structs) அனுப்புவதைத் தவிர்த்தல்

நீங்கள் ABIEncoderV2 (opens in a new tab) ஐப் பயன்படுத்துகிறீர்கள் என்றால், ஒரு செயல்பாட்டிற்கு ஸ்ட்ரக்ட்களை அனுப்பாமல் இருப்பது உதவக்கூடும். அளவுருவை (parameter) ஒரு ஸ்ட்ரக்ட்டாக அனுப்புவதற்குப் பதிலாக, தேவையான அளவுருக்களை நேரடியாக அனுப்பவும். இந்த எடுத்துக்காட்டில் நாம் மேலும் 0.1kb ஐச் சேமித்துள்ளோம்.

1function get(uint id) returns (address,address) {
2 return _get(myStruct);
3}
4
5function _get(MyStruct memory myStruct) private view returns(address,address) {
6 return (myStruct.addr1, myStruct.addr2);
7}
1function get(uint id) returns(address,address) {
2 return _get(myStructs[id].addr1, myStructs[id].addr2);
3}
4
5function _get(address addr1, address addr2) private view returns(address,address) {
6 return (addr1, addr2);
7}

செயல்பாடுகள் மற்றும் மாறிகளுக்கான சரியான விசிபிலிட்டியை (visibility) அறிவித்தல்

  • வெளியிலிருந்து மட்டுமே அழைக்கப்படும் செயல்பாடுகள் அல்லது மாறிகளா? அவற்றை public என்பதற்குப் பதிலாக external என அறிவிக்கவும்.
  • ஒப்பந்தத்திற்குள்ளிருந்து மட்டுமே அழைக்கப்படும் செயல்பாடுகள் அல்லது மாறிகளா? அவற்றை public என்பதற்குப் பதிலாக private அல்லது internal என அறிவிக்கவும்.

மாடிஃபையர்களை (modifiers) அகற்றுதல்

மாடிஃபையர்கள், குறிப்பாகத் தீவிரமாகப் பயன்படுத்தப்படும்போது, ஒப்பந்த அளவின் மீது குறிப்பிடத்தக்க தாக்கத்தை ஏற்படுத்தக்கூடும். அவற்றை அகற்றுவதைக் கருத்தில் கொண்டு, அதற்குப் பதிலாகச் செயல்பாடுகளைப் பயன்படுத்தவும்.

1modifier checkStuff() {}
2
3function doSomething() checkStuff {}
1function checkStuff() private {}
2
3function doSomething() { checkStuff(); }

இந்தக் குறிப்புகள் ஒப்பந்த அளவைக் கணிசமாகக் குறைக்க உங்களுக்கு உதவ வேண்டும். மீண்டும் ஒருமுறை நான் வலியுறுத்திக் கூறுகிறேன், மிகப்பெரிய தாக்கத்தை ஏற்படுத்த முடிந்தால் எப்போதும் ஒப்பந்தங்களைப் பிரிப்பதில் கவனம் செலுத்துங்கள்.

பக்கம் கடைசியாகப் புதுப்பிக்கப்பட்டது: 3 மார்ச், 2026

இந்த வழிகாட்டி பயனுள்ளதாக இருந்ததா?