EVILink shows that a malicious miner still has a slim chance to tamper randomness provided by Chainlink's VRF solution.
Chainlink VRF is an awesome solution for NEARLY tamper-proof randomness. Why say NEARLY tamper-proof? For a verifiable randomness implemented by Chainlink, it is composed by these parameters ordered by their generation time:
vrf_key
- generated before the corresponding public key registered as VRF serviceuser_seed
- provided when user interact with the contractblock_hash
andblock_number
- block containing the eventRandomnessRequest
The only parameter tamperable after transaction sent is the block_hash
, which can be easily tuned by change the block head extra data, but miner still has no way to predict randomness if it does not have vrf_key
. However, there exists these situations for randomenss to be tamperable:
vrf_key
is leaked to minervrf_key
is held by miner from the beginning- miner conspires with the malicious
vrf_key
holder
So miner can tune the block_hash
to get the favorable result before mining.
Condition | Diagram |
---|---|
Normal | ![]() |
Key Leaked | ![]() |
Here I present the first situation of randomenss to be tamperable to hack a coin tossing game. When malicious miner steals the vrf_key
, it can make owner of the contract FlipCoin.sol
always win and others always lose.
Owner | Others |
---|---|
![]() |
![]() |
So why Chainlink includes the manipulatable block information to be part of randomness? The reason is because the randomness will be fully manipulatable by vrf_key
holder if it is only composed by vrf_key
and user_seed
(when holder is the user). The design to include block information prevents the single-point-malicious in most time.
From Chainlink's awesome blog, they are already integrating threshold signatures with VRF to prevent single-key-leaked-failure, which is not only cost-effective and high-availability, but also is almost a tamper-proof randomness solution (in theory we still have very slim change to collect enough key shares, but extremely hard). Super looking forward to the approach.
But before the upcoming approach, we can still use current version VRF with some good practices:
- Contract should have a mechanism (by voting or by admin) to change the VRF service in case current one is thought malicious or key leaked.
- Contract could request multiple randomness in one transaction and combine incoming randomnesses to be final randomness for a safer approach, it will be tamper-proof when at least one secret key holder is safe and honest. However, it costs much more both time and LINK. Implementation can be found here.
Package | Description |
---|---|
@evilink/contracts-chainlink |
Chainlink related contracts |
@evilink/contracts-faucet |
Faucet contract for ether |
@evilink/contracts-flipcoin |
Coin tossing game using VRFConsumer |
Package | Description |
---|---|
@evilink/artifact-util |
Artifact util for contract factory |
@evilink/constant |
Constant including contract address, genesis private key |
Package | Description |
---|---|
@evilink/chainlink-client |
Chainlink API client |
@evilink/chainlink-orm |
Chainlink ORM client |
@evilink/chainlink-vrf |
Chainlink VRF implementation in golang as gyp binding |
Package | Description |
---|---|
@evilink/evilthereum |
Malicious miner with VRF key hacking VRFConsumers |
@evilink/playground |
Frontend for interaction with hacked contracts |
- go1.15
- yarn2
# build chainlink vrf for gyp binding
pushd packages/chainlink-vrf/go && make && popd
yarn --immutable
yarn build
bash script/run.sh build
cp docker-compose.sample.env docker-compose.env
# after update the values in docker-compose.env
bash script/run.sh up
# wait for ipfs and graph-node ready
bash script/run.sh deploy_subgraph flipcoin