我刚参加了Solana on-chain的编程项目。 我计划开发一个硬币游戏,可以判断正面或反面。 我尝试使用std:: rand和get_random crate但它们都无法正常工作。 如果你有相关经验,请告诉我。
我在Solana on-chain项目中使用anchor。
我刚参加了Solana on-chain的编程项目。 我计划开发一个硬币游戏,可以判断正面或反面。 我尝试使用std:: rand和get_random crate但它们都无法正常工作。 如果你有相关经验,请告诉我。
我在Solana on-chain项目中使用anchor。
遗憾的是,随机生成器不能在链上工作。 如果你需要一些随机数,你应该从链外获取。
为什么?
假设您使用块哈希或类似的东西生成随机数,那么用户可以通过插入检查有利结果的指令或值来利用它,甚至更糟的是,如果不满意,强制回滚。
那我们该怎么办?
尝试使用像chainlink vrf(可验证随机函数)这样的预言机。
模拟预言机vrf服务:
在链上进行一个交易,让您的服务器监听它。如果发生此交易,请在链外生成随机数并从您的服务器回调它。
Anchor就是这样使用随机数的。
use rand::rngs::OsRng;
.
.
.
let dummy_a = Keypair::generate(&mut OsRng);
因此,如果您需要UUID类的行为所需的随机性,则可以使用类似上面的锚定代码库中的机制,但如果您的情况是游戏逻辑(例如掷骰子),则需要使用预言机或者模拟。
2022年11月10日更新
我们知道,Metaplex糖果机工具使用随机数来选择下一个要铸造的物品。
请参见此代码片段:
// (2) selecting an item to mint
let recent_slothashes = &ctx.accounts.recent_slothashes;
let data = recent_slothashes.data.borrow();
let most_recent = array_ref![data, 12, 8];
let clock = Clock::get()?;
// seed for the random number is a combination of the slot_hash - timestamp
let seed = u64::from_le_bytes(*most_recent).saturating_sub(clock.unix_timestamp as u64);
let remainder: usize = seed
.checked_rem(candy_machine.data.items_available - candy_machine.items_redeemed)
.ok_or(CandyError::NumericalOverflowError)? as usize;
let config_line = get_config_line(candy_machine, remainder, candy_machine.items_redeemed)?;
candy_machine.items_redeemed = candy_machine
.items_redeemed
.checked_add(1)
.ok_or(CandyError::NumericalOverflowError)?;
这个想法是你可以从Solana网络中获取区块哈希和时钟作为随机输入种子,以创建下一个随机数。
另一个代码片段将是创建随机数的好起点:
//convert blockhash to random seed string
let recent_blockhash_data = recent_blockhashes.data.borrow();
let most_recent = array_ref![recent_blockhash_data, 0, 16];
let some_numbers = u128::from_le_bytes(*most_recent);
let blockhash_random_seed: String = (some_numbers).to_string();
在Solana上,Chainlink VRF(可验证随机函数)尚不可用。您可以使用https://docs.switchboard.xyz/solana/dev/rust。
use anchor_lang::prelude::*;
use anchor_lang::solana_program::clock;
use std::convert::TryInto;
pub use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID};
declare_id!("FnsPs665aBSwJRu2A8wGv6ZT76ipR41kHm4hoA3B1QGh");
#[derive(Accounts)]
#[instruction(params: ReadResultParams)]
pub struct ReadResult<'info> {
pub aggregator: AccountLoader<'info, AggregatorAccountData>,
}
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct ReadResultParams {
pub max_confidence_interval: Option<f64>,
}
#[program]
pub mod anchor_feed_parser {
use super::*;
pub fn read_result(
ctx: Context<ReadResult>,
params: ReadResultParams,
) -> anchor_lang::Result<()> {
let feed = &ctx.accounts.aggregator.load()?;
// get result
let val: f64 = feed.get_result()?.try_into()?;
// check whether the feed has been updated in the last 300 seconds
feed.check_staleness(clock::Clock::get().unwrap().unix_timestamp, 300)
.map_err(|_| error!(FeedErrorCode::StaleFeed))?;
// check feed does not exceed max_confidence_interval
if let Some(max_confidence_interval) = params.max_confidence_interval {
feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval))
.map_err(|_| error!(FeedErrorCode::ConfidenceIntervalExceeded))?;
}
msg!("Current feed result is {}!", val);
Ok(())
}
}
#[error_code]
#[derive(Eq, PartialEq)]
pub enum FeedErrorCode {
#[msg("Not a valid Switchboard account")]
InvalidSwitchboardAccount,
#[msg("Switchboard feed has not been updated in 5 minutes")]
StaleFeed,
#[msg("Switchboard feed exceeded provided confidence interval")]
ConfidenceIntervalExceeded,
}