آموزش تولید اعداد تصادفی روی بلاکچین در سالیدیتی

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

در این مقاله فرض می‌کنیم که خواننده با زبان برنامه نویسی سالیدیتی آشنایی دارد، به‌ویژه با توابعی مانند block.number()، block.hash() و امضاهای دیجیتال (digital signatures).

اگر توسعه‌دهنده برای تولید عدد تصادفی از مقادیری مثل block.timestamp یا blockhash بلاک قبلی استفاده کند، مهاجمان می‌توانند با نوشتن یک قرارداد هوشمند نتیجه نهایی تراکنش را قبل از اجرا تحلیل کنند و تصمیم بگیرند که آیا اجرای آن به نفعشان خواهد بود یا نه. پروژه‌ای با نام Capture the Ether دقیقاً برای بررسی امنیت چنین سناریوهایی طراحی شده و مجموعه‌ای از چالش‌های هک در این زمینه ارائه می‌دهد.

اگر در پروژه خود به تولید عدد تصادفی نیاز دارید، می‌توانید از چند الگوی طراحی (Design Pattern) معتبر استفاده کنید که در ادامه به آن‌ها خواهیم پرداخت.

روش Commit-Reveal در تولید اعداد تصادفی در سالیدیتی

با اینکه تراکنش‌های بلاکچین در لحظه اجرا کاملاً تعیین‌پذیر هستند، اما هیچ‌کدام از آن‌ها توانایی پیش‌بینی آینده را ندارند. به‌طور خاص، هیچ‌کس نمی‌تواند هش بلاک‌های آینده را پیش‌بینی کند (البته در ادامه به یک استثنا اشاره می‌کنیم).

در روش Commit-Reveal، فرآیند به این صورت پیش می‌رود: کاربر در بلاک فعلی با شماره n یک تراکنش ارسال می‌کند و اعلام می‌کند که عدد تصادفی را از هش بلاکی که دقیقاً ۲۰ بلاک بعد از آن قرار دارد، به دست خواهد آورد. به عبارت دیگر، برنامه عدد تصادفی را از هش بلاک n + 20 استخراج می‌کند. از آن‌جا که حتی هش بلاک n + 2 قابل پیش‌بینی نیست، می‌توان هش بلاک n + 20 را به‌عنوان یک منبع معتبر برای تولید عدد تصادفی در نظر گرفت.

کاربر برای دسترسی به هش بلاک موردنظر تنها ۲۵۶ بلاک فرصت دارد. بنابراین، باید بین بلاک‌های n + 20 تا n + 276 تراکنش دومی را ارسال کند. این تراکنش دوم همان مرحله «افشا» یا reveal محسوب می‌شود.

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

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

نقاط ضعف روش Commit-Reveal و راهکارهای مقابله با آن

روش Commit-Reveal اگرچه به‌ظاهر امن به‌نظر می‌رسد، اما تولیدکنندگان بلاک می‌توانند آن را دستکاری کنند. آن‌ها نمی‌توانند مقدار دقیق هش بلاک را تعیین کنند، اما می‌توانند ترتیب تراکنش‌ها را در بلاک طوری بچینند که هش بلاک به نتیجه‌ای مطلوب—for example، یک عدد زوج—منتهی شود.

برای جلوگیری از این نوع دستکاری، توسعه‌دهندگان می‌توانند سازوکاری دو مرحله‌ای طراحی کنند. در این ساختار، کاربر در بلاک n هش یک عدد مخفی را به‌عنوان تعهد ثبت می‌کند. سپس در بلاک n + 20، همان عدد مخفی را افشا می‌کند. در این مرحله، برنامه عدد مخفی را با هش بلاک n + 20 ترکیب می‌کند و از این ترکیب یک هش جدید می‌سازد. این هش نهایی را به‌عنوان عدد تصادفی استفاده می‌کنند.

چون تولیدکننده بلاک به عدد مخفی دسترسی ندارد، نمی‌تواند هش نهایی را پیش‌بینی کند یا آن را به نفع خود تغییر دهد. با این حال، این روش همچنان نمی‌تواند جلوی همه‌ی سناریوهای سوءاستفاده را بگیرد.

اگر تولیدکننده بلاک خودش در فرآیند قرعه‌کشی شرکت کند، ممکن است یک عدد مخفی دلخواه ثبت کند و سپس بلاک را طوری تنظیم کند که هش نهایی به سود او تمام شود. این نوع حمله در گذشته راحت‌تر اجرا می‌شد، اما حالا که اتریوم از الگوریتم اجماع اثبات سهام (Proof of Stake) استفاده می‌کند، اجرای آن دشوارتر شده است. در حال حاضر، مهاجم باید دقیقاً در همان بلاکی که مرحله افشا انجام می‌شود، مسئول تولید بلاک باشد تا بتواند حمله را اجرا کند.

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

از طرفی، مهاجمی که منابع مالی زیادی در اختیار دارد، می‌تواند از این سازوکار سوءاستفاده کند. فرض کنید برنامه به کاربری که هش بلاک زوج دارد، جایزه می‌دهد. در این حالت، مهاجم می‌تواند با ارسال تعداد زیادی تراکنش با کارمزد بالا، جلوی رسیدن تراکنش افشا را بگیرد. اگر برنامه نتواند تراکنش افشا را بین بلاک‌های 20 تا 276 دریافت کند، و از بلاک هدف بیش از ۲۵۶ بلاک گذشته باشد، دیگر به هش آن بلاک دسترسی ندارد و مقدار صفر بازمی‌گرداند. این حمله هزینه زیادی دارد، اما همچنان می‌تواند امنیت برنامه را تهدید کند.

استفاده از Chainlink VRF برای تولید عدد تصادفی

تا امروز مقالات و منابع زیادی درباره نحوه تولید عدد تصادفی با استفاده از Chainlink VRF (تابع تصادفی قابل تأیید) منتشر شده‌اند. مستندات رسمی Chainlink نیز بسیار کامل و قابل درک هستند. اما در اینجا، روند کلی کار را به‌صورت خلاصه توضیح می‌دهیم.

قرارداد هوشمندی که قصد دارد یک عدد تصادفی دریافت کند، باید درخواست خود را به قرارداد هوشمند Chainlink ارسال کند و در کنار آن، مقداری توکن LINK برای پرداخت هزینه این سرویس بپردازد.

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

Chainlink نمی‌تواند همه این فرآیند را در یک تراکنش انجام دهد، چون در این صورت، یک مهاجم می‌تواند تراکنش را در صورتی که نتیجه به نفعش نباشد، بازگرداند (revert کند).

به دلیل احتمال بازسازمان‌دهی زنجیره (chain reorganization)، برای کاربردهای مهم‌تر و باارزش‌تر، بهتر است اپلیکیشن مشخص کند که پاسخ‌گویی Chainlink در بلاک‌های دورتری در آینده انجام شود.

نکته مهم دیگر این است که Chainlink خودش تراکنش دوم (که حاوی عدد تصادفی است) را ایجاد می‌کند؛ بنابراین، کاربر نیازی به ارسال دستی مرحله دوم ندارد. البته این راحتی، هزینه دارد: علاوه بر کارمزد گس (Gas Fee)، کاربر یا اپلیکیشن باید توکن LINK را نیز برای استفاده از این سرویس پرداخت کند.

استفاده از امضا خارج از زنجیره (Offchain Signature) برای تولید عدد تصادفی

یکی از مشکلات رایج در تجربه کاربری روش‌های قبلی، تأخیر ذاتی آن‌ها و الزام به ارسال دو تراکنش است. در یک بازی بلاکچینی، چنین وقفه‌ای می‌تواند باعث نارضایتی بازیکنان شود.

اما چطور می‌توان بدون ایجاد یک قرارداد آسیب‌پذیر، این مشکل را حل کرد؟

یک راهکار نیمه‌متمرکز برای تولید عدد تصادفی این است که یک مولد عدد تصادفی خارج از زنجیره (Offchain RNG) عددی تصادفی تولید کند، سپس آن عدد را به‌همراه آدرس ارسال‌کننده و شماره یک بلاک آینده، به‌صورت رمزنگاری‌شده امضا کند.

سپس، قرارداد هوشمند در بلاکچین عدد تصادفی امضاشده را دریافت می‌کند و آن را با blockhash همان بلاک آینده ترکیب (concatenate) می‌کند. این رشته ترکیبی را هش می‌گیرد و هش نهایی به‌عنوان عدد تصادفی استفاده می‌شود.
قراردادی که مسئول پرداخت پاداش است، امضای دریافتی را با اطلاعات ارسال‌کننده و شماره بلاک تطبیق می‌دهد تا از صحت آن مطمئن شود.

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

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

نقاط ضعف احتمالی در روش امضای خارج از زنجیره و راهکارهای کاهش ریسک

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

  1. زمانی که تولیدکننده عدد تصادفی و تولیدکننده بلاک یکسان باشند:
    اگر یک ماینر (یا در سازوکار اثبات سهام، تولیدکننده بلاک) هم عدد تصادفی را خارج از زنجیره تولید کند و هم مسئول ایجاد بلاک مشخص‌شده باشد، می‌تواند نتیجه را دستکاری کند. البته تا زمانی که تولیدکننده بلاک و مولد عدد تصادفی با یکدیگر تبانی نکنند، این روش امنیت مناسبی خواهد داشت.

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

  3. سوءاستفاده کاربر با تولید چندین عدد تصادفی برای رسیدن به نتیجه مطلوب:
    اگر سیستم به کاربر اجازه دهد چند بار عدد تصادفی دریافت کند و فقط نتیجه مطلوب را انتخاب کند، عملاً تصادفی بودن زیر سؤال می‌رود. برای جلوگیری از این اتفاق، باید نوعی کنترل خارج از زنجیره (offchain gating) اجرا شود تا مانع تکرار درخواست توسط کاربران شود. البته این موضوع باعث کاهش شفافیت و امنیت می‌شود، چون منطق کنترل در بیرون از قرارداد هوشمند انجام خواهد شد.

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

این کار باعث می‌شود فرآیند تولید عدد تصادفی شفاف‌تر شود و احتمال سوءاستفاده کاهش پیدا کند، هرچند پیاده‌سازی آن به زیرساخت بین‌زنجیره‌ای نیاز دارد.

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

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

آموزش گام به گام برنامه نویسی اندروید با B4A (پروژه محور)
  • انتشار: ۷ خرداد ۱۴۰۴

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

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

مشاهده همه

نظرات

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