مرکزی مواد پر جائیں

کانٹریکٹ کے سائز کی حد سے نمٹنے کے لیے کانٹریکٹس کو چھوٹا کرنا

Solidity
اسمارٹ کانٹریکٹس
اسٹوریج
درمیانی
مارکس واس
26 جون، 2020
7 منٹ کی پڑھائی

حد کیوں ہے؟

November 22, 2016 (opens in a new tab) کو Spurious Dragon ہارڈ فورک نے EIP-170 (opens in a new tab) متعارف کرایا جس نے اسمارٹ کانٹریکٹ کے سائز کی حد 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) تک پہنچنے کی بہترین حکمت عملی یہ ہے کہ پہلے بڑے اثر والے طریقوں پر توجہ مرکوز کی جائے۔ زیادہ تر معاملات میں صرف اپنی خوراک کو ٹھیک کرنے سے آپ وہاں پہنچ جائیں گے، لیکن بعض اوقات آپ کو تھوڑی اور ضرورت ہوتی ہے۔ پھر آپ کچھ ورزش (درمیانہ اثر) یا یہاں تک کہ سپلیمنٹس (چھوٹا اثر) شامل کر سکتے ہیں۔

بڑا اثر

اپنے کانٹریکٹس کو الگ کریں

یہ ہمیشہ آپ کا پہلا طریقہ ہونا چاہیے۔ آپ کانٹریکٹ کو متعدد چھوٹے کانٹریکٹس میں کیسے الگ کر سکتے ہیں؟ یہ عام طور پر آپ کو اپنے کانٹریکٹس کے لیے ایک اچھا آرکیٹیکچر بنانے پر مجبور کرتا ہے۔ کوڈ پڑھنے کی اہلیت کے نقطہ نظر سے چھوٹے کانٹریکٹس کو ہمیشہ ترجیح دی جاتی ہے۔ کانٹریکٹس کو تقسیم کرنے کے لیے، اپنے آپ سے پوچھیں:

  • کون سے فنکشنز ایک ساتھ تعلق رکھتے ہیں؟ فنکشنز کا ہر سیٹ اپنے الگ کانٹریکٹ میں بہترین ہو سکتا ہے۔
  • کن فنکشنز کو کانٹریکٹ کی اسٹیٹ یا اسٹیٹ کے صرف ایک مخصوص سب سیٹ کو پڑھنے کی ضرورت نہیں ہے؟
  • کیا آپ اسٹوریج اور فنکشنلٹی کو تقسیم کر سکتے ہیں؟

لائبریریز

فنکشنلٹی کوڈ کو اسٹوریج سے دور لے جانے کا ایک آسان طریقہ لائبریری (opens in a new tab) کا استعمال ہے۔ لائبریری فنکشنز کو internal کے طور پر ڈکلیئر نہ کریں کیونکہ وہ کمپائلیشن کے دوران براہ راست کانٹریکٹ میں شامل (opens in a new tab) ہو جائیں گے۔ لیکن اگر آپ public فنکشنز استعمال کرتے ہیں، تو وہ درحقیقت ایک الگ لائبریری کانٹریکٹ میں ہوں گے۔ لائبریریز کے استعمال کو مزید آسان بنانے کے لیے using for (opens in a new tab) کے استعمال پر غور کریں۔

پراکسیز

ایک زیادہ جدید حکمت عملی پراکسی سسٹم ہوگی۔ لائبریریز بیک اینڈ میں DELEGATECALL کا استعمال کرتی ہیں جو کال کرنے والے کانٹریکٹ کی اسٹیٹ کے ساتھ کسی دوسرے کانٹریکٹ کے فنکشن کو آسانی سے ایگزیکیوٹ کرتا ہے۔ پراکسی سسٹمز کے بارے میں مزید جاننے کے لیے یہ بلاگ پوسٹ (opens in a new tab) دیکھیں۔ وہ آپ کو مزید فنکشنلٹی دیتے ہیں، مثلاً، وہ اپ گریڈ ایبلٹی کو فعال کرتے ہیں، لیکن وہ بہت زیادہ پیچیدگی بھی بڑھاتے ہیں۔ میں انہیں صرف کانٹریکٹ کے سائز کو کم کرنے کے لیے شامل نہیں کروں گا جب تک کہ کسی بھی وجہ سے یہ آپ کا واحد آپشن نہ ہو۔

درمیانہ اثر

فنکشنز کو ہٹائیں

یہ واضح ہونا چاہیے۔ فنکشنز کانٹریکٹ کے سائز میں کافی حد تک اضافہ کرتے ہیں۔

  • External: اکثر اوقات ہم سہولت کی وجوہات کی بنا پر بہت سے view فنکشنز شامل کرتے ہیں۔ یہ تب تک بالکل ٹھیک ہے جب تک کہ آپ سائز کی حد تک نہ پہنچ جائیں۔ پھر آپ کو واقعی انتہائی ضروری فنکشنز کے علاوہ باقی سب کو ہٹانے کے بارے میں سوچنا چاہیے۔
  • Internal: آپ internal/private فنکشنز کو بھی ہٹا سکتے ہیں اور کوڈ کو آسانی سے ان لائن کر سکتے ہیں جب تک کہ فنکشن کو صرف ایک بار کال کیا جائے۔

اضافی ویری ایبلز سے گریز کریں

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 میسجز اور خاص طور پر بہت سے مختلف revert میسجز کانٹریکٹ کو بڑھا سکتے ہیں۔ اس کے بجائے مختصر ایرر کوڈز استعمال کریں اور انہیں اپنے کانٹریکٹ میں ڈی کوڈ کریں۔ ایک طویل میسج بہت مختصر ہو سکتا ہے:

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

ایرر میسجز کے بجائے کسٹم ایررز کا استعمال کریں

کسٹم ایررز کو Solidity 0.8.4 (opens in a new tab) میں متعارف کرایا گیا ہے۔ یہ آپ کے کانٹریکٹس کے سائز کو کم کرنے کا ایک بہترین طریقہ ہیں، کیونکہ وہ سلیکٹرز کے طور پر ABI-encoded ہوتے ہیں (بالکل فنکشنز کی طرح)۔

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

آپٹیمائزر میں کم رن ویلیو پر غور کریں

آپ آپٹیمائزر کی سیٹنگز کو بھی تبدیل کر سکتے ہیں۔ 200 کی ڈیفالٹ ویلیو کا مطلب ہے کہ یہ بائٹ کوڈ کو اس طرح آپٹیمائز کرنے کی کوشش کر رہا ہے جیسے کسی فنکشن کو 200 بار کال کیا گیا ہو۔ اگر آپ اسے 1 میں تبدیل کرتے ہیں، تو آپ بنیادی طور پر آپٹیمائزر کو بتاتے ہیں کہ وہ ہر فنکشن کو صرف ایک بار چلانے کے کیس کے لیے آپٹیمائز کرے۔ صرف ایک بار چلانے کے لیے آپٹیمائز کیے گئے فنکشن کا مطلب ہے کہ یہ خود ڈیپلائمنٹ کے لیے آپٹیمائز کیا گیا ہے۔ آگاہ رہیں کہ اس سے فنکشنز کو چلانے کے لیے گیس کی لاگت بڑھ جاتی ہے، لہذا ہو سکتا ہے آپ ایسا نہ کرنا چاہیں۔

چھوٹا اثر

فنکشنز میں اسٹرکٹس پاس کرنے سے گریز کریں

اگر آپ ABIEncoderV2 (opens in a new tab) استعمال کر رہے ہیں، تو یہ فنکشن میں اسٹرکٹس پاس نہ کرنے میں مدد کر سکتا ہے۔ پیرامیٹر کو اسٹرکٹ کے طور پر پاس کرنے کے بجائے، مطلوبہ پیرامیٹرز کو براہ راست پاس کریں۔ اس مثال میں ہم نے مزید 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}

فنکشنز اور ویری ایبلز کے لیے درست وزیبلٹی ڈکلیئر کریں

  • وہ فنکشنز یا ویری ایبلز جو صرف باہر سے کال کیے جاتے ہیں؟ انہیں public کے بجائے external کے طور پر ڈکلیئر کریں۔
  • وہ فنکشنز یا ویری ایبلز جو صرف کانٹریکٹ کے اندر سے کال کیے جاتے ہیں؟ انہیں public کے بجائے private یا internal کے طور پر ڈکلیئر کریں۔

موڈیفائرز کو ہٹائیں

موڈیفائرز، خاص طور پر جب کثرت سے استعمال کیے جائیں، کانٹریکٹ کے سائز پر نمایاں اثر ڈال سکتے ہیں۔ انہیں ہٹانے پر غور کریں اور اس کے بجائے فنکشنز استعمال کریں۔

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

ان تجاویز سے آپ کو کانٹریکٹ کے سائز کو نمایاں طور پر کم کرنے میں مدد ملنی چاہیے۔ ایک بار پھر، میں اس بات پر جتنا بھی زور دوں کم ہے، سب سے بڑے اثر کے لیے اگر ممکن ہو تو ہمیشہ کانٹریکٹس کو تقسیم کرنے پر توجہ دیں۔

صفحہ کی آخری اپ ڈیٹ: 25 فروری، 2026

کیا یہ ٹیوٹوریل مددگار تھا؟