I need to add a random number function in my smart contract. Is it best to use Oraclize and e.g. random.org? It's not a lottery which means the random number generation does not require heavy security.
2 Answers
Responding to comments...
Although you say it does not require heavy security, I feel it is worthwhile to mention that a simple hash function would be predictable. For example, keccak256(now)
can be calculated, trivially, by another contract that calls into your contract. By working it out ahead of time, it will know, in advance, what your hash function will produce. This works because the time is "now" (same) in both contexts.
Setting the predictability concern aside, you would convert a hash function to a uint, e.g. uint(keccak256(now))
and this will yield a number in the range of 0-2^256-1. You would scale that number to the desired range and precision with divisors and offsets.
Hope it helps.
UPDATE
Scaling is a general programming concern not unique to Solidity. We're just going to take a number with known range and adjust it for another range.
pragma solidity 0.5.1;
contract Randomish {
uint public constant MAX = uint(0) - uint(1); // using underflow to generate the maximum possible value
uint public constant SCALE = 500;
uint public constant SCALIFIER = MAX / SCALE;
uint public constant OFFSET = 100;
// generate a randomish number between 100 and 600.
// Warning: It is trivial to know the number this function returns BEFORE calling it.
function randomish() public view returns(uint) {
uint seed = uint(keccak256(abi.encodePacked(now)));
uint scaled = seed / SCALIFIER;
uint adjusted = scaled + OFFSET;
return adjusted;
}
}
-
thanks. Since I'm relatively new to Solidity, could you further the part with scaling the uint to a desired range? Would you recommend Oraclize to receive a "random" number?– 2stefanJun 19, 2019 at 19:30
-
Scaling - divide the original number by a factor to ensure the result is below a maximum. It's hard to recommend confidently without knowing the use-case. That's why I added the caution item to the hash technique idea which is simpler, as long as it isn't too simple for your case. Jun 19, 2019 at 20:20
-
okay. I'm working on a token for the first time. Everything is clear and simple but just this randomness factor which I'd like to scale to a range between 0 to 1 (0% to 100%) is driving me crazy– 2stefanJun 19, 2019 at 20:31
-
In the example, change the SCALE to 100 and drop the parts about offset because you don't need it. Since you mentioned percent, you might run into further challenges with integer math. 0-100 is the right way to start, or even 0-1000 for an extra digit of precision. Jun 19, 2019 at 20:43
-
1Multiply x * 100 then divide by 80 to get 80% of x. Have a look at this for more hints: ethereum.stackexchange.com/questions/15090/… Jun 19, 2019 at 21:31
It sounds like you're looking for a low cost way to get random numbers, and the means doesn't really matter. If you use a centralized source like random.org, whatever application your creating can still be attacked and not really random.
A simple way to create a random number without it being predictable, and also easy to implement would be to use a Chainlink VRF.
A full contract for this is located at this similar question, but the contract could look something like below. To change the range of the random numbers, just change the .mod(6).add(1)
portion to be whatever range you're looking for.
constructor(address _vrfCoordinator, address _link)
VRFConsumerBase(_vrfCoordinator, _link) public
{
vrfCoordinator = _vrfCoordinator;
LINK = LinkTokenInterface(_link);
keyHash = 0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205; // hard-coded for Ropsten
fee = 10 ** 17; // 0.1 LINK LINK cost
}
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);
}
The function works asynchronously. When you rollDice
it makes a request to the Chainlink node, and it is returned through fulfillRandomness
.
keccak256(now)
in your contract. I think that this function might have changed between v0.4 and v0.5, so you might want to check the official documentation for the exact syntax.