Skip to main content

Every fairness page in crypto gaming says the same four words: we use Chainlink VRF. Almost none of them say what else goes into the number.

That is not a small omission. A verifiable random function hands you one 256 bit value. A game needs an index into a list of entrants, or a side of a coin, and getting from the value to the outcome is arithmetic the operator wrote. Some map the VRF word straight onto the result. Others stir in a second ingredient first, a block hash or a timestamp or a nonce or a server seed. Satoshie’s raffle is in the second group. The winning index is keccak256 of the VRF word combined with a prior block hash, taken modulo the number of tickets minted.

When you combine two sources of randomness, what do you actually get?

The instinctive answer is “more randomness”. The instinctive answer is wrong, and it can be wrong in either direction, and which direction depends entirely on one thing: who supplies the last ingredient.

TL;DR

  • Combining randomness is a strongest link property, not a weakest link one: a hash of several inputs is as unpredictable as the best independent input, not the average of them.
  • That holds only if nobody chooses their input after seeing the others. Whoever moves last can grind candidate values and keep the one they like.
  • Grinding is cheap in compute and expensive in opportunity. The limit is not hashes per second, it is how many distinct values of the last input an attacker can actually make appear on chain.
  • A Chainlink VRF word cannot be ground at all. One key and one seed produce exactly one valid output, and every other candidate fails the on-chain proof check.
  • So a second ingredient adds no randomness to a VRF draw. It buys insurance against the VRF itself, and it costs you a second party in the threat model. Ask any platform which one it has bought.

Combining randomness is not averaging it

Nearly every security property in this industry is weakest link. A game is as trustworthy as its front end, its contract, its chain and its oracle, all at once, and the answer is whichever of those is worst.

Entropy combination runs the other way.

Take a value A that is genuinely unpredictable, and a value B that is not random at all, that is in fact picked by someone who wants a particular winner. Hash them together. If B was fixed before A existed, the output is still uniform, and still unpredictable to everyone including the person who chose B. Adding a known value to an unknown one leaves it unknown. You can add ten such values, nine of them attacker controlled, and the result remains as unpredictable as the single honest input.

You do not have to trust every contributor. You only have to trust that at least one of them was real.

The condition is a clock, not a count

But the load bearing clause in all of that is easy to skim past: if B was fixed before A existed.

Flip the order and the property inverts. If the person supplying B gets to look at A first, they are no longer contributing to the randomness. They are searching through it. They compute the combined result for one candidate B, check whether they like the winner, and if not they try another. Nothing about the hash function fails here. The attacker is simply sampling the output distribution until it produces something convenient, and every sample is honest.

This is why the number of inputs tells you almost nothing about a draw. The only structural question is which input lands last, and whether whoever supplies it can see the rest when they choose it.

What a re-roll is actually worth

On a coinflip, a single attempt wins half the time. Two attempts win 75% of the time. Four win 93.75%. Ten attempts give you the result you wanted better than 999 times in 1,000. Fairness of each sample is not fairness of the process that selected which sample you were shown.

On a raffle the curve is flatter and the conclusion is worse, because raffles are where the money is. Take 10,000 tickets and an attacker who wants one specific ticket to win. A hundred candidate values gets them there just under 1% of the time. A thousand gets them 9.5%. Around 6,900 attempts and it is a coin flip whether they simply have the prize. None of those numbers require breaking anything.

Now the part people get backwards. The cost of that search is not computational. Keccak256 over a short input runs millions of times a second on an ordinary laptop, so 6,900 candidates is a rounding error in wall clock time. The scarce resource is candidate values the attacker can actually cause to exist.

That is the real bound, and it varies enormously by what the last input is. If the final ingredient is a nonce in the attacker’s own transaction, they have as many attempts as they care to type, and the grind is unlimited. If it is a block hash, then the attacker has to be whoever builds blocks, and each additional attempt costs them one block withheld or reordered, which is both expensive and publicly visible. If it is a value committed before the draw opened, they have exactly one attempt, which is to say none.

Why a VRF word has exactly one candidate

The property that makes a verifiable random function worth the name is routinely explained as though it were about quality of randomness. It is not. It is about the count of candidates.

The operating node commits to a key in advance, on chain, before your draw exists. Given that key and a given seed, the output is deterministic. There is precisely one valid value, it comes with a proof, and the coordinator verifies that proof on chain before the callback is allowed to run. A node that generates the word, dislikes the winner it produces and wants another go has nothing to submit. Every alternative it could invent fails verification and never reaches your contract.

That is the difference between a VRF and a random number from a trusted API, and it has nothing to do with statistical quality. A well seeded server side generator produces numbers that pass every distribution test you can throw at them. What it cannot do is prove it only rolled once.

Which leads somewhere slightly uncomfortable. If the VRF word cannot be ground, then a draw resolved from that word alone already has the property everything above is chasing. Adding a second ingredient cannot make it more unpredictable. It can only leave it unchanged, if the ingredient is committed early, or degrade it, if the ingredient arrives late and someone chooses it.

A second input does not add randomness to a VRF draw. It adds a second party to the threat model.

Then why mix anything in at all

Because “cannot be ground” is a claim about the key, and keys are objects in the world.

Everything above assumes the VRF node’s secret key is secret. If it ever is not, the picture changes completely: whoever holds that key can compute the word for a given seed in advance, before the callback fires, and knows the winner while the draw still looks open. Against that failure, a second value the key holder does not control and cannot know when they make their move is the only thing standing between them and the outcome. It buys nothing on a normal day and everything on the worst one.

That is the actual trade, and it is worth stating plainly. Mixing a second source into a VRF draw is insurance against the VRF, bought at the price of having to be right about ordering. Just know which policy you hold, and never call it more randomness, because it is not.

One clarification. An earlier piece here asked whether a provably fair game can still be front run: that was about who decides the order in which plays execute. This is about who supplies the last ingredient of the number itself. Different clock, different party.

What Satoshie does, and what you can check

The coinflip takes the VRF word and reduces it modulo two. One input, no mixing, no second party, and the ordering question does not arise because there is no second ingredient to order.

The raffle combines the VRF word with a prior block hash before reducing modulo the tickets minted, so it is squarely in the category this post is about, and it carries the trade described above: insurance against the randomness source, paid for in a requirement that the ordering be right.

We are not asking anyone to take our word for either. The contract runs on Base, the resolution logic is verified and immutable on BaseScan, the VRF coordinator address is fixed in deployed code and readable before you stake, ticketsMinted is contract state you can read before buying, and the VRF request and fulfilment transaction ids are published alongside every result. Which value was fixed when is answerable from public data by anyone, including somebody who has never played and never intends to.

So the next time a platform’s fairness page tells you it uses Chainlink VRF, believe it, and then ask the follow up. What else goes into the number, and who was holding the pen when it was chosen?

📷 Photo by Pawel Czerwinski on Unsplash

Valentina Ní Críonna

Author Valentina Ní Críonna

More posts by Valentina Ní Críonna