Как вы поняли из названия, речь пойдёт о рандоме на solidity, а именно о офчейн решении проблемы, и нет, это не chainlink. 

Костыли и велосипеды
Костыли и велосипеды

Для начало обозначим проблему: необходимо сделать рандомные значения на evm блокчейне которым сможет доверять и пользователь и сервер.

Решение в лоб

Просто сгенерировать случайное число на солидити в зависимости от случайного параметра

Пример

function random() external view returns (uint) {

        return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, additionalRandomParam)));

    }

Проблема:

Несмотря на казалось бы очевидную мало вероятную угрозу атаки со стороны майнеров, тут есть и менее очевидная угроза, но значительно более реальная. Атакующий может создать другой смарт контракт который вызовет данный метод и будет иметь все параметры для вычисления результатов.

В теории можно запретить вызов кода из других смарт контрактов, но даже тогда, обеспечить рандомность довольно таки сложно.

Chainlink / Две транзакции

Суть решения заключается, в том, что за рандомность числа, отвечает не транзакция клиента, а транзакция сервера. В случае с chainlink, сервер является третьей стороной. 

Проблема:

За транзакцию с третьей стороны должен платить сервер.  Безусловно, в некоторых случаях это оправданно, но не в нашем.

Offchain random

Идея: сервер и клиент договариваются о рандомном числе:

  • сервер генерирует некое число(r) по запросу клиента

  • в ответ посылает клиенту хэш этого числа

  • клиент генерирует число(c) меньшего размера и посылает его на сервер

  • сервер вскрывает карты, отдавая клиенту оригинальное число, что бы он сравнил его с хэшем, а также подписывая результат сложения этих чисел (а также, seq, address, chain id, contract address, дабы избежать повторного использования подписи). 

  • Клиент сравнивает хэш и публикует транзакцию на блокчейн

Лучше открывать в отдельно окен
js ethers test

    it("Base success", async function () {

        var seed = 500;

        var seq = 0;

        var address = user.address;

 

        var hash = ethers.utils.keccak256(

            ethers.utils.solidityPack(['uint256', 'uint256','address' ],

            [seed, seq, address])//add chain id, etc

            );

 

        var signature = await hrv.web3.eth.sign(hash, master.address);

        var item = await random.connect(user).next(seed, seq, signature);

        await item.wait();

    });

solidity contract

//SPDX-License-Identifier: Unlicense

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "hardhat/console.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

 

contract Random is Ownable {

    using ECDSA for bytes32;

    mapping(address => uint256) public SeqMapping;

 

    function next(

        uint256 seed,

        uint256 seq,

        bytes memory signature

    ) public returns (uint256) {

        require(SeqMapping[msg.sender] == seq, "wrong seq");

  //add chain id

        bytes32 hash = keccak256(abi.encodePacked(seed, seq, msg.sender));

        bytes32 hashEth = hash.toEthSignedMessageHash();

        require(hashEth.recover(signature) == owner(), "wrong signature");

 

        SeqMapping[msg.sender]++;

        return uint256(hash);

    }

}

Недостаток:

Очевидный недостаток здесь, это то, что клиент, будет знать результат до публикации на блокчейн и может не публиковать его. В нашем случае, это не проблема, ввиду того что, шагов в игре много, и один ход не на что не повлияет. А мы не позволим генерировать новые числа на сервере до публикации предыдущего.

Заключение

К сожалению при гуглении о том как сделать рандом на солидити, в ответах на вопросы чаще всего упомянаются первые два пункта, а третий путь кажется не очевидным. Возможно в этом способе есть подводные камни, не является секьюрити рекомендацией.

Спасибо что прочитали, если у вас есть годные комментарии, замечания по описанному методу, с удовольствием их почитаю и постараюсь ответить.

Комментарии (4)


  1. svr_91
    13.01.2022 12:34
    +1

    По-моему, не раскрыта тема, для каких целей генерируется рандом. Из этого не понятны рассмотренные варианты


    1. E1ektr0 Автор
      13.01.2022 12:46

      Показалось что цель здесь не так важна(для читающего)
      У нас планируется использовать для вытаскивания кусочков пазла. Клиент будет делать это множество раз и ценность одного кусочка низка.


      1. svr_91
        13.01.2022 13:29

        Если это просто игра, без всяких ставок, то по-моему, тут почти любой рандом подойдет, даже чтото типа первого


        1. E1ektr0 Автор
          13.01.2022 13:30
          +1

          Есть награда, есть конкуренция( просто много итераций. Награду можно получить условно после 30-100 итераций) .

          Есть несколько концептов игр. Хотя я и не считаю идею очень ценой, но из за nda разглашать не хочется.

          В любом случае мне показалось что имеет смысл написать про рандом в отрыве от проекта.