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

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

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

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

நவம்பர் 22, 2016 (புதிய தாவலில் திறக்கப்படும்) அன்று Spurious Dragon வன்-கவை (hard-fork) EIP-170 (புதிய தாவலில் திறக்கப்படும்) ஐ அறிமுகப்படுத்தியது, இது திறன் ஒப்பந்தத்தின் அளவு வரம்பை 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) தாக்குதல்களைத் தடுக்க இந்த வரம்பு அறிமுகப்படுத்தப்பட்டது. ஒரு ஒப்பந்தத்திற்கான எந்தவொரு அழைப்பும் எரிவாயு அடிப்படையில் ஒப்பீட்டளவில் மலிவானது. இருப்பினும், எத்திரியம் கணுக்களுக்கான (nodes) ஒப்பந்த அழைப்பின் தாக்கம், அழைக்கப்படும் ஒப்பந்தக் குறியீட்டின் அளவைப் பொறுத்து விகிதாசாரமின்றி அதிகரிக்கிறது (வட்டிலிருந்து குறியீட்டைப் படித்தல், குறியீட்டை முன்-செயலாக்கம் செய்தல், மெர்கல் சான்றில் தரவைச் சேர்த்தல்). தாக்குபவர் மற்றவர்களுக்கு அதிக வேலைப்பளுவை ஏற்படுத்தக் குறைவான வளங்களே தேவைப்படும் இத்தகைய சூழ்நிலை ஏற்படும்போதெல்லாம், DOS தாக்குதல்களுக்கான சாத்தியக்கூறுகள் உருவாகின்றன.

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

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

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

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

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

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

நிரலகங்கள்

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

ப்ராக்ஸிகள் (Proxies)

ப்ராக்ஸி அமைப்பு என்பது மிகவும் மேம்பட்ட உத்தியாக இருக்கும். நிரலகங்கள் பின்னணியில் DELEGATECALL ஐப் பயன்படுத்துகின்றன, இது அழைக்கும் ஒப்பந்தத்தின் நிலையுடன் மற்றொரு ஒப்பந்தத்தின் செயல்பாட்டைச் செயல்படுத்துகிறது. ப்ராக்ஸி அமைப்புகளைப் பற்றி மேலும் அறிய இந்த வலைப்பதிவு இடுகையைப் (புதிய தாவலில் திறக்கப்படும்) பார்க்கவும். அவை உங்களுக்குக் கூடுதல் செயல்பாட்டை வழங்குகின்றன, எ.கா., அவை மேம்படுத்தும் திறனைச் செயல்படுத்துகின்றன, ஆனால் அவை நிறையச் சிக்கல்களையும் சேர்க்கின்றன. எந்தவொரு காரணத்திற்காகவும் இது மட்டுமே உங்கள் ஒரே விருப்பமாக இல்லாவிட்டால், ஒப்பந்த அளவுகளைக் குறைப்பதற்காக மட்டுமே நான் அவற்றைச் சேர்க்க மாட்டேன்.

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

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

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

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

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

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

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

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

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

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

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

error Unauthorized();

if (msg.sender != owner) {
    revert Unauthorized();
}

உகப்பாக்கியில் (optimizer) குறைந்த இயக்க மதிப்பைக் கருத்தில் கொள்ளுதல்

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

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

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

நீங்கள் ABIEncoderV2 (புதிய தாவலில் திறக்கப்படும்) ஐப் பயன்படுத்துகிறீர்கள் என்றால், ஒரு செயல்பாட்டிற்குக் கட்டமைப்புகளை (structs) அனுப்பாமல் இருப்பது உதவக்கூடும். அளவுருவை (parameter) ஒரு கட்டமைப்பாக அனுப்புவதற்குப் பதிலாக, தேவையான அளவுருக்களை நேரடியாக அனுப்பவும். இந்த எடுத்துக்காட்டில் நாம் மேலும் 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);
}

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

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

மாற்றிகளை (modifiers) அகற்றுதல்

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

modifier checkStuff() {}

function doSomething() checkStuff {}
function checkStuff() private {}

function doSomething() { checkStuff(); }

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