7个版本 (3个稳定版本)
1.0.2 | 2024年2月8日 |
---|---|
1.0.1 | 2024年2月6日 |
1.0.0 | 2024年1月31日 |
0.0.4 | 2024年1月31日 |
#1604 在 神奇豆子
每月60次下载
7.5MB
3.5K SLoC
索拉纳随机服务
索拉纳随机服务使用Switchboard SGX启用预言机,通过回调指令为任何使用索拉纳程序的随机数提供随机数。
程序ID: RANDMo5gFnqnXJW5Z52KNmd24sAo95KAd5VbiCtq5Rh
注意: 此程序ID适用于主网测试版和开发网。
请求生命周期
- 用户的程序使用CPI调用和随机数字节数、自定义回调指令以及优先级费用配置调用
simple_randomness_v1
指令。- 创建
SimpleRandomnessV1Account
账户 - 设置自定义回调
- 将资金包裹成抵押金,以奖励预言机完成请求
- 创建
- 链下SGX启用预言机读取请求账户
- 在安全区内部生成随机字节
- 构建包含您的回调和所需优先级费用的交易
- 模拟交易。如果成功,则在链上中继交易。如果出错,则中继带有错误消息的错误指令,该错误消息可以在资源管理器中查看。
- 交易已在中继链上
- 预言机因完成请求而获得奖励
- 预言机调用用户的回调指令
- 请求账户关闭,并将租金豁免返还给原始付款人
使用方法
将 solana_randomness_service 添加到您的 Cargo.toml 中
solana-randomness-service = { version = "1", features = ["cpi"] }
请参阅以下示例程序,了解如何将索拉纳随机服务集成到您的 Anchor 程序中。
- 使用您的付款人、回调和所需的优先级费用配置调用
simple_randomness_v1
指令 - 构建随机服务将调用并使用您请求的随机字节的自定义回调指令
use solana_randomness_service::SimpleRandomnessV1Account;
use solana_randomness_service::TransactionOptions;
use solana_randomness_service::{
program::SolanaRandomnessService, ID as SolanaRandomnessServiceID,
};
use switchboard_solana::prelude::*;
use switchboard_solana::utils::get_ixn_discriminator;
declare_id!("39hMZgeiesFXMRFt8svuKVsdCW5geiYueSRx7dxhXN4f");
#[program]
pub mod solana_randomness_consumer {
use super::*;
pub fn request_randomness(ctx: Context<RequestRandomness>) -> anchor_lang::prelude::Result<()> {
msg!("Requesting randomness...");
// 1. Call the randomness service and request a new value
solana_randomness_service::cpi::simple_randomness_v1(
CpiContext::new(
ctx.accounts.randomness_service.to_account_info(),
solana_randomness_service::cpi::accounts::SimpleRandomnessV1Request {
request: ctx.accounts.randomness_request.to_account_info(),
escrow: ctx.accounts.randomness_escrow.to_account_info(),
state: ctx.accounts.randomness_state.to_account_info(),
mint: ctx.accounts.randomness_mint.to_account_info(),
payer: ctx.accounts.payer.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
associated_token_program: ctx
.accounts
.associated_token_program
.to_account_info(),
},
),
8, // Request 8 bytes of randomness
solana_randomness_service::Callback {
program_id: ID,
accounts: vec![
AccountMeta::new_readonly(ctx.accounts.randomness_state.key(), true).into(),
AccountMeta::new_readonly(ctx.accounts.randomness_request.key(), false).into(),
],
ix_data: get_ixn_discriminator("consume_randomness").to_vec(), // TODO: hardcode this discriminator [190,217,49,162,99,26,73,234]
},
Some(TransactionOptions {
compute_units: Some(1_000_000),
compute_unit_price: Some(100),
}),
)?;
// Here we can emit some event to index our requests
Ok(())
}
// 2. Build the instruction that will be called with the randomness bytes. The randomness bytes Vec will be appended to the end of the ixn data.
pub fn consume_randomness(
_ctx: Context<ConsumeRandomness>,
result: Vec<u8>,
) -> anchor_lang::prelude::Result<()> {
msg!("Randomness received: {:?}", result);
Ok(())
}
}
// The request_randomness macro breaks IDL generation. So we'll manually implement.
// #[request_randomness]
#[derive(Accounts)]
pub struct RequestRandomness<'info> {
/// The Solana Randomness Service program.
pub randomness_service: Program<'info, SolanaRandomnessService>,
/// The account that will be created on-chain to hold the randomness request.
/// Used by the off-chain oracle to pickup the request and fulfill it.
/// CHECK: todo
#[account(
mut,
signer,
owner = system_program.key(),
constraint = randomness_request.data_len() == 0 && randomness_request.lamports() == 0,
)]
pub randomness_request: AccountInfo<'info>,
/// The TokenAccount that will store the funds for the randomness request.
/// CHECK: todo
#[account(
mut,
owner = system_program.key(),
constraint = randomness_escrow.data_len() == 0 && randomness_escrow.lamports() == 0,
)]
pub randomness_escrow: AccountInfo<'info>,
/// The randomness service's state account. Responsible for storing the
/// reward escrow and the cost per random byte.
#[account(
seeds = [b"STATE"],
bump = randomness_state.bump,
seeds::program = randomness_service.key(),
)]
pub randomness_state: Box<Account<'info, solana_randomness_service::State>>,
/// The token mint to use for paying for randomness requests.
#[account(address = NativeMint::ID)]
pub randomness_mint: Account<'info, Mint>,
/// The account that will pay for the randomness request.
#[account(mut)]
pub payer: Signer<'info>,
/// The Solana System program. Used to allocate space on-chain for the randomness_request account.
pub system_program: Program<'info, System>,
/// The Solana Token program. Used to transfer funds to the randomness escrow.
pub token_program: Program<'info, Token>,
/// The Solana Associated Token program. Used to create the TokenAccount for the randomness escrow.
pub associated_token_program: Program<'info, AssociatedToken>,
}
#[derive(Accounts)]
pub struct ConsumeRandomness<'info> {
/// We need to make sure the randomness service signed this requests so it can only be invoked by a PDA and not a user.
#[account(
signer,
seeds = [b"STATE"],
seeds::program = SolanaRandomnessServiceID,
bump = randomness_state.bump,
)]
pub randomness_state: Box<Account<'info, solana_randomness_service::State>>,
pub request: Box<Account<'info, SimpleRandomnessV1Account>>,
}
Typescript 客户端
可以使用 TypeScript 客户端在链下与随机服务交互。
npm i @switchboard-xyz/solana-randomness-service
随机服务客户端可以从 anchor 提供器初始化。
Switchboard Labs提供了一套链下或acles,以满足在devnet和mainnet上的任何请求。
对于localnet,RandomnessService将初始化自身并创建自己的Switchboard基础设施。当您等待一个请求被解决时,随机服务将自行检查并满足该请求。
import * as anchor from "@coral-xyz/anchor";
import { RandomnessService } from "@switchboard-xyz/solana-randomness-service";
const provider = anchor.AnchorProvider.env();
const randomnessService = await RandomnessService.fromProvider(provider);
// Create a keypair for our request account. This account will be automatically closed on settlement and
// the rent will be returned to the original payer.
const requestKeypair = anchor.web3.Keypair.generate();
// Start watching for the settled event before triggering the request.
// If on localnet this will fulfill the randomness request for you in the background.
const settledRandomnessEventPromise = randomnessService.awaitSettledEvent(
requestKeypair.publicKey
);
// your program makes a CPI request to the RandomnessService
const signature = await program.methods
.requestRandomness()
.accounts({
randomnessService: randomnessService.programId,
randomnessRequest: requestKeypair.publicKey,
randomnessEscrow: anchor.utils.token.associatedAddress({
mint: randomnessService.accounts.mint,
owner: requestKeypair.publicKey,
}),
randomnessState: randomnessService.accounts.state,
randomnessMint: randomnessService.accounts.mint,
payer: provider.wallet.publicKey,
})
.signers([requestKeypair])
.rpc();
// Await the response from the Switchboard Service
const [settledRandomnessEvent, settledSlot] =
await settledRandomnessEventPromise;
依赖项
~18–27MB
~449K SLoC