Как вы поняли из названия, речь пойдёт о рандоме на 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);
}
}
Недостаток:
Очевидный недостаток здесь, это то, что клиент, будет знать результат до публикации на блокчейн и может не публиковать его. В нашем случае, это не проблема, ввиду того что, шагов в игре много, и один ход не на что не повлияет. А мы не позволим генерировать новые числа на сервере до публикации предыдущего.
Заключение
К сожалению при гуглении о том как сделать рандом на солидити, в ответах на вопросы чаще всего упомянаются первые два пункта, а третий путь кажется не очевидным. Возможно в этом способе есть подводные камни, не является секьюрити рекомендацией.
Спасибо что прочитали, если у вас есть годные комментарии, замечания по описанному методу, с удовольствием их почитаю и постараюсь ответить.
svr_91
По-моему, не раскрыта тема, для каких целей генерируется рандом. Из этого не понятны рассмотренные варианты
E1ektr0 Автор
Показалось что цель здесь не так важна(для читающего)
У нас планируется использовать для вытаскивания кусочков пазла. Клиент будет делать это множество раз и ценность одного кусочка низка.
svr_91
Если это просто игра, без всяких ставок, то по-моему, тут почти любой рандом подойдет, даже чтото типа первого
E1ektr0 Автор
Есть награда, есть конкуренция( просто много итераций. Награду можно получить условно после 30-100 итераций) .
Есть несколько концептов игр. Хотя я и не считаю идею очень ценой, но из за nda разглашать не хочется.
В любом случае мне показалось что имеет смысл написать про рандом в отрыве от проекта.