روش های تولید اعداد تصادفی در سالیدیتی (VRF Chainlink)
اعداد تصادفی، اعدادی هستند که در یک دنباله دیده تولید می شوند و شرایط زیر را دارند: 1-مقادیر در یک محدوده، توزیع یکنواخت دارد و 2-مقادیر بعدی بر اساس مقادیر گذشته قابل حدس نیست. در این مقاله روش های مختلف تولید اعداد تصادفی را بررسی کرده و مشکلات هر روش را معرفی خواهیم کرد. سپس بهترین روش برای تولید اعداد تصادفی در قرارداد هوشمند سالیدیتی (اوراکل) را به طور کامل همراه با سورس کد بررسی خواهیم کرد.
مشکلات تولید اعداد تصادفی در بلاک چین
دستیابی به رندومنس (Randomness) یا تصادفی بودن در سیستم های کامپیوتری به ویژه اتریوم سخت است. کلا تولید یک عدد واقعا تصادفی از طریق یک نرم افزار، سخت یا حتی غیرممکن است. با این حال، نیاز به رندمنس در اتریوم بالا است. چراکه بخش قابل توجهی از قراردادهای هوشمند موجود در اتریوم، بازی های بلاک چینی هستند که اغلب برای تعیین برنده از خاصیت انتخاب تصادفی استفاده می کنند.
1- فقدان رندمنس ذاتی در اتریوم
اتریوم یک ماشین تورینگ قطعی (Deterministic Turing Machine) است و هیچ گونه رندمنس ذاتی ندارد. برای رسیدن به یک اجماع، باید اکثریت ماینرها بعد از ارزیابی یک تراکنش، به نتیجه یکسانی برسند. اجماع یکی از ارکان فناوری بلاک چین است و تصادفی بودن دلالت بر غیرممکن بودن توافق متقابل تمام نودها دارد.
2- ماهیت عمومی بلاک چین
وضعیت (State) داخلی یک قرارداد و همچنین کل سابقه (History) بلاک چین برای عموم قابل مشاهده است (شفافیت). بنابراین، پیداکردن منبع مطمئن آنتروپی (Entropy : فقدان ترتیب یا الگوی منظم) در بلاک چین، کار سختی است.
روش های تولید اعداد تصادفی در بلاکچین
یکی از اولین منابع تصادفی بودن در اتریوم که به ذهن می رسد، برچسب زمانی بلاک (Block Timestamp) است. مشکل برچسب زمانی بلاک این است که می تواند از ماینر تأثیرپذیری داشته باشد. مگر زمانیکه تایم استمپ بلاک از تایم استمپ بلاک والدش قدیمی تر باشد. اغلب اوقات تایم استمپ ها صحیح هستند (یا یک خطای ناچیز دارند). ولی اگر یک ماینر قصد سوء استفاده از تایم استمپ های اشتباه را داشته باشد، می تواند از قدرت ماینینگ خود استفاده کرده و بلوک های خود را با برچسب زمانی نادرست استخراج کند. بدین طریق می تواند نتیجه تابع رندوم را به میل خود تغییر دهد.
راه حل های متعددی برای غلبه بر محدودیت های ذکر شدن برای رندومنس در بلاک چین ارائه شده است که هر کدام مزایا و معایب خودش را دارد. این راهکارها را میتوان به 3 گروه زیر تقسیم بندی کرد:
- Block Hash PRNG – در این روش هش بلاک به عنوان منشأ رندومنس استفاده می شود
- Oracle RNG – رندومنس به واسطه یک اوراکل ارائه شده است
- Collaborative PRNG – تولید مشارکتی یک عدد تصادفی در بلاک چین
شناخته شده ترین مثال از روش PRNG مشارکتی ، متد Randao نام دارد که در حال حاضر، هنوز عملیاتی نشده است. در این مقاله روش های اول و دوم برای تولید اعداد تصادفی در زبان سالیدیتی را مورد بررسی قرار خواهیم داد.
تولید اعداد تصادفی در سالیدیتی (به کمک هش بلاک)
در این بخش از مقاله، قصد داریم یک قرارداد در زبان برنامه نویسی سالیدیتی بنویسیم که یک تابع تولید عدد تصادفی داشته باشد و با فراخوانی آن تابع بتوانیم اعداد تصادفی تولید کنیم.
مراحل انجام این کار:
- از مقادیر block.difficulty و block.timestamp نمونه سازی میکنیم.
- مقادیر بالا را با استفاده از تابع abi.encodePacked پک میکنیم و سپس نتیجه را با استفاده از تابع keccak256 به یک هش 256 بیتی تبدیل می کنیم.
- مقدار هش تولید شده را به عدد صحیح uint256 تبدیل کرده و با گرفتن باقیمانده تقسیم آن بر عدد max کاری میکنیم که نتیجه محاسبات، اعداد تصادفی تولید شده بین صفر و max باشند. برای مثال اگر نتیجه هش تبدیل شده به عدد را بر 100 تقسیم کنیم، نتیجه اعداد تصادفی بین 0 تا 99 (2 رقم آخر) خواهد بود.
1 2 3 4 5 6 7 8 9 10 11 |
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.5 <=0.8.14; contract RandomNumber { /* return a random number in [0-max] */ function getRandom(uint max) public view returns(uint) { uint randInt = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp))); return randInt % max; } } |
مشکل امنیتی این روش
در شبکه بلاک چین اتریوم، همه گره ها سعی دارند مسئله را حل کرده و تراکنش را تایید کنند. وقتی که یک نود بلاک را Verify کرد آن بلاک را در تمام شبکه پخش (broadcast) می کند. یک DApp داریم که می توان در آن بازی هایی مانند سکه انداختن (شیر یا خط) را انجام داد. توسعه دهندگان DApp به راحتی می توانند تقلب کنند و هر وقت که بخواهند نتیجه را به نفع خود نشان دهند. سکه انداخته می شود (یک تراکنش تولید شده) و DApp تراکنش مربوطه را فقط در گره خودش منتشر می کند و آن را Broadcast نمی کند. DApp می تواند تابع getRando را تا وقتی که نتیجه به نفع خودش باشد اجرا کند و آنگاه پس از بنده شدن، به اشتراک بگذارد.
یکی از راه حل های جلوگیری از این تقلب، استفاده از اوراکل ها (Oracles) برای دسترسی به توابع تولید عدد تصادفی است. در این حالت، تابع اعداد تصادفی داخل قرارداد هوشمند یا بلاکچین نیست، بلکه بیرون از بلاکچین قرار دارد.
تولید اعداد تصادفی در سالیدیتی (به کمک اوراکل)
یکی از روش های تولید اعداد تصادفی در سالیدیتی، استفاده از سرویس VRF چین لینک، است که از تکنولوژی اوراکل بلاک چین استفاده می شود. برای پیاده سازی این روش و استفاده از اوراکل (Oracle)، ابتدا باید با اوراکل آشنا شوید. بنابراین قبل از ادامه این بخش می توانید ابتدا مقاله مفهوم اوراکل را مطالعه کنید.
در ادامه، سورس کد مربوط به تولید اعداد تصادفی در قرارداد هوشمند سالیدیتی به کمک سرویس اوراکل چین لینک را مشاهده می کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
// SPDX-License-Identifier: MIT // An example of a consumer contract that also owns and manages the subscription pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol"; import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol"; contract VRFv2SubscriptionManager is VRFConsumerBaseV2 { VRFCoordinatorV2Interface COORDINATOR; LinkTokenInterface LINKTOKEN; // Rinkeby coordinator. For other networks, // see https://docs.chain.link/docs/vrf-contracts/#configurations address vrfCoordinator = 0x6168499c0cFfCaCD319c818142124B7A15E857ab; // Rinkeby LINK token contract. For other networks, see // https://docs.chain.link/docs/vrf-contracts/#configurations address link_token_contract = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; // The gas lane to use, which specifies the maximum gas price to bump to. // For a list of available gas lanes on each network, // see https://docs.chain.link/docs/vrf-contracts/#configurations bytes32 keyHash = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc; // A reasonable default is 100000, but this value could be different // on other networks. uint32 callbackGasLimit = 100000; // The default is 3, but you can set this higher. uint16 requestConfirmations = 3; // For this example, retrieve 2 random values in one request. // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS. uint32 numWords = 2; // Storage parameters uint256[] public s_randomWords; uint256 public s_requestId; uint64 public s_subscriptionId; address s_owner; constructor() VRFConsumerBaseV2(vrfCoordinator) { COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); LINKTOKEN = LinkTokenInterface(link_token_contract); s_owner = msg.sender; //Create a new subscription when you deploy the contract. createNewSubscription(); } // Assumes the subscription is funded sufficiently. function requestRandomWords() external onlyOwner { // Will revert if subscription is not set and funded. s_requestId = COORDINATOR.requestRandomWords( keyHash, s_subscriptionId, requestConfirmations, callbackGasLimit, numWords ); } function fulfillRandomWords( uint256, /* requestId */ uint256[] memory randomWords ) internal override { s_randomWords = randomWords; } // Create a new subscription when the contract is initially deployed. function createNewSubscription() private onlyOwner { s_subscriptionId = COORDINATOR.createSubscription(); // Add this contract as a consumer of its own subscription. COORDINATOR.addConsumer(s_subscriptionId, address(this)); } // Assumes this contract owns link. // 1000000000000000000 = 1 LINK function topUpSubscription(uint256 amount) external onlyOwner { LINKTOKEN.transferAndCall(address(COORDINATOR), amount, abi.encode(s_subscriptionId)); } function addConsumer(address consumerAddress) external onlyOwner { // Add a consumer contract to the subscription. COORDINATOR.addConsumer(s_subscriptionId, consumerAddress); } function removeConsumer(address consumerAddress) external onlyOwner { // Remove a consumer contract from the subscription. COORDINATOR.removeConsumer(s_subscriptionId, consumerAddress); } function cancelSubscription(address receivingWallet) external onlyOwner { // Cancel the subscription and send the remaining LINK to a wallet address. COORDINATOR.cancelSubscription(s_subscriptionId, receivingWallet); s_subscriptionId = 0; } // Transfer this contract's funds to an address. // 1000000000000000000 = 1 LINK function withdraw(uint256 amount, address to) external onlyOwner { LINKTOKEN.transfer(to, amount); } modifier onlyOwner() { require(msg.sender == s_owner); _; } } |
معرفی دوره آموزشی
با آموزش های تخصصی پیاده سازی قرارداد هوشمند امن و بهینه با ما همراه باشید. برای یادگیری پیاده سازی اصولی قرارداد هوشمند و توسعه توکن در بستر اتریوم می توانید در دوره آموزش برنامه نویسی بلاکچین و قرارداد هوشمند که در مرکز آموزش علوم نوین امیرکبیر برگزار می شود شرکت نمایید.
درباره مجید شبیری
کارشناس ارشد فناوری اطلاعات از دانشگاه صنعتی امیرکبیر. مدیر و مؤسس "علوم نوین امیرکبیر"، متخصص برنامه نویسی، شبکه، لینوکس و امنیت. از سال 84 همزمان با شروع تحصیلات دانشگاهی، وارد حوزه تخصصی مهندسی نرم افزار شدم و اکنون مشغول تحقیق، توسعه و آموزش در حوزه بلاک چین هستم و معتقدم بلاکچین به زودی فضای کسب و کارها را منقلب خواهد کرد.
نوشته های بیشتر از مجید شبیری
دیدگاهتان را بنویسید