19

I'm developing a game on-chain that needs to generate random numbers inside my program. How can I make it once random numbers are not available in Solana? Are there alternative ways to do it on-chain without using the rand crate?

If that is not possible, what should be the best approach to get it off-chain?

6 Answers 6

14

Chainlink Verifiable Random Function (VRF) is not currently available on Solana (it's only available on EVM chains currently), but Solana does have another oracle service with VRF called Switchboard.

VRF is great because it's close to true random and not manipulatable by validators and/or transaction authors since VRF isn't a function of the block height or blockchain time, however it can be a bit expensive to order many random numbers from Switchboard (at least in the current implementation) in cases such as generating 10k*6 random numbers for the attributes in an NFT collection, so another option would be to generate the random number procedurally with a seed and a function like Xorshift or hash function such as murmur3 or SHA-256. This article goes into more depth into implementing those algorithms in a Solana onchain program.

Another option Solrand may be a good compromise between Switchboard and rolling your own RNG because Solrand is cheap since it uses the blockhash rather than an oracle network, and it also is difficult for validators to manipulate because a Verifiable Delay Function is used such that it would take validators too long to both manipulate Solrand's generated number and still have enough time to complete the block validation. The main issue here is that end-users could theoretically predict what the random number would be based on the last confirmed block hash then only submit a tx when the random number results in an outcome which is in their favor.

If your business model can handle the associated costs, then I would recommend Switchboard. They are actively working on means to lower costs.

1
  • 2
    Indeed, today the cost of using Switchboard VRF has been lowered to 0.002 SOL Commented Jul 13, 2022 at 21:04
9

Switchboard has a pretty good example for VRF here: https://github.com/switchboard-xyz/switchboard-v2/tree/main/programs/anchor-vrf-parser

You can read more about it here: https://docs.switchboard.xyz/randomness

1
  • Note, any 3rd party VRF services are not free, and normally they are not cheap at all per request, so can be not suitable for many free to play games.
    – rxdx
    Commented Jul 13, 2022 at 11:14
7

Since random number generation by definition isn't reproducible, it isn't really compatible with a decentralized validator model. You could try using Chainlink VRF or some other trusted oracle. Or if you're willing to accept the security downsides you could create deterministic random numbers by hashing various data from the transaction, such as the slot, calling public key, etc. But if you do the latter it will always be somewhat gameable.

1
  • 3
    Few are capable of identifying, let alone evaluating, the security downsides of deterministic on-chain "randomness". Oracles are definitely The Way (TM)
    – trent.sol
    Commented Jul 13, 2022 at 7:06
4

ORAO network did a demo last Solana hacker house of their VRF solution. This repo contains an example.

https://github.com/orao-network/solana-vrf

1
  • 3
    I don't believe the project has been audited yet, so make sure you understand how it works before committing to using it
    – trent.sol
    Commented Jul 16, 2022 at 4:39
2

Depending on your game and the number of players, you could use a commit and reveal set-up. Players commit a hash and after the reveal, all the secrets could be smashed together to generate the seed for your randomness. Obviously, this isn't going to work for every type of application as it requires two instead of one transaction and at least two players.

-1

Here is the full code on how to generate a random number. From here

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,
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.