آموزش Function Selector در سالیدیتی

function selector در سالیدیتی، یک شناسه ۴ بایتی است که در پشت صحنه برای شناسایی توابع مورد استفاده قرار می‌گیرد.

در واقع، این شناسه مشخص می‌کند که هنگام ارسال یک تراکنش، کدام تابع از قرارداد هوشمند باید فراخوانی شود.

شما می‌توانید این شناسه ۴ بایتی را با استفاده از متد .selector مشاهده کنید:

اگر یک تابع هیچ ورودی نداشته باشد، می‌توان آن را با استفاده از دستور سطح پایینcall و تنها با ارسال ۴ بایت شناسه تابع (function selector) به‌عنوان داده (data) به قرارداد، اجرا کرد.

در مثال زیر، قرارداد CallFoo تابع foo را که در قرارداد FooContract تعریف شده، با استفاده از دستور call و ارسال شناسه ۴ بایتی مربوط به آن تابع به قرارداد FooContract اجرا می‌کند.

اگر قراردادهای FooContract و CallFoo مستقر شده باشند و تابع callFooLowLevel() با آدرس FooContract اجرا شود، مقدار متغیر x درون FooContract برابر با ۱ خواهد شد. این نشان می‌دهد که تابع foo با موفقیت از طریق دستور call فراخوانی شده است.

شناسایی تابع و شناسه آن در سالیدیتی با استفاده از msg.sig

در سالیدیتی، متغیر سراسری msg.sig چهار بایت اول داده تراکنش (transaction data) را بازمی‌گرداند؛ این همان شناسه‌ای است که قرارداد از آن برای تشخیص اینکه کدام تابع باید اجرا شود استفاده می‌کند.

نکته مهم این است که مقدار msg.sig در طول اجرای تراکنش ثابت باقی می‌ماند و بسته به اینکه در حال حاضر در کدام تابع هستیم، تغییر نمی‌کند. به عبارت دیگر، حتی اگر یک تابع عمومی (public) در حین اجرا تابع عمومی دیگری را فراخوانی کند، مقدار msg.sig همچنان مربوط به تابع اولیه‌ای خواهد بود که در ابتدا از بیرون فراخوانی شده است.

در کد زیر، تابع foo تابع bar را فراخوانی می‌کند تا مقدار msg.sig را دریافت کند؛ اما در زمان اجرای foo، مقدار برگشتی برابر با selector تابع foo خواهد بود، نه bar:

می‌توانید این کد را در محیط Remix تست کنید:

امضای تابع در سالیدیتی (Function Signature)

در سالیدیتی، امضای تابع (Function Signature) رشته‌ای متنی است که شامل نام تابع و نوع آرگومان‌های آن است—اما نام متغیرها در آن لحاظ نمی‌شود.

برای مثال، در قطعه کد زیر، سمت چپ تعریف تابع است و سمت راست امضای معادل آن:

نکات مهم:

  • در امضای تابع هیچ فاصله‌ای وجود ندارد.
  • تمام نوع‌های عدد صحیح (uint) باید به‌صورت دقیق با اندازه مشخص شوند؛ مثل uint256، uint40، uint8 و غیره.
  • نوع حافظه مانند memory یا calldata در امضا درج نمی‌شود.

بنابراین، رشته‌ای مثل “getBalanceById(uint)” یک امضای نامعتبر است، چون اندازه نوع داده مشخص نشده است.

نحوه محاسبه Function Selector از روی Function Signature در سالیدیتی

Function Selector در سالیدیتی از چهار بایت اول هش keccak256 امضای تابع (function signature) به دست می‌آید.

مثال زیر تابعی را نشان می‌دهد که از طریق ورودی رشته‌ای (یعنی همان امضای تابع)، شناسه تابع را محاسبه می‌کند:

برای مثال، اگر ورودی این تابع رشته "foo()" باشد، مقدار بازگشتی برابر خواهد بود با: 0xc2985578

این شناسه دقیقاً همان ۴ بایت ابتدایی هش keccak256 از رشته "foo()" است و توسط سالیدیتی برای شناسایی توابع در زمان اجرای تراکنش‌ها استفاده می‌شود.

کد زیر نیز همین منطق را پیاده‌سازی می‌کند و به شما اجازه می‌دهد از روی امضای تابع، شناسه مربوط به آن را به‌دست آورید.

در سالیدیتی، رابط ها (Interfaces) به‌صورت آدرس کدگذاری می‌شوند. برای مثال، تابعی با امضای f(IERC20 token)، از نظر کدگذاری ABI، معادل f(address token) در نظر گرفته می‌شود. همچنین، اگر یک آدرس را به حالت payable تبدیل کنیم، این تغییر هیچ اثری بر امضای تابع (function signature) ندارد.

توابع داخلی (Internal) فاقد Function Selector هستند

توابع public و external دارای selector هستند، اما توابع internal و private این ویژگی را ندارند. کد زیر کامپایل نمی‌شود:

اگر سطح دسترسی تابع bar به public یا external تغییر داده شود، این کد کامپایل خواهد شد.

توابع internal نیازی به function selector ندارند، زیرا function selector برای استفاده توسط قراردادهای خارجی طراحی شده است. به‌عبارت دیگر، function selector مشخص می‌کند که یک کاربر خارجی قصد فراخوانی کدام تابع public یا external را دارد.

چرا به جای نام تابع از Function Selector در سالیدیتی استفاده می‌کنیم؟

نام توابع در سالیدیتی می‌تواند به‌صورت دلخواه و با هر طولی تعریف شود. اما اگر نام تابع طولانی باشد، این موضوع باعث افزایش اندازه و هزینه تراکنش خواهد شد، چرا که داده‌های بیشتری باید در تراکنش گنجانده شوند.

استفاده از function selector (که تنها ۴ بایت است) به‌جای نام کامل تابع، باعث می‌شود تراکنش ها کوچک‌تر و کم‌هزینه‌تر باشند. به همین دلیل، در سطح پایین، انتخاب تابع در قراردادهای سالیدیتی از طریق selector انجام می‌شود، نه نام.

آیا تابع fallback دارای function selector است؟

خیر، تابع fallback هیچ function selector ندارد.

اگر درون تابع fallback مقدار msg.sig را ثبت (log) کنید، این مقدار تنها چهار بایت اول داده تراکنش (transaction data) را نشان خواهد داد. اگر تراکنش هیچ داده‌ای نداشته باشد، msg.sig مقدار 0x00000000 را بازمی‌گرداند.

اما این به این معنا نیست که function selector تابع fallback برابر با صفر است؛ بلکه فقط به این معنی است که msg.sig تلاش کرده داده‌ای را بخواند که وجود نداشته است.

می‌توانید این قرارداد را در محیط Remix تست کنید. همچنین در ادامه، ویدیویی قرار دارد که نحوه اجرای تابع fallback را در Remix نشان می‌دهد:

 

احتمال برخورد (Collision) در Function Selector

یک function selector می‌تواند تا حداکثر 2**32 – 1 مقدار ممکن را در خود نگه دارد (تقریباً ۴.۲ میلیارد مقدار). به همین دلیل، احتمال اینکه دو تابع دارای selector یکسان باشند، کم اما واقعی است.

مثال بالا از یک پست در فروم گرفته شده است. هر دو تابع بالا دارای شناسه تابع 0x42966c68 هستند.

function selector و ماشین مجازی اتریوم (EVM)

برای توسعه‌دهندگانی که با EVM آشنایی دارند، ممکن است یک سوءتفاهم رایج وجود داشته باشد:
شناسه تابع ۴ بایتی یک ویژگی مربوط به سطح زبان سالیدیتی است، نه بخشی از مشخصات رسمی EVM.

در واقع، در مستندات اتریوم هیچ الزامی وجود ندارد که توابع حتماً با ۴ بایت شناسایی شوند. این تنها یک رویه رایج در سالیدیتی و زبان‌های مشابه است، نه یک الزام پروتکلی.

بهینه‌سازی رایج در لایه دوم‌ها:

در برخی از اپلیکیشن‌های لایه دوم، برای کاهش مصرف گس، تنها از ۱ بایت برای شناسه تابع استفاده می‌شود. در این روش:

  • همه فراخوانی‌ها به تابع fallback هدایت می‌شوند.

  • سپس، تابع fallback با بررسی اولین بایت داده تراکنش، تشخیص می‌دهد که کدام تابع باید اجرا شود.

این تکنیک باعث صرفه‌جویی در هزینه‌ها و کوچک‌تر شدن اندازه تراکنش ها در شبکه هایی با محدودیت منابع می‌شود.

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

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

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

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

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

مشاهده همه

نظرات

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