sbv2-solana/website/docs/developers/randomness.mdx

329 lines
9.4 KiB
Plaintext

---
sidebar_position: 40
title: Randomness
---
import { Box, Typography, Grid } from "@mui/material";
import Link from "@docusaurus/Link";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
<!--
<Typography variant="h4">
<Link to="https://github.com/switchboard-xyz/switchboard-v2/tree/main/programs/anchor-vrf-parser">
# anchor-vrf-parser
</Link>
</Typography>
-->
## Reading a VRF Account
<Tabs>
<TabItem value="Rust" label="Rust" default>
```rust
use switchboard_v2::VrfAccountData;
let vrf = VrfAccountData::new(vrf_account_info)?;
let result_buffer = vrf.get_result()?;
if result_buffer == [0u8; 32] {
msg!("vrf buffer empty");
return Ok(());
}
let value: &[u128] = bytemuck::cast_slice(&result_buffer[..]);
let result = value[0] % 256000 as u128;
```
</TabItem>
<TabItem value="Typescript" label="Typescript">
```ts
import * as anchor from "@project-serum/anchor";
import { Keypair } from "@solana/web3.js";
import {
loadSwitchboardProgram,
VrfAccount,
} from "@switchboard-xyz/switchboard-v2";
let payer: Keypair;
const program = await loadSwitchboardProgram("devnet", undefined, payer);
const vrfAccount = new VrfAccount({
program,
publicKey: vrfKey,
});
const vrf = await vrfAccount.loadData();
console.log(vrf.currentRound.result);
```
</TabItem>
<TabItem value="CLI" label="CLI">
```bash
sbv2 vrf print VRFKEY
# OR
sbv2 vrf watch VRFKEY
```
</TabItem>
</Tabs>
## Creating a VRF Account
- `vrfInit`
<Tabs>
<TabItem value="Typescript" label="Typescript" default>
```ts
import * as anchor from "@project-serum/anchor";
import { Keypair } from "@solana/web3.js";
import {
loadSwitchboardProgram,
OracleQueueAccount,
PermissionAccount,
SwitchboardPermission,
VrfAccount,
} from "@switchboard-xyz/switchboard-v2";
let payer: Keypair;
const program = await loadSwitchboardProgram("devnet", undefined, payer);
const queueAccount = new queueAccount({ program, publicKey: queueKey });
const queue = await queueAccount.loadData();
// load client program used for callback
const vrfClientProgram = anchor.workspace
.AnchorVrfParser as anchor.Program<AnchorVrfParser>;
const vrfSecret = anchor.web3.Keypair.generate();
const vrfIxCoder = new anchor.BorshInstructionCoder(vrfClientProgram.idl);
const vrfClientCallback: Callback = {
programId: vrfClientProgram.programId,
accounts: [
// ensure all accounts in updateResult are populated
{ pubkey: vrfClientKey, isSigner: false, isWritable: true },
{ pubkey: vrfSecret.publicKey, isSigner: false, isWritable: false },
],
ixData: vrfIxCoder.encode("updateResult", ""), // pass any params for instruction here
};
// create VRF
const vrfAccount = await VrfAccount.create(program, {
queue: queueAccount,
callback: vrfClientCallback,
authority: vrfClientKey, // vrf authority
keypair: vrfSecret,
});
// create permission
const permissionAccount = await PermissionAccount.create(program, {
authority: queue.authority,
granter: queue.publicKey,
grantee: vrfAccount.publicKey,
});
// if queue has not enabled unpermissionedVrfEnabled, queue will need to grant permission
let queueAuthority: Keypair;
await permissionAccount.set({
authority: queueAuthority,
permission: SwitchboardPermission.PERMIT_VRF_REQUESTS,
enable: true,
});
```
</TabItem>
<TabItem value="CLI" label="CLI">
```bash
sbv2 vrf create QUEUEKEY \
--keypair "path/to/payer/keypair.json" \
--authority "path/to/authority/keypair.json" \
--queueAuthority "path/to/queue-authority/keypair.json" \
--vrfKeypair vrf-keypair.json \
--callbackPid CALLBACKPROGRAMID \
--ixData "[145,72,9,94,61,97,126,106]" \
-a "{\"pubkey\": \"C7PVykaTT8xDt8D1bugNvAJP1mJnfK5h12PuqgghmCDZ\",\"isSigner\": false,\"isWritable\": true}" \
-a "{\"pubkey\": \"BV7TXpXtnzYCFjoQCeGx8iMgfi9WB38tmirrDkun8P38\",\"isSigner\": false,\"isWritable\": false}" \
--enable \
--verbose
```
</TabItem>
</Tabs>
## Request Randomness
- `vrfRequestRandomness`
<Tabs>
<TabItem value="Rust" label="Rust" default>
```rust
use crate::*;
use anchor_lang::prelude::*;
pub use switchboard_v2::{VrfAccountData, VrfRequestRandomness};
use anchor_spl::token::Token;
use anchor_lang::solana_program::clock;
#[derive(Accounts)]
#[instruction(params: RequestResultParams)] // rpc parameters hint
pub struct RequestResult<'info> {
#[account(
mut,
seeds = [
STATE_SEED,
vrf.key().as_ref(),
authority.key().as_ref(),
],
bump = state.load()?.bump,
has_one = vrf,
has_one = authority
)]
pub state: AccountLoader<'info, VrfClient>,
#[account(signer)]
pub authority: AccountInfo<'info>,
#[account(constraint = switchboard_program.executable == true)]
pub switchboard_program: AccountInfo<'info>,
#[account(mut, constraint = vrf.owner.as_ref() == switchboard_program.key().as_ref())]
pub vrf: AccountInfo<'info>,
#[account(mut, constraint = oracle_queue.owner.as_ref() == switchboard_program.key().as_ref())]
pub oracle_queue: AccountInfo<'info>,
pub queue_authority: UncheckedAccount<'info>,
#[account(constraint = data_buffer.owner.as_ref() == switchboard_program.key().as_ref())]
pub data_buffer: AccountInfo<'info>,
#[account(mut, constraint = permission.owner.as_ref() == switchboard_program.key().as_ref())]
pub permission: AccountInfo<'info>,
#[account(mut, constraint = escrow.owner == program_state.key())]
pub escrow: Account<'info, TokenAccount>,
#[account(mut, constraint = payer_wallet.owner == payer_authority.key())]
pub payer_wallet: Account<'info, TokenAccount>,
#[account(signer)]
pub payer_authority: AccountInfo<'info>,
#[account(address = solana_program::sysvar::recent_blockhashes::ID)]
pub recent_blockhashes: AccountInfo<'info>,
#[account(constraint = program_state.owner.as_ref() == switchboard_program.key().as_ref())]
pub program_state: AccountInfo<'info>,
#[account(address = anchor_spl::token::ID)]
pub token_program: Program<'info, Token>,
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct RequestResultParams {
pub permission_bump: u8,
pub switchboard_state_bump: u8,
}
impl RequestResult<'_> {
pub fn validate(&self, _ctx: &Context<Self>, _params: &RequestResultParams) -> Result<()> {
Ok(())
}
pub fn actuate(ctx: &Context<Self>, params: &RequestResultParams) -> Result<()> {
let client_state = ctx.accounts.state.load()?;
let bump = client_state.bump.clone();
let max_result = client_state.max_result.clone();
drop(client_state);
let switchboard_program = ctx.accounts.switchboard_program.to_account_info();
let vrf_request_randomness = VrfRequestRandomness {
authority: ctx.accounts.state.to_account_info(),
vrf: ctx.accounts.vrf.to_account_info(),
oracle_queue: ctx.accounts.oracle_queue.to_account_info(),
queue_authority: ctx.accounts.queue_authority.to_account_info(),
data_buffer: ctx.accounts.data_buffer.to_account_info(),
permission: ctx.accounts.permission.to_account_info(),
escrow: ctx.accounts.escrow.clone(),
payer_wallet: ctx.accounts.payer_wallet.clone(),
payer_authority: ctx.accounts.payer_authority.to_account_info(),
recent_blockhashes: ctx.accounts.recent_blockhashes.to_account_info(),
program_state: ctx.accounts.program_state.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
};
let vrf_key = ctx.accounts.vrf.key.clone();
let authority_key = ctx.accounts.authority.key.clone();
msg!("bump: {}", bump);
msg!("authority: {}", authority_key);
msg!("vrf: {}", vrf_key);
let state_seeds: &[&[&[u8]]] = &[&[
&STATE_SEED,
vrf_key.as_ref(),
authority_key.as_ref(),
&[bump],
]];
msg!("requesting randomness");
vrf_request_randomness.invoke_signed(
switchboard_program,
params.switchboard_state_bump,
params.permission_bump,
state_seeds,
)?;
emit!(RequestingRandomness{
vrf_client: ctx.accounts.state.key(),
max_result: max_result,
timestamp: clock::Clock::get().unwrap().unix_timestamp
});
msg!("randomness requested successfully");
Ok(())
}
}
```
</TabItem>
<TabItem value="Typescript" label="Typescript">
```ts
import * as anchor from "@project-serum/anchor";
import { Keypair } from "@solana/web3.js";
import {
loadSwitchboardProgram,
VrfAccount,
} from "@switchboard-xyz/switchboard-v2";
let payer: Keypair;
let authority: Keypair;
const program = await loadSwitchboardProgram("devnet", undefined, payer);
const vrfAccount = new VrfAccount({
program,
publicKey: vrfKey,
});
const vrf = await vrfAccount.loadData();
const queueAccount = new OracleQueueAccount({
program,
publicKey: vrf.queuePubkey,
});
const queue = await queueAccount.loadData();
const mint = await queueAccount.loadMint();
const payerTokenWallet = = (
await mint.getOrCreateAssociatedAccountInfo(payer.publicKey)
).address;
const signature = await vrfAccount.requestRandomness({
authority,
payer: payerTokenWallet,
payerAuthority: payer,
});
```
</TabItem>
<TabItem value="CLI" label="CLI">
```bash
sbv2 vrf request VRFKEY \
--keypair "path/to/payer/keypair.json"
```
</TabItem>
</Tabs>
```