The Importance of Randomness and How to Get it in Your Smart Contract
Tags: Solidity, Cryptocurrency, Blockchain Development, Cryptography, Smart Contracts

Smart contracts are all about reliable, repeatable, verifiable, deterministic behavior. In this sense, randomness in a smart contract is a bit of an oxymoron. In the face of this challenging contradiction, there are numerous use cases like gaming, security and layer-two protocols on blockchains which require randomness while not compromising the verifiable nature of smart contract states. If you’re looking to implement randomness in your smart contract, there are some important considerations when choosing a solution. Below I aim to cover how Chainlink handles these unique issues and how to implement random number generation in solidity.

First, what is randomness needed for and why are there special concerns when using it in smart contracts vs legacy centralized systems? Randomness powers a multitude of the digital components in our lives. Random numbers are at at the core of the SSL encryption which secures the internet, while also being used for more light-hearted cases such as generating stats on a unique videogame item or financial cases such as lotteries. Typically this random number generation is closely controlled, up to the centralized entity using it how they will generate it such as Cloudflare using lava lamps for randomness.

In a distributed system, that’s not so easy. No longer is the randomness generated internally, but rather randomness is coming from participants in the network whose intentions can’t be assumed to be trustworthy. Those network participants providing randomness may also be market participants who stand to gain from something such as a “random” lottery paying out more often than it should if it had truthful randomness. It’s for this reason blockchain smart contracts cannot accept just any randomness, but rather must use verifiable randomness. Just as network participants can validate blocks in a blockchain, so too must the randomness input into those blocks be verifiable in order to maintain the same level of determinism desired of blockchain.

PoolTogether, an example dApp which requires verifiable randomness

Verifiable randomness is a cryptographic concept that has been established for over two decades now, but only recently applied to the world of smart contracts. Essentially verifiable randomness, in its most basic explanation, is randomness with a cryptographic proof attached. In the case of Chainlink VRF, an asymmetric keypair proof is generated so that the requester can verify that the randomness was not manipulated in anyway. This private/public keypair is the same encryption behind tech such as SSH which is trusted world-wide for encrypted remote server access. With this encryption system, an entity with a publicly known key can verify that another entity holds the corresponding private key which has encrypted the data. Chainlink uses this same style of encryption for VRF, but with a requester provided seed value incorporated into the generation of the keypair. Since the seed used to generate the randomness is intrinsically connected to the keypair, not only can the identity of the randomness provider be verified, but also the accuracy of the randomness they generated from the seed. The exact implementation of this can be found in Chainlink’s Verifiable Randomness article.

Flow of a Chainlink VRF request

Now this theory is all well and good but how does it look in a smart contract? Thankfully Chainlink has published a guide for generating random numbers in solidity for us to explore along with the Chainlink VRF documentation pages. Implementation is fairly straightforward thanks to the relevant code already being provided in importable contracts. Like importing a library in Python, we just need to import Chainlink’s VRFConsumerBase.sol contract and make sure our contract inherits from it. This then exposes the getRandomNumber function to form a request to an oracle for VRF data like so:

function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {

require(LINK.balanceOf(address(this)) > fee, "Not enough LINK - fill contract with faucet");

return requestRandomness(keyHash, fee, userProvidedSeed);

}

which then returns the random uint256 to your specified callback function. Excluding standard things like the import and constructor, effectively it’s just this one function that’s required to request randomness from Chainlink. The data is automatically verified by the VRFCoordinator.sol contract on-chain, making the process even easier. With Chainlink VRF making it so simple to get cryptographically verifiable random data from an established network of decentralized Chainlink nodes, there’s really no reason not to use it.