ذخیره‌سازی کارآمد اتریوم (Ethereum)

ممکن است تصور کنید که قراردادهای متامورفیک (دگردیسی) به هیچ کاری نمی‌آیند و هیچ استفاده‌ی قانونی نمی‌توان از آن‌ها کرد. در این مقاله نمونه‌ای از استفاده از این قراردادها برای کاهش هزینه‌ها و انعطاف‌پذیری بیشتر را می‌بینیم.

0 80

ذخیره‌سازی کارآمد اتریوم:

در همین حال که مشتاقانه به دنبال کاهش هزینه‌های Gas هستید، ناگزیر متوجه می‌شوید که ذخیره‌سازی بر روی زنجیره معمولاً بیشترین هزینه را در پی دارد. روش‌های بسیاری به خوبی ارائه شده‌اند تا این هزینه‌ها را کاهش دهند، از جمله بررسی اثبات‌های مِرکل (Merkle) در مقابل یک روت هَش (root hash)، ذخیره‌سازی هش‌های IPFS به جای جایگذاری مستقیم داده بر روی زنجیره و دسته‌بندی چندین ارزش به شکل کلمات مجزای 32 بیتی (احتمالاً از طریق به‌کارگیری آدرس‌های کارآمد). به جای پرداختن به جزئیات هر یک از این روش‌ها، ما قرار است یک تکنیک تازه و عجیب را بررسی کنیم.

CREATE2 امکان ایجاد قراردادهای دگردیسی (metamorphic)، یا قراردادهایی که می‌توانند با کد زمان اجرای (Runtime Code) جدید مجدداً به کار گرفته شوند، را فراهم می‌کند. ذهنیت غالب فعلی اینگونه است که این قراردادها بسیار بد هستند و بایستی به هر قیمتی از آن‌ها دوری کرد. عقیده‌ مخالف این است که قراردادهای متامورفیک در واقع به نسبت جذاب هستند و می‌توانند به عنوان قراردادهای انعطاف‌پذیر با بارگذاری سبک به کار برده شوند. این کاربرد تازه و کمی نگران‌کننده را برای قراردادهای متامورفیک در نظر بگیرید: آن‌ها می‌توانند با سواستفاده از طرز کار فعلی Gas ، کد زمان اجرای خود را به عنوان منبع ذخیره‌سازی پویای ارزان‌تری استفاده کنند.

یک لحظه دست نگه دار دوست عزیز: تو الان گفتی کد زمان اجرا؟ یعنی چیزی توی مایه‌های 0x60806040… ؟ به جای ذخیره‌سازی؟ اصلاً این چطور عمل می‌کنه؟

به طور مختصر: شما با ذخیره‌سازی داده به شیوه‌ی مرسوم در یک قرارداد کارخانه آغاز می‌کنید که در ادامه یک قرارداد متامورفیک (metamorphic) را به کار گرفته و داده را بازیابی کرده و آن را داخل کد زمان اجرا قرار می‌دهد؛ در کنار یک کلمه‌ی کنترل ابتدایی که به کارخانه امکان « خود تخریبی » (SELFDESTRUCT) را می‌دهد. سپس، شما داده را با EXTCODECOPY از قرارداد متامورفیک می‌خوانید.

بنابراین سوال بهتر این است که چرا کسی با عقل سلیم ممکن است چنین راه پرپیچ و خمی را صرفاً برای ذخیره‌سازی و خوانش داده انتخاب کند؟ بسیار خوب، SSTORE برای ذخیره‌ی هر کلمه‌ی 32 بایتی از داده، 20000 Gas هزینه در پی دارد، در حالی که هزینه‌ی CREATE2، 32000 Gas است (با کمی مبلغ اضافه برای هزینه‌ی هش‌کردن کد اولیه آغاز قرارداد)، به علاوه‌ی 200 Gas برای هر بایتی که در کد زمان اجرای قرارداد ذخیره می‌شود که در نهایت به 6400 Gas برای هر کلمه‌ی ذخیره‌شده منتهی می‌شود. ما می‌توانیم داده‌ی خود را برای ذخیره‌سازی موقت نوشته، یک قرارداد ذخیره‌سازی که داده‌ها را در خود بکشد را به کار گرفته و آن را در کد زمان اجرای داده بگذاریم و سپس حافظه‌ی موقت را پاک کنیم تا 15000 Gas به ازای هر کلمه به ما برگردد. این به ما هزینه‌ی جمعی پایه‌ 11400 Gas به ازای هر کلمه را می‌دهد که 8600 Gas ارزان‌تر از SSTORE است.

از اینجا به بعد، ما این روش استفاده از کد زمان اجرای یک قرارداد متامورفیک برای ذخیره‌سازی پویا را با RSTORE مورد اشاره قرار خواهیم داد.

RSTORE: بیشتر پس‌انداز کنید (گاهی اوقات)

پس تا زمانی که اندازه‌ی داده‌ای که نیاز به ذخیره‌ی آن داریم، مخارج اضافه‌شده از به‌کارگیری یک قرارداد را خنثی کند، می‌توان استدلال کرد که استفاده از کد زمان اجرای قرارداد به جای قرارداد ذخیره‌سازی بهتر است. در عمل، چند مرحله و ترفند دیگر هم وجود دارد اما لب کلام این است: استفاده از RSTORE برای راه‌اندازی اولیه ارزان‌تر از SSTORE است تا زمانی که نیاز به ذخیره‌ی بیش از یک لغت باشد. همینطور که شما کلمات بیشتری را به این ترکیب اضافه می‌کنید، این در نهایت با تقریباً دو سوم هزینه‌ی ذخیره‌سازی استاندارد برابری می‌کند.

‌ ذخیره‌سازی کارآمد اتریوم (Ethereum)

نکته دیگری که باید به آن توجه کرد: برای آرایه‌های ذخیره‌سازی با اندازه‌ی پویا مانند bytes و string، شما نیازی به ذخیره‌ی اختلاف (offset) و طول ندارید چرا که « اختلاف » همان آدرس قرارداد متامورفیک است و طول می‌تواند از طریق EXTCODESIZE تعیین شود.

همیشه یک معاوضه در میان است

به‌روزرسانی حافظه از طریق RSTORE پرهزینه‌تر از SSTORE است و به عنوان پاشنه‌ی آشیل این روش در نظر گرفته می‌شود. از آنجایی که به‌روزرسانی یک شکاف حافظه‌ی موجود تنها 5000 Gas هزینه دارد، هیچ پس‌انداز قابل‌توجهی در استفاده از حافظه موقت برای پرکردن کد زمان اجرای قراردادهای متامورفیک وجود ندارد. هزینه‌ی به‌روزرسانی حافظه همانطور که شما لغات دیگری اضافه می‌کنید، از هم فاصله گرفته تا اینکه RSTORE تقریباً دو برابر گران‌تر از SSTORE می‌شود.

‌ ذخیره‌سازی کارآمد اتریوم (Ethereum)

ما بخشی از هزینه‌ی اضافی را با استفاده از 24000 Gasی که به هنگام SELFDESTRUCT قرارداد متامورفیک قبلی به ما برگردانده می‌شود، خنثی کردیم اما طول کد زمان اجرای قدیمی تأثیری بر اندازه‌ی این بازپرداخت ندارد و ما همچنان بایستی 5000 Gas برای SELFDESTRUCT خود کد اجرایی (opcode) بپردازیم. علاوه بر این، شما باید تمامی حافظه را یکجا به‌روزرسانی کنید، به همین جهت مشخص است که شما می‌توانید در صورت نیاز حافظه‌ی خود را به چندین قرارداد تقسیم کنید.

RLOAD: یک رنگین‌کمان خواندن

مشکل اینجاست: پس‌اندازهای واقعی هزینه از نوشتن داده نمی‌آیند بلکه از خواندن آن حاصل می‌شوند. هر لغتی که از طریق SLOAD بازگردانی شده، 200 Gas هزینه دارد اما اگر ما داده را از کد زمان اجرا بازگردانی کنیم، تنها یک هزینه‌ی ثبات 700 Gas برای EXTCODESIZE و 700 Gas برای EXTCODECOPY (یا صرفاً EXTCODECOPY برای داده با اندازه ثابت) و یک مبلغ جزئی 3 Gas برای هر کلمه پرداختیم. با استفاده از این روش که ما به آن تحت عنوان RLOAD اشاره می‌کنیم، تنها اندکی بیشتر از بازگردانی مقادیر کوچک داده هزینه برمی‌دارد، اما با زیادشدن کلمات پس‌انداز عظیمی را فراهم می‌کند؛ این روش تنها حدود یک‌سوم بازگردانی مقادیر بزرگ داده هزینه دارد.

‌ ذخیره‌سازی کارآمد اتریوم (Ethereum)

کارکردن از راه دور

در نهایت، کاربردی وجود دارد که RLOAD همواره ارزان‌تر از SLOAD است. زمانی که قرارداد دیگری نیاز به دسترسی به حافظه داشته باشد، آن‌ها می‌توانند تنها کد زمان اجرای قرارداد حافظه‌ی متامورفیک مناسب را به طور مستقیم گرفته و از هزینه‌های اضافی یک CALL یا STATICCALL در کنار هزینه‌ی اضافی پردازش تماس بر روی انتهای دریافت‌کننده جلوگیری کنند. این روش برای بازگردانی داده با هر اندازه‌ای کم‌هزینه‌تر است و به اندازه‌ی یک‌چهارم بازگردانی مقادیر بزرگ داده هزینه برمی‌دارد که آن را گزینه‌ی مناسب فوق‌العاده‌ای برای ریجستری‌ها و دیگر اپلیکیشن‌هایی که به در دسترس‌قراردادن حافظه برای انواع مختلفی از قراردادهای مصرف‌کننده نیاز دارند، تبدیل می‌کند.

‌ ذخیره‌سازی کارآمد اتریوم (Ethereum)

واقعیت بدون پرده‌ی RSTORE و RLOAD

برای درنظرگرفتن هزینه‌ی نسبی RSTORE و RLOAD در مقایسه با SSTORE و SLOAD، ما می‌توانیم یک ضریب هزینه براساس اندازه‌ی داده‌ی ذخیره و بازگردانی‌شده محاسبه کنیم (همراه پاک‌کردن 21000 Gas برای تراکنش بالاسری) و نسبت‌ها را با یکدیگر مقایسه کنیم. به یاد داشته باشید که این ضرایب می‌توانند بسته به اینکه شما تا چه اندازه مجاز به کاهش هستید، تغییر کنند چرا که کاهش‌ها در نصف کل هزینه‌ی تراکنش محدود شده‌اند.

‌ ذخیره‌سازی کارآمد اتریوم (Ethereum)

اساساً، شما اگر به یک منبع خوانش قوی حافظه نیاز دارید که اغلب تغییر نکند، بهتر است استفاده از RSTORE و RLOAD را در نظر بگیرید. علاوه بر این، اگر حافظه نیاز به تغییر نداشته باشد، شما می‌توانید آن را کاملاً با قراردادهای متامورفیک انجام دهید و تنها قراردادهای ثابت را که شامل داده‌های ضروری هستند با کد زمان اجرا پیاده‌سازی کنید. یک مثال فوق‌العاده برای زمانی که این موضوع راهگشا است، انتشار اثبات‌های طولانی است که می‌توانند به ارزانی بر روی زنجیره تأیید شوند.

برای اینکه RSTORE و RLOAD را در عبارات مشخص‌تری قرار دهیم: فرض کنید شما یک توکن مجوزدار دارید و این توکن نیاز به این دارد که چهار صفت (attribute) در یک رجیستری برای یک آدرس داده‌شده مشخص شوند تا آن آدرس بتواند توکن‌ها را دریافت کند. صفات احتمالاً خیلی زیاد تغییر داده نمی‌شوند اما همچنان نیاز دارند تا قابل‌تغییر باشند. RSTORE از تقریباً 30000 Gas کمتری برای مشخص‌کردن اولیه‌ی تمامی این صفات استفاده می‌کند و RLOAD هر بار که آن‌ها طی انتقال چک می‌شوند، نزدیک 2000 Gas پس‌انداز می‌کند. اگر شما 1000 کاربر در روز که صفات را مشخص می‌کنند و 5000 انتقال توکن در روز (که ممکن است آن را یکی از مورد استفاده‌ترین توکن‌های حال حاضر تبدیل کند) داشته باشید، جمع آن‌ها به 40 میلیون Gas پس‌انداز در هر روز می‌رسد. با قیمت‌های کنونی  5 gwei) Gas) و قیمت اتر (134 دلار) آن مبلغ معادل تقریباً 10000 دلار پس‌انداز سالانه می‌شود.

ساختن یک قرارداد ذخیره متامورفیک

همانطور که ممکن است انتظار داشته باشید، پیاده‌سازی یک قرارداد ذخیره متامورفیک با کد آغازگر ثابت و کد زمان اجرای دلخواه، شامل اندکی جادوگری پیچیده می‌شود که سالیدیتی (Solidity) برای برآمدن از پس آن ساخته نشده است. می‌دانید چه چیزی قابلیت اجرای آن را دارد؟ آپ‌کد EVM خام.

(Metamorphic Storage Contract Initialization Code (69 bytes

0x5860008158601c335a630c85c0028752fa153d602090039150607381533360601b600152653318585733ff60d01b601552602080808403918260d81b601b52602001903ef3

برای شروع، ما نیاز به بازیابی کد به حافظه داریم. قرارداد متامورفیک از قرارداد ایجاد‌کننده انتظار دارد که یک تابع مشاهده‌ی ()getTransientStorage را در دسترس قرار دهد که کدی را که انتخابگر تابع آن 0x0c85c002 بود ، بازگردانی خواهد کرد. اولین 17 بایت کد آغازکننده به این اختصاص یافته است.

 

حالا که داده در بافر برگشت قرار گرفته، ما می‌توانیم سراغ مشخص‌کردن پایین دسته برویم تا محل قرارگیری کد زمان اجرای نهایی را آماده کنیم. تابع () getTransientStorageما آرایه‌ای از بایت‌های انکودشده با ABI را برمی‌گرداند که به ترتیب شامل دو کلمه‌ی اضافی در جلو برای انحراف و طول می‌شود. کد زمان اجرای ما تنها یک کلمه‌ی اضافی در جلو دارد-یک کلمه‌ی کنترل که به سازنده‌ی قرارداد متامورفیک (و تنها به سازنده) اجازه می‌دهد تا قرارداد را از طریق یک فراخوانی (CALL) ساده‌ی خالی SELFDESTRUCT کند. بنابراین، ما 32 را از هر طولی که از RETRUNDATASIZE بدست می‌آوریم، کم می‌کنیم و آن را برای استفاده‌ی بعدی کنار می‌اندازیم. (به عنوان یک نکته‌ی سریع جانبی، این به آن معناست که ما می‌توانیم در واقع لغات جزئی را در حافظه زمان اجرا ذخیره کنیم و تنها برای آنچه استفاده می‌کنیم، هزینه بپردازیم، برخلاف حافظه‌ی معمولی که در آن هر شکاف از تمام 32 بایت استفاده می‌کند.)

 

پس از این، ما شروع به ایجاد لغت کنترلی می‌کنیم و آن را در چهار بخش، هر بار یک قسمت، داخل حافظه قرار می‌دهیم. سه بخش اول (یا 27 بایت) لغت کنترلی، مبتنی بر قراردادهای کوچک (child) ایجادشده توسط نوع GST2 از GasToken ایجاد شدند و به شکل زیر هستند:

 

بدون اتلاف وقت، ما سراغ قراردادن آن‌ها در حافظه می‌رویم.

 

ما چهارمین بخش لغت کنترل را مشخص می‌کنیم که طول داده را برای راحتی پشت سر آرایش دسته برای RETURNDATACOPY انکود می‌کند تا برخی از عملیات را ذخیره کند زیرا آن‌ها از برخی مقادیر مشترک استفاده می‌کنند.

 

در نهایت، ما داده را از بافر برگشت با استفاده از RETURNDATACOPY، با صرفنظر کردن از 64 بایت اول با انحراف و طول، کپی می‌کنیم و آن‌ها را بعد از 32 بایت اشغال‌شدن توسط کلمه کنترل قرار می‌دهیم، سپس برمی‌گردیم تا قرارداد متامورفیک را با تمام داده‌های ذخیره‌شده در کد زمان اجرا پیاده‌سازی کنیم!

 

قطعاً راه‌هایی برای بهینه‌سازی بیشتر و پوشش برخی لبه‌های تیز وجود دارد اما این برنامه، کار را بدون سر و صدای زیاد انجام می‌دهد.

زمان تراکنش

اگر شما با مکانیک SELFDESTRUCT آشنا هستید، می‌دانید که وقتی به آپ‌کد دست می‌یابیم، قرارداد در واقع از حالت پاک نمی‌شود. به جای آن، این قرارداد برای حذف‌شدن در زیرحالت تراکنش برنامه‌ریزی می‌شود که تنها در پایان تراکنش به راه می‌افتد. این یک چالش را پیش روی RSTORE می‌گذارد، چرا که این حالت دو تراکنش مجزا برای به‌روزرسانی یک کد زمان اجرای مربوط به قرارداد متامورفیک داده‌شده را نیاز خواهد داشت.

با این حال، ما می‌توانیم این محدودیت را با بکارگیری یک قرارداد حافظه‌ اولیه و ثانویه به طور همزمان دور بزنیم. بنابراین، ما می‌توانیم یکی را به‌روزرسانی کرده و به طور همزمان دیگری را SELFDESTRUCT کنیم و از EXTCODEHASH برای تعیین اینکه از کدام قرارداد خوانده شود، استفاده کنیم. این روش به ما اجازه می‌دهد تا حافظه‌ی زمان اجرا را در یک تراکنش به‌روزرسانی کنیم اما هنوز تنها اجازه‌ی یک به‌روزرسانی برای هر تراکنش را می‌دهد و می‌تواند منجر به داده‌ی بین تراکنشی کهنه شود. بنابراین، توصیه می‌شود که از RSTORE در یک تراکنش واحد اتمی استفاده شود.

تکالیف اضافی برای بدهی اضافی

RSTORE و RLOAD می‌توانند براساس مورد استفاده‌ی به‌خصوص شما با بکارگیری یک آدرس اجرای کارآمد و دسته‌بندی فشرده‌تر کلمه کنترل و داده زمان اجرا در قراردادهای حافظه، با پاک‌کردن قرارداد حافظه‌ی ثانویه زمانی که نیازی به آن نیست و با استفاده از حافظه‌ی استاندارد در مکان یک حافظه‌ی موقت زمانی که به‌روزرسانی‌های بیشتری لازم باشد، بیشتر بهینه شوند. صرفنظر از آن، این پیاده‌سازی امکان‌پذیری آن برای استفاده از کد زمان اجرای قرارداد متامورفیک در محل حافظه برای کاهش هزینه‌های Gas را به تصویر می‌کشد؛ به خصوص اپلیکیشن‌هایی با خوانش سنگین که به به‌روزرسانی‌های متعدد نیازی ندارند.

اگر شما دوست دارید تا درباره‌ی اجزای درونی RSTORE و RLOAD دقیق‌تر شوید یا ایده‌هایی برای چگونگی بهبود بیشتر تکنیک دارید، نگاهی به GitHub repo بیاندازید. اگر فکر می‌کنید این می‌تواند برای شما سودمند باشد و دوست دارید آن را اجرا کنید، مطمئن شوید که برای زمانیکه تغییرات آتی در محاسبات Gas در نهایت مقیاس‌ها را برعلیه این استراتژی تغییر دهد، برنامه دارید و اگر شما همچنان فکر می‌کنید که قراردادهای متامورفیک بدردنخور و بدون هیچ مورد استفاده‌ی قانونی هستند، من را در جریان بگذارید تا شاید در یک زمانی با هم بحث کنیم.

منبع

شاید از این مطالب هم خوشتان بیاید.

ارسال پاسخ

آدرس ایمیل شما منتشر نخواهد شد.