staticcall در سالیدیتی

Staticcall عملکردی مشابه فراخوانی معمول در اتریوم دارد، با این تفاوت که اگر باعث تغییر در وضعیت (State) شود، تراکنش را برمی‌گرداند (revert می‌کند). این نوع فراخوانی اجازه انتقال اتر را نمی‌دهد. در سطح ماشین مجازی اتریوم (EVM)، این قابلیت به صورت یک دستورالعمل (opcode)، در زبان اسمبلی Yul به شکل یک تابع، و در خود سالیدیتی نیز به عنوان یک تابع داخلی با نام staticcall قابل استفاده است.

معرفی EIP 214 در سالیدیتی

قابلیت Staticcall برای نخستین بار در سال ۲۰۱۷ با پیشنهاد بهبود EIP 214 و در قالب هاردفورک Byzantium به اتریوم اضافه شد. هدف از این قابلیت، افزایش ایمنی در مواردی است که تنها به خروجی تابع نیاز داریم و نمی‌خواهیم هیچ تغییری در وضعیت قرارداد اتفاق بیفتد. اگر تابعی که با staticcall فراخوانی شده، عملیاتی انجام دهد که منجر به تغییر وضعیت شود، اجرای آن با شکست مواجه خواهد شد. این تغییرات شامل موارد زیر است:

  • ثبت یک رویداد (Event)

  • ارسال اتر

  • ایجاد یک قرارداد جدید

  • نابود کردن یک قرارداد

  • تغییر مقدار یک متغیر ذخیره شده در حافظه قرارداد (Storage)

اما خواندن مقادیر از حافظه (Storage) مجاز است. همچنین، فراخوانی‌ای که در آن اتر جابه‌جا نمی‌شود مجاز است، به شرط آنکه موجب تغییر وضعیت نشود.

توابع View در سالیدیتی

در زبان سالیدیتی، توابعی که به صورت view تعریف شده‌اند و در آن‌ها به قراردادهای خارجی ارجاع داده می‌شود، به‌صورت خودکار با استفاده از staticcall کامپایل می‌شوند. این کار باعث می‌شود از تغییر ناخواسته وضعیت در این توابع جلوگیری شود.

مثال ساده

در ادامه، یک مثال ساده از نحوه استفاده از staticcall ارائه می‌شود:

اگر balanceOf باعث تغییر وضعیت شود، اجرای کد متوقف خواهد شد

در صورتی که تابع balanceOf در یک قرارداد توکن، تغییری در وضعیت بلاک چین ایجاد کند (مثلاً با نوشتن در حافظه یا ارسال رویداد)، استفاده از staticcall برای فراخوانی آن باعث برگشت تراکنش (revert) خواهد شد.

پارامترهای متا (Meta Arguments)

مقدار گازی که باید به staticcall منتقل شود، را می‌توان به شکل زیر مشخص کرد:

اما باید توجه داشت که این گاز تحت تأثیر قانون ۶۳/۶۴ تعریف شده در EIP-150 قرار می‌گیرد. بر اساس این قانون، تنها ۶۳/۶۴ گاز باقی‌مانده از تراکنش اصلی به فراخوانی داخلی منتقل می‌شود، تا از مصرف کامل گاز جلوگیری شود.

بردارهای حمله (Attack Vectors)

اگرچه staticcall از بعضی جهات ایمن‌تر از call عادی است، اما این به معنی بی‌نیازی از بررسی‌های امنیتی نیست. در ادامه به دو نوع حمله رایج اشاره می‌کنیم:

حمله انکار سرویس (Denial of Service)

درست است که staticcall اجازه تغییر وضعیت را نمی‌دهد، اما همچنان در برابر حملات مبتنی بر سوءاستفاده از گاز (gas griefing) آسیب‌پذیر است. تصور کنید که تابع balanceOf در یک توکن ERC20 به‌صورت عمدی دارای حلقه بی‌نهایت باشد. در این حالت، حتی اگر فراخوانی برگشت نخورد، قرارداد فراخواننده ممکن است تنها با یک‌شصت‌و‌چهارم گاز باقی‌مانده مواجه شود، که این می‌تواند منجر به توقف یا اختلال در ادامه اجرای کد شود.

بازدرون‌یابی فقط‌خواندنی (Read-Only Re-entrancy)

نوع دیگری از حمله، بازدرون‌یابی فقط‌خواندنی است. در این سناریو، مهاجم با استفاده از وام آنی (flash loan) مقدار دارایی‌ها را به صورت موقتی دستکاری می‌کند. در نتیجه، قراردادهایی که صرفاً با staticcall مقادیر را بررسی می‌کنند، ممکن است با مقدارهای نادرست مواجه شده و در معرض حملاتی از نوع دستکاری اوراکل قرار گیرند.

استفاده از Staticcall در اسمبلی Yul

در زبان اسمبلی Yul، ساختار staticcall به شکل زیر تعریف می‌شود:

پارامترهای out و outsize مشخص می‌کنند که مقدار بازگشتی باید در کدام بخش از حافظه قرار بگیرد. با این حال، معمولاً این دو پارامتر صفر در نظر گرفته می‌شوند، و به جای آن از دو دستور زیر برای مدیریت انعطاف‌پذیر مقدار بازگشتی استفاده می‌شود:

این قابلیت‌ها با EIP 211 معرفی شدند و نیاز به پیش‌بینی اندازه خروجی قرارداد خارجی را از بین بردند.

همچنین پارامترهای in و insize به بخشی از حافظه اشاره دارند که داده‌های ABI-Encoded مربوط به فراخوانی قرارداد خارجی در آن قرار دارد.

مقدار ok نیز نشان می‌دهد که آیا فراخوانی موفقیت‌آمیز بوده یا با شکست مواجه شده است. توجه داشته باشید که در صورت revert، خطا bubbled up نمی‌شود (به سطح بالاتر انتقال نمی‌یابد).

استفاده در قراردادهای از پیش‌ کامپایل‌ شده (Precompiled Contracts)

برای تعامل با قراردادهای از پیش‌ کامپایل‌ شده اتریوم (آدرس‌های 0x01 تا 0x09)، استفاده از staticcall روش صحیح است؛ چرا که هیچ‌کدام از این قراردادها وضعیت را تغییر نمی‌دهند.

مثال زیر هش SHA256 یک عدد را با استفاده از قرارداد از پیش‌ کامپایل‌ شده آدرس 0x02 محاسبه می‌کند. توجه داشته باشید که این قرارداد داده را مستقیم هش می‌کند و نیازی به کدگذاری ABI نیست.

5/5 - (2 امتیاز)

راستی! برای دریافت مطالب جدید در کانال تلگرام یا پیج اینستاگرام سورس باران عضو شوید.

دوره صفر تا صد آموزش بین المللی لینوکس
  • انتشار: ۵ خرداد ۱۴۰۴

دسته بندی موضوعات

آخرین محصولات فروشگاه

مشاهده همه

نظرات

بازخوردهای خود را برای ما ارسال کنید