Skip to content

Commit

Permalink
ran grammarly , edited language to be clearer
Browse files Browse the repository at this point in the history
  • Loading branch information
adpthegreat committed Oct 23, 2024
1 parent 5149f9b commit 42555e8
Showing 1 changed file with 51 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ description: "Use proper cryptographic randomness in your onchain programs."

- Attempts at generating randomness within your program are likely to be
guessable by users given there's no true randomness onchain.
- Verifiable Random Functions (VRFs) give developers the opportunity to
- Verifiable Random Functions (VRFs) allow developers to
incorporate securely generated random numbers in their onchain programs.
- A VRF is a public-key pseudorandom function that provides proofs that its
- A VRF is a public-key pseudorandom function that proves its
outputs were calculated correctly.
- Switchboard offers a developer-friendly VRF for the Solana ecosystem.

Expand All @@ -24,7 +24,7 @@ description: "Use proper cryptographic randomness in your onchain programs."

Random numbers are **_not_** natively allowed onchain. This is because Solana is
deterministic, every validator runs your code and needs to have the same result.
So if you wanted to create a raffle program, you'd have to look outside of the
So if you wanted to create a raffle program, you'd have to look outside the
blockchain for your randomness. This is where Verifiable Random Functions (VRFs)
come in. VRFs offer developers a secure means of integrating randomness onchain
in a decentralized fashion.
Expand All @@ -33,7 +33,7 @@ in a decentralized fashion.

Before we dive into how random numbers can be generated for a blockchain, we
must first understand how they are generated on traditional computer systems.
There are really two types of random numbers: _true random_ and _pseudorandom_.
There are two types of random numbers: _true random_ and _pseudorandom_.
The difference between the two lies in how the numbers are generated.

Computers can acquire _true random_ numbers by taking some type of physical
Expand All @@ -51,7 +51,7 @@ in the sequence. Given the same seed, a PRNG will always produce the same
sequence of numbers. It's important to seed with something close to true
entropy: an admin-provided "random" input, the last system log, some combination
of your system's clock time and other factors, etc.. Fun fact: some older video
games have been broken because speedrunners found out how their randomness was
games have been broken because speed runners found out how their randomness was
calculated. One game in particular used the number of steps you've taken in the
game as a seed.

Expand All @@ -65,21 +65,21 @@ more. So we'll have to look outside of the blockchain for randomness with VRFs.
### What is Verifiable Randomness?

A Verifiable Random Function (VRF) is a public-key pseudorandom function that
provides proofs that its outputs were calculated correctly. This means we can
provides proof that its outputs were calculated correctly. This means we can
use a cryptographic keypair to generate a random number with a proof, which can
then be validated by anyone to ensure the value was calculated correctly without
the possibility of leaking the producer's secret key. Once validated, the random
value is stored onchain in an account.

VRFs are a crucial component for achieving verifiable and unpredictable
randomness on a blockchain, addressing some of the shortcomings of traditional
PRNGs and the challenges with achieving true randomness in a decentralized
PRNGs and the challenges of achieving true randomness in a decentralized
system.

There are three key properties of a VRF:

1. **Deterministic** - A VRF takes a secret key and a nonce as inputs and
deterministically produces an output ( seeding ). The result is a seemingly
deterministically produces an output (seeding). The result is a seemingly
random value. Given the same secret key and nonce, the VRF will always
produce the same output. This property ensures that the random value can be
reproduced and verified by anyone.
Expand All @@ -91,7 +91,7 @@ There are three key properties of a VRF:
generated by a VRF using the corresponding secret key and nonce.

VRFs are not specific to Solana and have been utilized on other blockchains to
generate pseudorandom numbers. Fortunately switchboard offers their
generate pseudorandom numbers. Fortunately, switchboard offers their
implementation of VRF to Solana.

### Switchboard VRF Implementation
Expand Down Expand Up @@ -135,15 +135,15 @@ and consuming randomness from Switchboard looks like this:
6. Once VRF proof is verified, the Switchboard program will invoke the
`callback` that was passed in as the callback in the initial request with the
pseudorandom number returned from the Oracle.
7. Program consumes the random number and can execute business logic with it!
7. The program consumes the random number and can execute business logic with it!

There are a lot of steps here, but don't worry, we'll be going through each step
of the process in detail.

First there are a couple of accounts that we will have to create ourselves to
First, there are a couple of accounts that we will have to create to
request randomness, specifically the `authority` and `vrf` accounts. The
`authority` account is a PDA derived from our program that is requesting the
randomness. So the PDA we create will have our own seeds for our own needs. For
`authority` account is a PDA derived from our program that is requesting
randomness. So the PDA we create will have our seeds for our needs. For
now, we'll simply set them at `VRFAUTH`.

```typescript
Expand All @@ -167,9 +167,9 @@ pub struct VrfAccountData {
pub counter: u128,
/// Onchain account delegated for making account changes. <-- This is our PDA
pub authority: Pubkey,
/// The OracleQueueAccountData that is assigned to fulfill VRF update request.
/// The OracleQueueAccountData that is assigned to fulfill the VRF update request.
pub oracle_queue: Pubkey,
/// The token account used to hold funds for VRF update request.
/// The token account used to hold funds for VRF update requests.
pub escrow: Pubkey,
/// The callback that is invoked when an update request is successfully verified.
pub callback: CallbackZC,
Expand All @@ -180,23 +180,23 @@ pub struct VrfAccountData {
/// The number of builders.
pub builders_len: u32,
pub test_mode: bool,
/// Oracle results from the current round of update request that has not been accepted as valid yet
/// Oracle results from the current round of update requests that have not been accepted as valid yet
pub current_round: VrfRound,
/// Reserved for future info.
pub _ebuf: [u8; 1024],
}
```

Some important fields on this account are `authority`, `oracle_queue`, and
`callback`. The `authority` should be a PDA of the program that has the ability
to request randomness on this `vrf` account. That way, only that program can
`callback`. The `authority` should be a PDA of the program that can
request randomness on this `vrf` account. That way, only that program can
provide the signature needed for the vrf request. The `oracle_queue` field
allows you to specify which specific oracle queue you'd like to service the vrf
requests made with this account. If you aren't familiar with oracle queues on
Switchboard, checkout the
allows you to specify which specific oracle queue youd like to service the vrf
requests made with this account. If you arent familiar with oracle queues on
Switchboard, check the
[Oracles lesson in the Connecting to Offchain Data course](/content/courses/connecting-to-offchain-data/oracles)!
Lastly, the `callback` field is where you define the callback instruction the
Switchboard program should invoke once the randomness result has be verified.
Switchboard program should be invoked once the randomness result has been verified.

The `callback` field is of type
`[CallbackZC](https://github.com/switchboard-xyz/solana-sdk/blob/9dc3df8a5abe261e23d46d14f9e80a7032bb346c/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs#L25)`.
Expand Down Expand Up @@ -234,7 +234,7 @@ const vrfCallback: Callback = {
{ pubkey: vrfClientKey, isSigner: false, isWritable: true },
{ pubkey: vrfSecret.publicKey, isSigner: false, isWritable: true },
],
// use name of instruction
// use the name of instruction
instructionData: vrfInstructionCoder.encode("consumeRandomness", ""), // pass any params for instruction here
}
```
Expand Down Expand Up @@ -306,7 +306,7 @@ context.
- Queue Authority - Authority of the Oracle Queue chosen
- [Data Buffer](https://github.com/switchboard-xyz/solana-sdk/blob/9dc3df8a5abe261e23d46d14f9e80a7032bb346c/rust/switchboard-solana/src/oracle_program/accounts/queue.rs#L57C165-L57C165) -
Account of the `OracleQueueBuffer` account holding a collection of Oracle
pubkeys that have successfully hearbeated before the queues `oracleTimeout`
pubkeys that have successfully heartbeat before the queues `oracleTimeout`
configuration has elapsed. Stored in the Oracle Queue account.
- [Permission Account Data](https://docs.rs/switchboard-solana/latest/switchboard_solana/oracle_program/accounts/permission/struct.PermissionAccountData.html)
- Escrow (Switchboard escrow account) - Token Account
Expand All @@ -324,7 +324,7 @@ what it looks like in a Solana program via CPI. To do this, we make use of the
`VrfRequestRandomness` data struct from the
[Switchboard-Solana rust crate.](https://github.com/switchboard-xyz/solana-sdk/blob/main/rust/switchboard-solana/src/oracle_program/instructions/vrf_request_randomness.rs)
This struct has some built-in capabilities to make our lives easier here, most
notably the account structure is defined for us and we can easily call `invoke`
notably, the account structure is defined for us and we can easily call `invoke`
or `invoke_signed` on the object.

```rust
Expand Down Expand Up @@ -373,7 +373,7 @@ it back to the Switchboard program.
Once the result is verified, the Switchboard program then invokes the `callback`
instruction defined in the `vrf` account. The callback instruction is where you
would have written your business logic using the random numbers. In the
following code we store the resulting randomness in our `vrf_auth` PDA from our
following code, we store the resulting randomness in our `vrf_auth` PDA from our
first step.

```rust
Expand Down Expand Up @@ -418,9 +418,9 @@ yet and that's how the randomness is returned. Switchboard, gives you your
randomness calling
[`get_result()`](https://github.com/switchboard-xyz/solana-sdk/blob/9dc3df8a5abe261e23d46d14f9e80a7032bb346c/rust/switchboard-solana/src/oracle_program/accounts/vrf.rs#L122).
This method returns the `current_round.result` field of the `vrf` account
SwitchboardDecimal format, which is really just a buffer of 32 random
[`u8`](https://github.com/switchboard-xyz/solana-sdk/blob/9dc3df8a5abe261e23d46d14f9e80a7032bb346c/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs#L65C26-L65C26)
unsigned-integers. You can use these unsigned-integers however you see fit in
SwitchboardDecimal format, which is just a buffer of 32 random
`[u8](https://github.com/switchboard-xyz/solana-sdk/blob/9dc3df8a5abe261e23d46d14f9e80a7032bb346c/rust/switchboard-solana/src/oracle_program/accounts/ecvrf.rs#L65C26-L65C26)`
unsigned integers. You can use these unsigned integers however you see fit in
your program, but a very common method is to treat each integer in the buffer as
its own random number. For example, if you need a dice roll (1-6) just take the
first byte of the array, module it with 6 and add one.
Expand Down Expand Up @@ -450,7 +450,7 @@ is provided for you in
[the main branch of the lab Github repository](https://github.com/solana-developers/burry-escrow).
[the main branch of the lab Github repository](https://github.com/solana-developers/burry-escrow).

The repo contains a "Michael Burry" escrow program. This is a program that
The repo contains a "Michael Burry" escrow program. This program
allows a user to lock up some SOL funds in escrow that cannot be withdrawn
until SOL has reached a predefined price in USD chosen by the user. We will be
adding VRF functionality to this program to allow the user to "Get out of jail"
Expand Down Expand Up @@ -485,7 +485,7 @@ in our `Cargo.toml` file.
[dependencies]
anchor-lang = "0.28.0"
anchor-spl = "0.28.0"
switchboard-solana = "0.28"
switchboard-solana = "0.28.0"
```

### 3. Lib.rs
Expand Down Expand Up @@ -574,7 +574,7 @@ hold the state of our dice rolls. It will have the following fields:
will create this before we call `VrfClient`'s initialization function.
- `escrow` - Public key of our burry escrow account.

We're also going to make the `VrfClient` context a `zero_copy` struct. This
We will also make the `VrfClient` context a `zero_copy` struct. This
means that we will initialize it with `load_init()` and pass it into accounts
with `AccountLoader`. We do this because VRF functions are very account
intensive and we need to be mindful of the stack. If you'd like to learn more
Expand All @@ -599,7 +599,7 @@ pub struct VrfClient {
}
```

Lastly we are going to add the `VRF_STATE_SEED` to PDA our VRF Client account.
Lastly, we are going to add the `VRF_STATE_SEED` to the PDA of our VRF Client account.

```rust
pub const VRF_STATE_SEED: &[u8] = b"VRFCLIENT";
Expand Down Expand Up @@ -627,7 +627,7 @@ pub struct Escrow {
pub struct VrfClient {
pub bump: u8,
pub result_buffer: [u8; 32],
pub dice_type: u8, // 6 sided
pub dice_type: u8, // 6 sided
pub die_result_1: u8,
pub die_result_2: u8,
pub timestamp: i64,
Expand Down Expand Up @@ -783,11 +783,11 @@ pub struct InitVrfClient<'info> {
Notice the `vrf_state` account is a PDA derived with the `VRF_STATE_SEED` string
and the `user`, `escrow_account`, and `vrf` public keys as seeds. This means a
single user can only initialize a single `vrf_state` account, just like they can
only have one `escrow_account`. Since there is only one, If you wanted to be
only have one `escrow_account`. Since there is only one, If you want to be
thorough, you might want to implement a `close_vrf_state` function to get your
rent back.

Now, let's write some basic initialization logic for this function. First we
Now, lets write some basic initialization logic for this function. First, we
load and initialize our `vrf_state` account by calling `load_init()`. Then we
fill in the values for each field.

Expand Down Expand Up @@ -822,10 +822,10 @@ VRF Accounts:

- `payer_wallet` - the token wallet that will pay for the VRF request; the
`user` must be the owner of this account.
- `vrf` - The VRF account that was created by the client.
- `oracle_queue` - The oracle queue that will field the randomness result.
- `vrf` - The VRF account created by the client.
- `oracle_queue` - The oracle queue that will feed the randomness result.
- `queue_authority` - The authority over the queue.
- `data_buffer` - The queue's data buffer account - used by the queue to
- `data_buffer` - The queue's data buffer account which is used by the queue to
compute/verify the randomness.
- `permission` - Created when creating the `vrf` account. It's derived from
several of the other accounts.
Expand Down Expand Up @@ -938,10 +938,10 @@ pub struct RequestRandomnessParams {
}
```

Now, we can work on the logic of this instruction. The logic should gather all
of the accounts needed and pass them to
Now, we can focus on implementing the logic of this instruction. The logic should collect all
the necessary accounts needed and pass them to
`[VrfRequestRandomness](https://github.com/switchboard-xyz/solana-sdk/blob/fbef37e4a78cbd8b8b6346fcb96af1e20204b861/rust/switchboard-solana/src/oracle_program/instructions/vrf_request_randomness.rs#L8)`,
which is a really nice struct from Switchboard. Then we'll sign the request and
which is a well designed struct from Switchboard. After that we'll sign the request and
send it on it's way.

```rust
Expand Down Expand Up @@ -1093,17 +1093,17 @@ pub fn consume_randomness_handler(ctx: Context<ConsumeRandomness>) -> Result <()
}
```

Now it's time to actually use the random result. Since we only use two dice we
Now its time to use the random result. Since we're working with two dice we
only need the first two bytes of the buffer. To convert these random values into
“dice rolls”, we use modular arithmetic. For anyone not familiar with modular
arithmetic,
[Wikipedia can help](https://en.wikipedia.org/wiki/Modular_arithmetic). In
modular arithmetic, numbers "wrap around" upon reaching a given fixed quantity.
This given quantity is known as the modulus to leave as the remainder. Here, the
[this Wikipedia article](https://en.wikipedia.org/wiki/Modular_arithmetic) provides a helpful introduction. In
modular arithmetic, numbers "wrap around" when they reach a given fixed given quantity.
This given quantity is known as the modulus to leave as the remainder. In our case, the
modulus is the `dice_type` stored on the `vrf_state` account. We hard-coded this
to 6 when the account was initialized to represent a 6-sided die. When we use
`dice_type`, or 6, as the modulus, our result will be a number 0-5. We then add
one, to make the resulting possibilities 1-6.
`dice_type`, or 6, as the modulus, our result will be a number between 0 and 5. We then add
one to shift tha range, making the possible outcomes 1-6.

```rust
pub fn consume_randomness_handler(ctx: Context<ConsumeRandomness>) -> Result <()> {
Expand Down Expand Up @@ -1242,7 +1242,7 @@ import {
import { NodeOracle } from "@switchboard-xyz/oracle";
import { assert } from "chai";

export const solUsedSwitchboardFeed = new anchor.web3.PublicKey(
export const solUsdSwitchboardFeed = new anchor.web3.PublicKey(
"GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR",
);

Expand Down Expand Up @@ -1440,7 +1440,7 @@ describe.only("burry-escrow-vrf", () => {
Now let's run the actual test. We'll structure the test to keep rolling dice
until we get doubles, then we'll check that we can withdraw the funds.
First, we'll gather all of the accounts we need. The `switchboard` test context
First, we'll gather all the accounts we need. The `switchboard` test context
gives us most of these. Then we'll need to call our `initVrfClient` function.
Finally, we'll roll our dice in a loop and check for doubles.
Expand Down Expand Up @@ -1557,7 +1557,7 @@ it("Roll till you can withdraw", async () => {
await provider.connection.confirmTransaction(transaction, "confirmed");
console.log(`Created VrfClient Account: ${vrfClientKey}`);

// wait a few sec for switchboard to generate the random number and invoke callback instruction
// wait a few seconds for switchboard to generate the random number and invoke the callback instruction
console.log("Rolling Die...");

let didUpdate = false;
Expand Down

0 comments on commit 42555e8

Please sign in to comment.