How Chainlink Solved Ethereumโs โRandom Problemโ
Random numbers and Blockchains have always been in disagreement. Until now, a verifiably random function has never existed on the Blockchain.
The problem stems from the fact that when transactions are mined, they need to be confirmed by more than one node on the network. This means that every node must come to the same conclusion. So, if a function was truly random, each node would come to a different conclusion, resulting in an unconfirmed transaction.
There have been workarounds that result in a pseudo-random generation, but until now all known methods have been either not truly random, or vulnerable to manipulation.
Introducing Chainlink
โThe Chainlink network provides reliable tamper-proof inputs and outputs for complex smart contracts on any blockchain.โโโโchain.link
Blockchains and smart contracts are great at performing computation according to a set of immutable rules. The problem is that the rules can only be applied to data inside the system. Getting verifiable data from outside the system is difficult.
Chainlinkโs mission is to solve this by providing decentralised oracles, enabling the Blockchain to access data outside of its ecosystem. Oracles are essentially a bridge between the Blockchain and the outside world.
Thatโs so random
In a recent article, Chainlink announced the release of their new Verifiable Random Function (VRF). The function, now available for developers to integrate into their DApps on several testnets, enables smart contracts to retrieve random numbers which are verifiable on-chain. This means no more vulnerabilities and guaranteed randomness.
How it works
If you want to generate a random number in Javascript, the code is pretty simple:
Math.random();
In a single execution of one line, you retrieve a random number. This is not how VRF works. Unlike Javascript, VRF works over a few transactions.
Hereโs the sequence of events:
- Your smart contract requests a random number from VRF, via transaction.
- VRF generates that number and verifies it.
- VRF prepares the response.
- VRF then sends the number back to your contract, via another transaction.
For point 4 to succeed, your contract needs to implement a known function so that VRF can call back with the result. But how can this be implemented in your project?
How to implement randomness
Letโs create a new smart contract called RandomGenerator
, this is where weโll make the call to VRF and receive the result.
Step 1: Create the consumer contract
Weโre going to use a contract provided by Chainlink called VRFConsumerBase
, which is an abstract contract that defines the minimum functions we need to consume VRF. Letโs define the beginnings of our โRandomGenerator.solโ file like this:
pragma solidity ^0.6.2;
import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/VRFConsumerBase.sol";
contract RandomGenerator is VRFConsumerBase {
constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) public {
}
}
VRFConsumerBase
is still in late testing, so itโs not yet available in production packages. This is why we are using an HTTP URL from Github for the import.
The base contract takes two parameters representing the coordinator and the address of the LINK ERC20 token. These are fixed addresses on each network (more on this later).
Step 2: Overriding functions
VRFConsumerBase
comes with two functions vital to the VRF process.
The first is called requestRandomness
which is already implemented, and which we donโt need to override. This is the function that makes the initial call to VRF.
The next is called fulfillRandomness
, and this is the function which VRF calls back to when it has generated the number. We can override this to perform actions on the random number when it gets called.
Our contract is simply going to store the generated random number in a state variable, called randomNumber
, so that we can query it when itโs finished. It should look something like this:
pragma solidity ^0.6.2;
import "https://raw.githubusercontent.com/smartcontractkit/chainlink/develop/evm-contracts/src/v0.6/VRFConsumerBase.sol";
contract RandomGenerator is VRFConsumerBase {
bytes32 public reqId;
uint256 public randomNumber;
constructor(address _vrfCoordinator, address _link) VRFConsumerBase(_vrfCoordinator, _link) public {
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) external override {
reqId = requestId;
randomNumber = randomness;
}
}
Weโve added the override for the fulfillRandomness
function, and set the state variables reqId
and randomNumber
to equal the values that the function receives.
Step 3: Generating the number
As I mentioned earlier in step 1, there are a few addresses and other values expected by the function calls that need to be passed in as parameters.
When deploying the contract and calling the constructor, it needs the VRF coordinator address and the address of the LINK token on the network. On the Ropsten testnet these are:
- VRF coordinator:
0xf720CF1B963e0e7bE9F58fd471EFa67e7bF00cfb
- LINK address:
0x20fE562d797A42Dcb3399062AE9546cd06f63280
When calling the requestRandomness
function, we need to pass in the key hash by which randomness is generated, the fee for the random generation (in LINK tokens) and the seed to generate the randomness against (this last one is provided by us). The function signature looks like this:
function requestRandomness(bytes32 _keyHash, uint256 _fee, uint256 _seed) public returns (bytes32 requestId)
On Ropsten, the parameter values are:
- Key hash:
0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205
- Fee (1 LINK):
1000000000000000000
- Seed: [whatever_you_want_your_seed_to_be]
So our call would look something like this:
// set ropsten key hash
bytes32 keyHash = "0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205";
// set ropsten LINK fee
fee = 1000000000000000000;
// set example seed
seed = 123456789;
// make call to request randomness
bytes32 reqId = rand.requestRandomness(keyHash, fee, seed);
When the result comes back, the value will be present and can be retrieved by calling:
rand.randomNumber;
Try it out yourself
This section is going to walk through how we can get a random number from VRF using Remix IDE and Metamask. Make sure you have Metamask extension installed on your browser before continuing.
- Head over to Remix IDE.
- If youโve not used Remix before, make sure youโre using Solidity, as shown in figure 3.
- Create a new file called RandomGenerator and paste in the code from figure 2.
- Using the left menu, click on the Solidity icon and chose 0.6.2 compiler version, as shown in figure 4.
- Then, click on the button just below that and chose โInjected web3โ in the dropdown, as shown in figure 5.
- This should prompt a connection request from Metamask which you should accept.
- Make sure youโre using the Ropsten testnet on Metamask, as shown in figure 6.
- Make sure you have some Ropsten Ether in your Metamask account, which you can request here.
- Heading back to Remix, on that same tab, you should see an orange โDeployโ button, click that button and accept the contract deployment request from Metamask.
- Once deployed, we need to make sure the contract has LINK tokens so it can request random numbers. Head over the Ropsten LINK faucet and paste in your Metamask address, so that you receive 100 LINK in Metamask.
- Metamask wonโt know where the LINK token is on Ropsten, so we need to add it. In Metamask, to the left of your account name, click the burger symbol, then click โAdd Tokenโ at the bottom.
- Under โCustom Tokenโ, add the address:
0x20fE562d797A42Dcb3399062AE9546cd06f63280
. The rest of the details should fill in automatically. When submitted, providing you requested LINK to the correct address, you should see 100 LINK in your account. Figure 7 shows an account with 70 LINK.
- Back to Remix, copy the address of the deployed contract by clicking the button circled in figure 8.
- Now weโre going to send the contract some of our LINK. Head back onto Metamask, and click on the 3 dots next to the 100 LINK. Paste the contract address and send 10 LINK. Once the transaction has been confirmed, move to the next step.
- In Remix, we can now request randomness. In the same tab, scrolling down youโll find more orange buttons representing public functions, as shown in figure 9. Click on the arrow to the right of
requestRandomness
to expand the parameters.
- Paste in each of these values into the three input boxes in order:
0xced103054e349b8dfb51352f0f8fa9b5d20dde3d06f9f43cb2b85bc64b238205
,1000000000000000000
,123456789
(or whatever you want your seed to be). Then hit โTransactโ!
This may take some time, so you should keep an eye on the transaction in Etherscan that the output terminal gives you.
Once that transaction has completed, we need to wait for VRF to generate the random number and send it back to our contract. After a few minutes, check if your contract has received the random number by clicking the blue โrandomNumberโ button below the orange button where we sent our transaction in Remix, as shown in figure 12.
If all goes well, you should have a random number like me, which is 30207470459964961279215818016791723193587102244018403859363363849439350753829
.
Congratulations!
Conclusion
Chainlink has demonstrated that verifiable random numbers are now possible in smart contracts. Weโve explained how the mechanism works, how to integrate the code into smart contracts, and a demonstration of retrieving random numbers using the Remix IDE.
Further Reading
If youโre interested in Blockchain Development, I write tutorials, walkthroughs, hints, and tips on how to get started and build a portfolio. Check out some of these resources: