Skip to content

Commit

Permalink
Merge pull request #41 from FhenixProtocol/randomness
Browse files Browse the repository at this point in the history
Randomness
  • Loading branch information
toml01 authored Oct 28, 2024
2 parents d10dd4a + 9e7c418 commit cdaa5c5
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 32 deletions.
6 changes: 0 additions & 6 deletions docs/devdocs/Fhenix Testnet/Details/Limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ real sensitive data or private keys on the testnet.
The current iteration of the network does not include multiple components (such as input knowledge proofs, threshold decryption, execution proofs, etc.) that are critical for the security of data and network keys.
These features will be added iteratively as we move towards full release - this should be obvious, but please **do not store any valuable information on the network as long as it is in the testnet phase**.

## Randomness

Randomness as a service is planned as a future addition. Until we can guarantee a secure source of randomness, we do not
want to make such a function available as a network service. For demos and development that require a source of randomness, we encourage
the use of external oracles, or usage of a [mock random number generator](../../Writing%20Smart%20Contracts/Useful-Tips.md#randomness).

## Gas Costs

All gas costs are subject to change, and are being evaluated for optimization. The current gas costs are not final, and may change.
Expand Down
116 changes: 116 additions & 0 deletions docs/devdocs/Writing Smart Contracts/Randomness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Randomness

Randomness was introduced in the Nitrogen testnet.
Contracts in Fhenix can get random numbers by calling one of the randomness functions in `FHE.sol`.
These functions are:
```solidity
import {
FHE, euint8, euint16, euint32, euint64, euint128, euint256
} from "@fhenixprotocol/contracts/FHE.sol";
euint8 randomValue = FHE.randomEuint8();
euint16 randomValue = FHE.randomEuint16();
euint32 randomValue = FHE.randomEuint32();
euint64 randomValue = FHE.randomEuint64();
euint128 randomValue = FHE.randomEuint128();
euint256 randomValue = FHE.randomEuint256();
```

Note that the random values are returned encrypted.
This is a fundamental quality of randomness generation, because if the returned value
was plaintext, then it would be possible to simulate the execution and predict the random value.

### Best practice: Ensure caller is not a Contract
When acting upon the resulting random numbers, it is important to keep the following
scenario in mind.

Suppose we have a simple game contract that you can send funds to, and with a probability of `P=0.5` (or, 50% chance),
you will receive double the funds back.

```solidity
contract RandomLucky {
function play() external payable {
require(msg.value > 0, "You need to send some FHE");
// Generate a random encrypted number
euint8 outcome = FHE.randomEuint8();
uint8 outcomeDecrypted = outcome.decrypt();
// If the outcome is even, send double the value back to the sender
if (outcomeDecrypted % 2 == 0) {
uint prize = msg.value * 2;
require(address(this).balance >= prize, "Contract does not have enough balance");
payable(msg.sender).transfer(prize);
}
// If the outcome is odd, the contract keeps the value
}
// Fallback function to receive FHE
receive() external payable {}
}
```
An adversary could could call the randomness consumer function, check the result of the random,
and revert the transaction if that result were not favorable.

In this case:
```solidity
contract Adversary {
RandomLucky game;
constructor(address gameAddress) {
game = RandomLucky(gameAddress);
}
// Function to attack the RandomLucky contract
function attack() public payable {
require(msg.value > 0, "Must send some FHE to attack");
// Store the initial balance of the contract
uint256 initialBalance = address(this).balance;
// Call the play function of the RandomLucky contract
game.play{value: msg.value}();
// Check if the balance did not increase
if (address(this).balance <= initialBalance) {
revert("Did not win, reverting transaction");
}
}
}
```

To prevent this kind of attacks, it is recommended to not allow contracts
to call functions that act upon random numbers, like so:
```solidity
modifier callerIsUser() {
require(tx.origin == msg.sender, "The caller is another contract");
_;
}
function play() callerIsUser {
...
}
```

:::danger[Warning]
### Randomness in View functions
Randomness is guaranteed to be unique for each transaction, but not for each `eth_call`.
Specifically, two eth_calls to the same contract, on the same block may receive the same random value (more on this below).

:::

:::info[How does it work?]

Random generation takes as input a seed, and returns a random number which is unique for each seed and key sets.

To cause each random to be different for each transaction, the seed is created from a) the contract address,
b) the transaction hash, and c) a counter that gets incremented each transaction.
```bash
seed = hash(contract_address, tx_hash, counter)
```
For eth calls, which don't have a tx_hash nor can use a counter, we use the block hash instead, that's why two quick subsequent
calls to the same contract may return the same random number.
```bash
seed = hash(contract_address, block_hash)
```
:::
52 changes: 26 additions & 26 deletions docs/devdocs/Writing Smart Contracts/Types-and-Operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,32 +104,32 @@ Please refer to the table below for a comprehensive list of supported operations

Note that all functions are supported in both direct function calls and library bindings. However, operator overloading is only supported for the operations listed in the table (solidity please support operator overloading for boolean return types!).


| Name | FHE.sol function | Operator | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 | ebool | eaddress |
|-----------------------|-------------------|:---------:|:--------:|:--------:|:--------:|:---------:|:-----------:|:-------------:|:--------:|:-----------:|
| Addition | `add` | `+` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Subtraction | `sub` | `-` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Multiplication | `mul` | `*` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Bitwise And | `and` | `&` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Bitwise Or | `or` | `\|` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Bitwise Xor | `xor` | `^` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Division | `div` | `/` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Remainder | `rem` | `%` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Shift Right | `shr` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Shift Left | `shl` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Equal | `eq` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Not equal | `ne` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Greater than or equal | `gte` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Greater than | `gt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Less than or equal | `lte` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Less than | `lt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Min | `min` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Max | `max` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Not | `not` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Select | `select` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Require | `req` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Decrypt | `decrypt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Seal Output | `sealOutput` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Name | FHE.sol function | Operator | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 | ebool | eaddress |
|-----------------------|------------------|:---------:|:--------:|:--------:|:--------:|:---------:|:-----------:|:-------------:|:--------:|:-----------:|
| Addition | `add` | `+` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Subtraction | `sub` | `-` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Multiplication | `mul` | `*` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Bitwise And | `and` | `&` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Bitwise Or | `or` | `\|` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Bitwise Xor | `xor` | `^` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Division | `div` | `/` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Remainder | `rem` | `%` | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> | <r>✘</r> | n/a | n/a |
| Shift Right | `shr` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Shift Left | `shl` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Equal | `eq` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Not equal | `ne` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Greater than or equal | `gte` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Greater than | `gt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Less than or equal | `lte` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Less than | `lt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Min | `min` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Max | `max` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | n/a | n/a |
| Not | `not` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <g>✔</g> | n/a |
| Select | `select` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Require | `req` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Decrypt | `decrypt` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Seal Output | `sealOutput` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> |
| Randomness | `randomEuintX` | n/a | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <g>✔</g> | <r>✘</r> | <r>✘</r> |

:::danger[Caveat]
At the moment it is not possible to do `ebool result = (lhs == rhs)` and others that return a boolean result. This is because FHE.sol expects a `ebool`, while Solidity only allows overloading to return a regular boolean.
Expand Down

0 comments on commit cdaa5c5

Please sign in to comment.