Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add randomness doc #441

Merged
merged 29 commits into from
Nov 8, 2023
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c391650
Add randomness doc
chasefleming Nov 8, 2023
52340fd
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
d4c854a
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
632dbba
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
29843f4
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
c133021
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
f5af7e9
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
f33192e
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
85c3019
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
0dbd254
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
59e678d
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
ad20d6f
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
fb4f52c
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
e1c6aff
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
8e9b869
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
37a79b6
Fix line breaks
chasefleming Nov 8, 2023
fd7b979
Fix line breaks
chasefleming Nov 8, 2023
8ff1e52
Fix line breaks
chasefleming Nov 8, 2023
5115b90
Fix line breaks
chasefleming Nov 8, 2023
2e48f89
Fix list formatting
chasefleming Nov 8, 2023
6d0db1f
Fix title case
chasefleming Nov 8, 2023
c693acc
Change title
chasefleming Nov 8, 2023
eccee7d
Change title
chasefleming Nov 8, 2023
5629953
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
1b546af
Update docs/build/advanced-concepts/randomness.md
chasefleming Nov 8, 2023
e862d91
Fix casing
chasefleming Nov 8, 2023
ca01103
Add suggestion
chasefleming Nov 8, 2023
dc81aa2
Remove
chasefleming Nov 8, 2023
7612939
Remove
chasefleming Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions docs/build/advanced-concepts/randomness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
title: Flow's Verified Random Function (VRF)
sidebar_label: Verified Random Function (VRF)
---

# Native Verified Random Function

Flow enhances blockchain functionality and eliminates reliance on external oracles by providing native onchain randomness at the protocol level. This secure, decentralized feature empowers developers to build a variety of applications with truly unpredictable, transparent, and fair outcomes, achieved with greater efficiency.

Flow’s onchain randomness delivers immediate random values within smart contracts, bypassing the latency and complexity of oracle integration. Developers can obtain verifiably random results with a single line of Cadence code, streamlining the development process and enhancing the performance of decentralized applications.

## Use Cases of Onchain Randomness

- **Gaming:** Integrates fairness and unpredictability into gameplay, enhancing user engagement without delays.
- **NFTs:** Facilitates the creation of uniquely randomized traits in NFTs quickly, adding to their rarity and value.
- **Lotteries & Draws:** Offers instant and verifiably fair random selection for lotteries, solidifying trust in real-time.
- **DeFi Protocols:** Enables rapid and innovative random reward systems within decentralized finance.
- **DAOs:** Assists in unbiased voting and task assignments through immediate randomness.
- **Broad Applications:** Extends to any domain requiring impartial randomization, from asset distribution to security mechanisms, all with the added benefit of on-demand availability.

## History of the Distributed Random Beacon

Within the Flow protocol, the heart of randomness generation lies in the "Distributed Random Beacon."
This module generates randomness that is distributed across the network while adhering to established cryptographic and security standards.
The output from the randomness beacon is a sequence of random bytes that are unpredictable and impartial.

For over three years, the beacon has ensured protocol security by selecting which consensus node gets to propose the next block and assigning verification nodes to oversee block computations. For those interested in a more detailed exploration of the randomness beacon and its inner workings, you can read [the technical deep dive on the Flow forum](https://forum.flow.com/t/secure-random-number-generator-for-flow-s-smart-contracts/5110).

### The History and Limitations of `unsafeRandom` (Now Deprecated)

Cadence has historically provided the `unsafeRandom` function to return a pseudo-random number. The stream of random numbers produced was potentially unsafe in the following two regards:

1. The sequence of random numbers is potentially predictable by transactions within the same block and by other smart contracts calling into your smart contract.
2. A transaction calling into your smart contract can potentially bias the sequence of random numbers which your smart contract internally generates. Currently, the block hash seeds `unsafeRandom`. Consensus nodes can *easily* bias the block hash and **influence the seed for `unsafeRandom`**.

⚠️ Note `unsafeRandom` will be deprecated in the next Cadence release. **Developers are advised to refrain from using `unsafeRandom.`**

chasefleming marked this conversation as resolved.
Show resolved Hide resolved
## Guidelines for Safe Usage

For usage of randomness where result abortion is not an issue, it is recommended to use the Cadence built-in function `revertibleRandom.` `revertibleRandom` returns a pseudo-random number and is also based on the Distributed Random Beacon.

```cadence
// Language reference:
// https://developers.flow.com/cadence/language/built-in-functions#revertiblerandom
chasefleming marked this conversation as resolved.
Show resolved Hide resolved
// Run the snippet here: https://academy.ecdao.org/en/snippets/cadence-random
access(all) fun main(): UInt64 {
// Simple assignment using revertibleRandom - keep reading docs for safe usage!
let rand: UInt64 = revertibleRandom()
return rand
}
```

It is notable that the random number generation process is unpredictable (for miners unpredictable at block construction time and for cadence logic unpredictable at time of call), verifiable, uniform, as well as safe from bias by miners and previously-running Cadence code.

Although Cadence exposes safe randomness generated by the Flow protocol via `revertibleRandom`, there is an additional safety-relevant aspect that developers need to be mindful about.

The `revertibleRandom` function can be used safely in some applications where the transaction results are _not_ deliberately reverted after the random number is revealed (i.e. a contract distributing random NFTs to registered users or onchain lucky draw).
However, if applications require a non-trusted party (for instance app users) to submit a transaction calling a randomized (non-deterministic) contract, the developer must explicitly protect the stream of random numbers to not break the security guarantees:

<Callout type="warning">
🚨 A transaction can atomically revert all its action during its runtime and abort. Therefore, it is possible for a transaction calling into your smart contract to post-select favorable results and revert the transaction for unfavorable results.
chasefleming marked this conversation as resolved.
Show resolved Hide resolved
</Callout>

In other words, transactions submitted by a non-trusted party are able to reject their results after the random is revealed.

<Callout type="info">
💡 **Post-selection** - the ability for transactions to reject results they don't like - is inherent to any smart contract platform that allows transactions to roll back atomically. See this very similar [Ethereum example](https://consensys.github.io/smart-contract-best-practices/development-recommendations/general/public-data/).
</Callout>

The central aspect that a contract developer needs to think about is the following scenario:

- Imagine an adversarial user that is sending a transaction that calls your smart contract.
- The transaction includes code that runs after your smart contract returns and inspects the outcome.
- If the outcome is unfavorable (based on some criteria codified in the transaction), the transaction aborts itself.

As an example, consider a simple coin toss randomized contract where users can bet any amount of tokens against a random binary output. If the coin toss contract outputs `1`, the user doubles their bet. If the coin toss contract outputs `0`, the user loses their bet in favor of the coin toss.

Although the user (or the honest coin toss contract) cannot predict or bias the outcome, the user transaction can check the randomized result and cancel the transaction if they are losing their bet. This can be done by calling an exception causing the transaction to error. All temporary state changes are cancelled and the user can repeat the process till they double their bet.

## Commit-Reveal Scheme

The recommended way to mitigate the problems above is via a commit-reveal scheme. The scheme involves two steps: commit and reveal. During the commit phase, the user transaction commits to accepting the future output of a smart contract where the last remaining input is an unknown stream of random numbers. The smart contract stores this commitment on the blockchain. At the current level of optimization, the reveal phase can start as early as the next block, when the beacon's source of randomness becomes available. With a second transaction, the smart contract can be executed to explicitly generate the output now that the stream of random numbers can be seeded (plus suitable salt).

There are ideas how to further optimize the developer experience in the future. For example, a transaction could delegate part of its gas to an independent transaction it spawns. Conceptually, also this future solution would be a commit-and-reveal scheme, just immediately happening within the same block. Until we eventually get to this next level, developers may need to implement their own commit-reveal. In Cadence, it is clean and short.

Protocol improvements (documented in [FLIP 120](https://github.com/onflow/flips/blob/main/cadence/20230713-random-function.md))
expose the random beacon to the FVM and Cadence where it can be used to seed pseudo-random number generators [PRNGs] for smart contracts.

While both are backed by Flow's Random Beacon it is important for developers to mindfully choose between `revertibleRandom` or
seeding their own PRNG utilizing the `RandomBeaconHistory` smart contract:

- Under the hood, the FVM also just instantiates a PRNG for each transaction that `revertibleRandom` draws from.
Though, with `revertibleRandom` a developer is calling the PRNG that is controlled by the transaction,
which also has the power to abort and revert if it doesn't like `revertibleRandom`'s outputs.
`revertibleRandom` is only suitable for smart contract functions that exclusively run within the trusted transactions.
- In contrast, using the `RandomBeaconHistory` means to use a deterministically-seeded PRNG.
The `RandomBeaconHistory` is key for effectively implementing a commit-and-reveal scheme.
During the commit phase, the user commits to proceed with a future source of randomness,
which is revealed after the commit transaction concluded.
For each block, the `RandomBeaconHistory` automatically stores the subsequently generated source of randomness.

Adding a safe pattern to reveal randomness without the possibility of conditional transaction reversion unlocks applications relying on randomness. By providing examples of commit-reveal implementations we hope to foster a more secure ecosystem of decentralized applications and encourage developers to build with best practices.

In simpler terms, the native secure randomness provided by the protocol can now be safely utilized within Cadence smart contracts
and is available to all developers on Flow and the FVM.

## FLIP 123

On Flow, we have absorbed all security complexity into the platform.

[FLIP 123: On-chain Random beacon history for commit-reveal schemes](https://github.com/onflow/flips/blob/main/protocol/20230728-commit-reveal.md#flip-123-on-chain-random-beacon-history-for-commit-reveal-schemes) was introduced to provide a safe pattern to use randomness in transactions so that it's not possible to revert unfavorable randomized transaction results.
We recommend this approach as a best-practice example for implementing a commit-reveal-recover scheme in Cadence. The `RandomBeaconHistory` contract provides a convenient archive, where for each past block height (starting Nov 2023) the respective “source of randomness” can be retrieved. The `RandomBeaconHistory` contract is automatically executed by the system at each block to store the next source of randomness value.

<Callout type="info">
💡 While the commit-and-reveal scheme mitigates post-selection of results by adversarial clients, Flow’s secure randomness additionally protects against any pre-selection vulnerabilities (like biasing attacks by byzantine miners).
</Callout>

A commit-reveal scheme can be implemented as follows. The coin toss example described earlier will be used for illustration:

- When a user submits a bidding transaction, the bid amount is transferred to the coin toss contract, and the block height where the bid was made is stored. This is a commitment by the user to use the SoR at the current block. Note that the current block's `SoR_A` isn't known to the transaction execution environment, and therefore the transaction has no way to inspect the random outcome and predict the coin toss result. The current block's `SoR_A` is only available once added to the history core-contract, which only happens at the end of the block's execution. The user may also commit to using an SoR of some future block, which is equally unknown at the time the bid is made.
- The coin toss contract may grant the user a limited window of time (i.e a block height range) to send a second transaction for resolving the results and claim any winnings. Failing to do so, the bid amount remains in the coin toss contract.
- Within that reveal transaction, the user calls the coin toss contract, looks us up the block height at which the block was committed and checks that it has already passed. The contract queries that block's `SoR_A` from the core-contract `RandomBeaconHistory` via block height.
- The coin toss contract uses a PRG seeded with the queried `SoR_A` and diversified using a specific information to the use-case (a user ID or resource ID for instance). Diversification does not add new entropy, but it avoids generating the same outcome for different use-cases. If a diversifier (or salt) isn't used, all users that committed a bid on the same block would either win or lose.
- The PRG is used to generate the random result and resolve the bid. Note that the user can make the transaction abort after inspecting a losing result. However, the bid amount would be lost anyway when the allocated window expires.

The following lines of code illustrate a random coin toss that cannot be gamed or biased. The reveal-and-commit scheme prevent clients from post-selecting favorable outcomes.

```cadence
// The code below is taken from the example CoinToss contract found in this project repo
// Source: https://github.com/onflow/random-coin-toss

/// --- Commit ---
chasefleming marked this conversation as resolved.
Show resolved Hide resolved
/// In this method, the caller commits a bet. The contract takes note of the
/// block height and bet amount, returning a Receipt resource which is used
/// by the better to reveal the coin toss result and determine their winnings.
access(all) fun commitCoinToss(bet: @FungibleToken.Vault): @Receipt {
let receipt <- create Receipt(
betAmount: bet.balance
)
// commit the bet
// `self.reserve` is a `@FungibleToken.Vault` field defined on the app contract
// and represents a pool of funds
self.reserve.deposit(from: <-bet)

emit CoinTossBet(betAmount: receipt.betAmount, commitBlock: receipt.commitBlock, receiptID: receipt.uuid)

return <- receipt
}

/// --- Reveal ---
/// Here the caller provides the Receipt given to them at commitment. The contract
/// then "flips a coin" with randomCoin(), providing the committed block height
/// and salting with the Receipts unique identifier.
/// If result is 1, user loses, if it's 0 the user doubles their bet.
/// Note that the caller could condition the revealing transaction, but they've
/// already provided their bet amount so there's no loss for the contract if
/// they do
access(all) fun revealCoinToss(receipt: @Receipt): @FungibleToken.Vault {
pre {
receipt.commitBlock <= getCurrentBlock().height: "Cannot reveal before commit block"
}

let betAmount = receipt.betAmount
let commitBlock = receipt.commitBlock
let receiptID = receipt.uuid
// self.randomCoin() errors if commitBlock <= current block height in call to
// RandomBeaconHistory.sourceOfRandomness()
let coin = self.randomCoin(atBlockHeight: receipt.commitBlock, salt: receipt.uuid)

destroy receipt

if coin == 1 {
emit CoinTossReveal(betAmount: betAmount, winningAmount: 0.0, commitBlock: commitBlock, receiptID: receiptID)
return <- FlowToken.createEmptyVault()
}

let reward <- self.reserve.withdraw(amount: betAmount * 2.0)

emit CoinTossReveal(betAmount: betAmount, winningAmount: reward.balance, commitBlock: commitBlock, receiptID: receiptID)

return <- reward
}
```

## An Invitation to Build

Flow's onchain randomness opens new doors for innovation in web3, offering developers the tools to create fair and transparent decentralized applications. With this feature, new possibilities emerge—from enhancing gameplay in decentralized gaming to ensuring the integrity of smart contract-driven lotteries or introducing novel mechanisms in DeFi.

This is an invitation for builders and creators: leverage Flow's onchain randomness to distinguish your projects and push the boundaries of what's possible. Your imagination and code have the potential to forge new paths in the web3 landscape. So go ahead and build; the community awaits the next big thing that springs from true randomness.

## Learn More

If you’d like to dive deeper into Flow’s onchain randomness, here’s a list of resources:

- To learn more about how randomness works under the hood, see [the forum post](https://forum.flow.com/t/secure-random-number-generator-for-flow-s-smart-contracts/5110).
- These documents provide a more in-depth technical understanding of the updates and enhancements to the Flow blockchain.
- **[FLIP 120: Update unsafeRandom function](https://github.com/onflow/flips/blob/main/cadence/20230713-random-function.md#flip-120-update-unsaferandom-function)**
- **[FLIP 123: On-chain Random beacon history for commit-reveal schemes](https://github.com/onflow/flips/blob/main/protocol/20230728-commit-reveal.md#flip-123-on-chain-random-beacon-history-for-commit-reveal-schemes)**
- To see working Cadence code, explore the [coin toss example on GitHub](https://github.com/onflow/random-coin-toss/tree/update-prg-impl).