Skip to content

Commit

Permalink
FTSO tutorial on the random number FLR-537
Browse files Browse the repository at this point in the history
  • Loading branch information
swarna1101 committed Jun 28, 2024
1 parent 18b0045 commit 6c392bf
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 1 deletion.
111 changes: 111 additions & 0 deletions docs/dev/tutorials/ftso/getting-random-numbers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Getting Random Numbers

This tutorial shows how to obtain random numbers from the [Flare Systems Protocol (FSP)](../../../tech/flare-systems-protocol.md), the infrastructure that powers most current Flare protocols.
The source of the randomness is the submissions from all [FTSO data providers](../../../tech/ftso.md#procedure-overview) and is therefore not centralized.

Random numbers are generated every 90 seconds and can be read directly from a smart contract.

This is useful in several development contexts where secure, fair random numbers are required, such as in games and certain blockchain protocol functionalities such as [selecting a random vote power block](../../../tech/ftso.md#vote-power).

!!! info "Security and Fairness"

A generated random number is tagged as secure if all data providers correctly followed the FTSO protocol and at least one of them is not malicious.

If a number is tagged as secure, then the protocol guarantees its fairness, meaning that it has no bias and all outcomes are equally probable.

This tutorial shows:

* How to obtain a random number.
* How to use the Flare periphery packages to simplify working with the Flare API.

## Code

Choose your preferred programming language and ensure you have a working [development environment](../../getting-started/setup/index.md).

For easy navigation, numbered comments in the source code (e.g. `// 1.`) link to the tutorial sections below.

{% import "runner.md" as runner with context %}

=== "Solidity"

{{ runner.sol("ftso/", "GetRandomNumber") | indent(4) }}

=== "JavaScript"

{{ runner.js("ftso/", "GetRandomNumber", runFromBrowser='false') | indent(4) }}

<script>
--8<-- "./docs/samples/ftso/GetRandomNumber.js::34"
</script>

<div class="tutorial" markdown>

## Tutorial

### 1. Import Dependencies

The tutorial uses the following dependencies:

* The [Flare Periphery Package](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) for Solidity and the [Flare Periphery Artifacts Package](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contract-artifacts) for JavaScript, which provide the API for all Flare smart contracts.

* If you use JavaScript, the [ethers](https://www.npmjs.com/package/ethers) package is also needed to work with smart contracts.

{{ runner.multisnippet("ftso/GetRandomNumber", 3, 5, 8, 11) }}

The Periphery Packages simplify working with the Flare smart contracts significantly.

!!! warning
If you remove this dependency, you must manually provide the signatures for all the methods you want to use.

### 2. Access the Contract Registry

The [`FlareContractRegistry`](FlareContractRegistry.md) contains the current addresses for all Flare smart contracts, and it is [the only recommended way](../../getting-started/contract-addresses.md) to retrieve them.

=== "Solidity"

The `FlareContractsRegistryLibrary` contract from the Flare Periphery Package accesses the Flare Contract Registry for you, as shown next.

=== "JavaScript"

The address of the Flare Contract Registry is the same on all of [Flare's networks](../../../tech/flare.md#flare-networks), and it is the only Flare address that needs to be hard-coded into any program.

```javascript title="GetRandomNumber.js" linenums="3"
--8<-- "./docs/samples/ftso/GetRandomNumber.js:3:4"
```

```javascript title="GetRandomNumber.js" linenums="13"
--8<-- "./docs/samples/ftso/GetRandomNumber.js:13:17"
```

### 3. Retrieve the Relay Contract

Use the [`getContractAddressByName()`](FlareContractRegistry.md#fn_getcontractaddressbyname_82760fca) method of the [`FlareContractRegistry`](FlareContractRegistry.md) smart contract to retrieve the address of the `Relay` smart contract.

{{ runner.multisnippet("ftso/GetRandomNumber", 9, 11, 20, 27) }}

### 4. Get the Random Number

Get the latest generated random number by calling the `getRandomNumber()` method of the `Relay` contract.

{{ runner.multisnippet("ftso/GetRandomNumber", 12, 15, 30, 33) }}

In addition to the `randomNumber`, two other variables are retrieved:

* `isSecure` is a boolean flag that indicates whether the random number was generated securely, according to the description given in the introduction.

The random number is based on all the data providers' submissions and is therefore decentralized, improving transparency and fairness.
However, this decentralization makes the protocol slightly open to attempts at manipulation.
If such manipulation attempts are detected, the `isSecure` flag is set to `false`, and dapps can decide whether they should discard the generated number.

* `timestamp` is the [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) indicating the time at the end of the voting epoch during which data was collected from data providers to generate this particular number.

The timestamp can be useful, for example, to ensure that certain actions have been performed before the random number was generated.
For example, in a roulette game, to ensure all bets were placed before the number was generated.
Each voting epoch is a fixed 90-second window.

## Conclusion

This tutorial has shown:

* How to use the Flare Periphery Package, both from [Solidity](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) and from [JavaScript](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contract-artifacts), to work with the Flare API.
* How to get the latest random number via the `Relay` contract.
1 change: 1 addition & 0 deletions docs/dev/tutorials/ftso/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ These code samples and explanations show how to use the [FTSO system](../../../t

* [FTSO Developer Overview](../../../dev/reference/ftso.md)
* [Getting FTSO Data Feeds](../ftso/getting-data-feeds.md)
* [Getting Random Numbers](../ftso/getting-random-numbers.md)
36 changes: 36 additions & 0 deletions docs/samples/ftso/GetRandomNumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const FLARE_CONTRACTS = "@flarenetwork/flare-periphery-contract-artifacts";
const FLARE_RPC = "https://coston-api.flare.network/ext/C/rpc";
const FLARE_CONTRACT_REGISTRY_ADDR =
"0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019";

async function runGetRandomNumber() {
// 1. Import Dependencies
const ethers = await import("ethers");
const flare = await import(FLARE_CONTRACTS);
const provider = new ethers.JsonRpcProvider(FLARE_RPC);

// 2. Access the Contract Registry
const flareContractRegistry = new ethers.Contract(
FLARE_CONTRACT_REGISTRY_ADDR,
flare.nameToAbi("FlareContractRegistry", "coston").data,
provider
);

// 3. Retrieve the Relay Contract
const relayAddress = await flareContractRegistry.getContractAddressByName(
"Relay"
);
const relay = new ethers.Contract(
relayAddress,
flare.nameToAbi("IRelay", "coston").data,
provider
);

// 4. Get the Random Number
const [randomNumber, isSecure, timestamp] = await relay.getRandomNumber();
console.log("Random Number is", randomNumber);
console.log("Is it secure", isSecure);
console.log("Creation timestamp is", timestamp);
}

runGetRandomNumber();
17 changes: 17 additions & 0 deletions docs/samples/ftso/GetRandomNumber.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

import {IRelay} from "@flarenetwork/flare-periphery-contracts/coston/util-contracts/userInterfaces/IRelay.sol";
import {FlareContractsRegistryLibrary} from "@flarenetwork/flare-periphery-contracts/coston/util-contracts/ContractRegistryLibrary.sol";

contract GetRandomNumber {
function generateNumber() external view returns (uint256, bool, uint256) {
address relayAddress =
FlareContractsRegistryLibrary.getContractAddressByName("Relay");
IRelay relay = IRelay(relayAddress);
(uint256 randomNumber, bool isSecure, uint256 timestamp) =
relay.getRandomNumber();

return (randomNumber, isSecure, timestamp);
}
}
33 changes: 33 additions & 0 deletions docs/samples/ftso/GetRandomNumber.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Import dependencies
import "forge-std/Test.sol";
import "../src/GetRandomNumber.sol";

// Test Contract
contract GetRandomNumberTest is Test {
string private constant FLARE_RPC =
"https://flare-api.flare.network/ext/bc/C/rpc";
uint256 private flareFork;

function setUp() public {
flareFork = vm.createFork(FLARE_RPC);
}

function testRandomNumber() public {
vm.selectFork(flareFork);
GetRandomNumber randNumber = new GetRandomNumber();

(uint256 _randomNumber, bool _isSecure, uint256 _timeStamp) = randNumber
.getRandomNumber();

assertGt(_randomNumber, 0, "Random Number expected to be > 0");
assertTrue(_isSecure, "Expect to be true");
assertGt(
_timestamp,
1695817332,
"Timestamp expected to be greater than a known past block"
);
}
}
16 changes: 16 additions & 0 deletions docs/samples/ftso/TestGetRandomNumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { expect } = require("chai");
describe("Test Random Number", function () {
let contract;
beforeEach(async function () {
contract = await ethers.deployContract("GetRandomNumber");
});

it("RandomNumber", async function () {
const [randomNumber, isSecure, timestamp] = await contract.generateNumber();
expect(randomNumber).to.be.at.least(
1000000000000000000000000000000000000000n
);
expect(isSecure).to.be.true;
expect(timestamp).to.be.gt(1695817332);
});
});
2 changes: 1 addition & 1 deletion include/runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
3. Initialize project and install dependencies with:
```bash
npm init
npm install ethers@6.3 @flarenetwork/flare-periphery-contract-artifacts@0.1.7
npm install ethers@6.3 @flarenetwork/flare-periphery-contract-artifacts@0.1.15
```
4. Run the program with:
```bash
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ nav:
- dev/tutorials/ftso/index.md
- dev/reference/ftso.md
- dev/tutorials/ftso/getting-data-feeds.md
- dev/tutorials/ftso/getting-random-numbers.md
- State Connector:
- dev/tutorials/sc/index.md
- dev/tutorials/sc/address-validity.md
Expand Down

0 comments on commit 6c392bf

Please sign in to comment.