I have a couple of keccaks, which could be reduced to one if I would find a cheap way to get parts of the created uint.
pragma solidity ^0.4.19;
contract test {
function test() {
}
function sup() returns (uint test) {
uint _test = uint(keccak256("wow"));
return _test;
}
}
This returns me a sweet random number: 13483274892375982735325
Now the plan is that instead of calling keccak 5 times with different "seeds", I could take that number apart and get something like: 1348, 3274, 8923 etc. which I then use for my random number e.g.: 1348 % 10
But solidity can't just do that. Is there anything cheap that could work?
Solidity contracts are deterministic. Anyone who figures out how your contract produces randomness can anticipate its results and use this information to exploit your application.
One option is to produce randomness off-chain (where it cannot be predicted) and use it in your smart contract. Chainlink VRF is an easy-to-implement solution for using random data in smart contracts. Here's an example snippet to request & receive random data:
requestRandomness(keyHash, fee, seed);
Your contract's request is fulfilled in a callback function:
function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
// Do something with randomness
}
An example of a full contract that implements a random number would be:
pragma solidity 0.6.2;
import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/VRFConsumerBase.sol";
contract Verifiable6SidedDiceRoll is VRFConsumerBase {
using SafeMath for uint;
bytes32 internal keyHash;
uint256 internal fee;
event RequestRandomness(
bytes32 indexed requestId,
bytes32 keyHash,
uint256 seed
);
event RequestRandomnessFulfilled(
bytes32 indexed requestId,
uint256 randomness
);
/**
* @notice Constructor inherits VRFConsumerBase
* @dev Ropsten deployment params:
* @dev _vrfCoordinator: 0xf720CF1B963e0e7bE9F58fd471EFa67e7bF00cfb
* @dev _link: 0x20fE562d797A42Dcb3399062AE9546cd06f63280
*/
constructor(address _vrfCoordinator, address _link)
VRFConsumerBase(_vrfCoordinator, _link) public
{
vrfCoordinator = _vrfCoordinator;
LINK = LinkTokenInterface(_link);
keyHash = 0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205; // hard-coded for Ropsten
fee = 10 ** 18; // 1 LINK hard-coded for Ropsten
}
/**
* @notice Requests randomness from a user-provided seed
* @dev The user-provided seed is hashed with the current blockhash as an additional precaution.
* @dev 1. In case of block re-orgs, the revealed answers will not be re-used again.
* @dev 2. In case of predictable user-provided seeds, the seed is mixed with the less predictable blockhash.
* @dev This is only an example implementation and not necessarily suitable for mainnet.
* @dev You must review your implementation details with extreme care.
*/
function rollDice(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");
uint256 seed = uint256(keccak256(abi.encode(userProvidedSeed, blockhash(block.number)))); // Hash user seed and blockhash
bytes32 _requestId = requestRandomness(keyHash, fee, seed);
emit RequestRandomness(_requestId, keyHash, seed);
return _requestId;
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
uint256 d6Result = randomness.mod(6).add(1);
emit RequestRandomnessFulfilled(requestId, randomness);
}
}
You cannot create truly random numbers but you can pseudo-random numbers. Blockchain is a deterministic system so we have to make sure that each node must give the same random number. Determinism, is very important because it is vital that regardless of where the smart contract code executes, it produces the same result every time and everywhere.
Getting truly numbers in a deterministic system is impossible because global variables are being used is predictable or can be somehow get manipulated.
For example, Timestamp vulnerability is quite common. Usually, the timestamp
of a block is accessed via block.timestamp but this timestamp can be manipulated by miners, leading to influencing the outcome of some function that relies on timestamps. The timestamp is used as a source of randomness in lottery games to select the next winner. Thus, it might be possible for a miner to modify the timestamp in such a way that its chances of becoming the next winner increase.
In order to get the true random number we have to look at outside the blockchain. We need to use oracle services to get the true random number. If you do not have your true random number in your smart contract, your smart contract can get hacked. You can read about 2 cases:
https://www.reddit.com/r/ethereum/comments/74d3dc/smartbillions_lottery_contract_just_got_hacked/
https://hrishiolickel.medium.com/why-smart-contracts-fail-undiscovered-bugs-and-what-we-can-do-about-them-119aa2843007
since solidity evolving very fast, other answers are outdated. This answer will be outdated one day but as of now you can implement a pseudo number generator like this:
// I realized if you call the random() in for loop, you get same result. So I had to add another changing variable
// https://stackoverflow.com/questions/73555009/how-to-generate-random-words-in-solidity-based-based-on-a-string-of-letters/73557284#73557284
uint counter =1;
function random() private view returns (uint) {
counter++;
// sha3 and now have been deprecated
return uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp, players,counter)));
// convert hash to integer
// players is an array of entrants
}
this will return very very big number. But we use modulo operator.
random() % players.length
this will return a number between 0 and players.length. we write a function for this:
function pickWinner() public {
uint index=random()%players.length;
}
After you deploy this contract, you have to send Link tokens to this contract and press on getRandomNumber. Wait about a minute and then click on result
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBase.sol";
contract Test is VRFConsumerBase {
bytes32 public keyHash;
uint256 public fee;
uint256 public ticketPrice;
uint256 public result;
// ---------------- GOERLI ADDRESSESS----------------
// link address 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
// key hash 0x0476f9a745b61ea5c0ab224d3a6e4c99f0b02fce4da01143a4f70aa80ae76e8a
//ChainlinkVRFCoordinator 0x2bce784e69d2Ff36c71edcB9F88358dB0DfB55b4
constructor(
address _ChainlinkVRFCoordinator,
address _ChainlinkLINKToken,
bytes32 _ChainlinkKeyHash,
uint256 _ticketPrice
) VRFConsumerBase(_ChainlinkVRFCoordinator, _ChainlinkLINKToken) {
keyHash = _ChainlinkKeyHash;
fee = 0.1 * 10 ** 18;
ticketPrice = _ticketPrice;
}
function getRandomNumber() public payable returns (bytes32 requestId) {
require(
LINK.balanceOf(address(this)) >= fee,
"YOU HAVE TO SEND LINK TOKEN TO THIS CONTRACT"
);
return requestRandomness(keyHash, fee);
}
// this is callback, it will be called by the vrf coordinator
function fulfillRandomness(
bytes32 requestId,
uint256 randomness
) internal override {
result = randomness;
}
receive() external payable {}
}
I answered this question: Getting a random number with Chainlink VRF
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With