繁体   English   中英

尝试使用 Anchor 在 Devnet 上初始化 VRF 时我的事务失败(0x0 错误)

[英]My transaction fails when trying to Init VRF on Devnet using Anchor (0x0 Error)

我正在尝试为 Solana 使用 VRF 来生成随机数,我正在使用 CPI 来执行此操作。 在 Localhost 上运行测试时,它可以正常工作并生成数字。

但是,当我切换到 Devnet 时,它会因错误而失败:

交易模拟失败自定义错误 0x0

我正在使用 Orao-VRF: Github

我的 lib.rs 代码:

use anchor_lang::prelude::*;
use orao_solana_vrf::network_state_account_address;
use orao_solana_vrf::program::OraoVrf;
use orao_solana_vrf::randomness_account_address;
use orao_solana_vrf::state::NetworkState;
use orao_solana_vrf::CONFIG_ACCOUNT_SEED;
use orao_solana_vrf::RANDOMNESS_ACCOUNT_SEED;
use std::mem::size_of;

declare_id!("6ag7tVY7RizWm4xZr7Vv3N4yGio5mqS6H9VFAUFvuMQt");

#[program]
pub mod cflip {
    use orao_solana_vrf::cpi::accounts::Request;

    use super::*;

    pub fn spin_and_pull_the_trigger(
        ctx: Context<SpinAndPullTheTrigger>,
        force: [u8; 32],
    ) -> Result<()> {
        // Zero seed is illegal in VRF
        if force == [0_u8; 32] {
            return Err(Error::YouMustSpinTheCylinder.into());
        }

        // Request randomness.
        let cpi_program = ctx.accounts.vrf.to_account_info();
        let cpi_accounts = Request {
            payer: ctx.accounts.player.to_account_info(),
            network_state: ctx.accounts.config.to_account_info(),
            treasury: ctx.accounts.treasury.to_account_info(),
            request: ctx.accounts.random.to_account_info(),
            system_program: ctx.accounts.system_program.to_account_info(),
        };
        let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
        orao_solana_vrf::cpi::request(cpi_ctx, force)?;

        fn get_random(randomness: &[u8; 64]) -> u8 {
            // use only first 8 bytes for simplicyty
            let value = &randomness[0..size_of::<u64>()];
            return value[0];
        }

        Ok(())
    }
}

#[derive(Accounts)]
#[instruction(force: [u8; 32])]
pub struct SpinAndPullTheTrigger<'info> {
    #[account(mut)]
    player: Signer<'info>,
    /// CHECK:
    #[account(
        mut,
        seeds = [RANDOMNESS_ACCOUNT_SEED.as_ref(), &force],
        bump,
        seeds::program = orao_solana_vrf::ID
    )]
    random: AccountInfo<'info>,
    /// CHECK:
    #[account(mut)]
    treasury: AccountInfo<'info>,
    #[account(
        mut,
        seeds = [CONFIG_ACCOUNT_SEED.as_ref()],
        bump,
        seeds::program = orao_solana_vrf::ID
    )]
    config: Account<'info, NetworkState>,
    vrf: Program<'info, OraoVrf>,
    system_program: Program<'info, System>,
}

#[error_code]
pub enum Error {
    #[msg("The player is already dead")]
    PlayerDead,
    #[msg("Unable to serialize a randomness request")]
    RandomnessRequestSerializationError,
    #[msg("Player must spin the cylinder")]
    YouMustSpinTheCylinder,
    #[msg("The cylinder is still spinning")]
    TheCylinderIsStillSpinning,
}

/// Helper that builds the instruction.
#[cfg(feature = "sdk")]
pub fn spin_and_pull_the_trigger<'a>(
    cflip: &'a anchor_client::Program,
    vrf: &anchor_client::Program,
) -> std::result::Result<anchor_client::RequestBuilder<'a>, anchor_client::ClientError> {
    let seed = rand::random();

    // vrf accounts
    let network_state_address = network_state_account_address();
    let request_address = randomness_account_address(&seed);

    let vrf_config = vrf.account::<NetworkState>(network_state_address)?.config;

    Ok(cflip
        .request()
        .accounts(crate::accounts::SpinAndPullTheTrigger {
            player: cflip.payer(),
            treasury: vrf_config.treasury,
            random: request_address,
            config: network_state_address,
            vrf: orao_solana_vrf::id(),
            system_program: anchor_client::solana_sdk::system_program::ID,
        })
        .args(crate::instruction::SpinAndPullTheTrigger { force: seed }))
}

我的 test.ts 代码:

import assert from "assert";
import * as anchor from "@project-serum/anchor";
import { Program, BN, Spl } from "@project-serum/anchor";
import {
    Keypair,
    PublicKey,
    SystemProgram,
    LAMPORTS_PER_SOL,
    SYSVAR_RENT_PUBKEY,
    SYSVAR_INSTRUCTIONS_PUBKEY,
    Ed25519Program,
} from "@solana/web3.js";
import { Orao, networkStateAccountAddress, randomnessAccountAddress } from "../js/dist";
import { Cflip } from "../target/types/cflip";
import nacl from "tweetnacl";

describe("cflip", () => {
    const provider = anchor.AnchorProvider.env();
    anchor.setProvider(provider);

    const program = anchor.workspace.Cflip as Program<Cflip>;
    const vrf = new Orao(provider);

    // This accounts are for test VRF.
    const treasury = Keypair.generate();
    const fulfillmentAuthority = Keypair.generate();

    // Initial force for russian-roulette
    let force = Keypair.generate().publicKey;

    // This helper will play a single round of russian-roulette.
    async function spinAndPullTheTrigger(force: Buffer) {
        const random = randomnessAccountAddress(force);

        await program.methods
            .spinAndPullTheTrigger([...force])
            .accounts({
                player: provider.wallet.publicKey,
                vrf: vrf.programId,
                config: networkStateAccountAddress(),
                treasury: treasury.publicKey,
                random,
                systemProgram: SystemProgram.programId,
            })
            .rpc();
    }

    // This helper will fulfill randomness for our test VRF.
    async function emulateFulfill(seed: Buffer) {
        let signature = nacl.sign.detached(seed, fulfillmentAuthority.secretKey);
        await vrf.methods
            .fulfill()
            .accounts({
                instructionAcc: SYSVAR_INSTRUCTIONS_PUBKEY,
                networkState: networkStateAccountAddress(),
                request: randomnessAccountAddress(seed),
            })
            .preInstructions([
                Ed25519Program.createInstructionWithPublicKey({
                    publicKey: fulfillmentAuthority.publicKey.toBytes(),
                    message: seed,
                    signature,
                }),
            ])
            .rpc();
    }


    before(async () => {
        await provider.connection.confirmTransaction(
            await provider.connection.requestAirdrop(treasury.publicKey, 1 * LAMPORTS_PER_SOL),
            "confirmed"
          );

        // Initialize test VRF
        const fee = 1 * LAMPORTS_PER_SOL;
        const fullfillmentAuthorities = [
            fulfillmentAuthority.publicKey,
        ];
        
        const configAuthority = Keypair.generate();

        await vrf.methods
            .initNetwork(
                new BN(fee),
                configAuthority.publicKey,
                fullfillmentAuthorities,
                null
            )
            .accounts({
                networkState: networkStateAccountAddress(),
                treasury: treasury.publicKey,
            })
            .rpc();
    });

    it("spin and pull the trigger", async () => {
        await spinAndPullTheTrigger(force.toBuffer());

        await emulateFulfill(force.toBuffer());
        const randomness = await vrf.getRandomness(force.toBuffer());

        console.log(randomness.randomness);
    });
});

从我收集到的内容来看,这与测试文件中的 before 块有关:

    before(async () => {
        await provider.connection.confirmTransaction(
            await provider.connection.requestAirdrop(treasury.publicKey, 1 * LAMPORTS_PER_SOL),
            "confirmed"
          );

        // Initialize test VRF
        const fee = 1 * LAMPORTS_PER_SOL;
        const fullfillmentAuthorities = [
            fulfillmentAuthority.publicKey,
        ];
        
        const configAuthority = Keypair.generate();

        await vrf.methods
            .initNetwork(
                new BN(fee),
                configAuthority.publicKey,
                fullfillmentAuthorities,
                null
            )
            .accounts({
                networkState: networkStateAccountAddress(),
                treasury: treasury.publicKey,
            })
            .rpc();
    });

任何帮助将不胜感激。 谢谢你。

devnet 上的 VRF 程序已经用我们的配置初始化了,所以你不能初始化它。 emulateFulfill function 将抛出UnauthorizedFulfillmentAuthority异常,因为您的fulfillmentAuthority 密钥未列入白名单。 试试这样的,别忘了用自己的cflip程序ID改程序ID

import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import {
    Keypair,
    PublicKey,
    SystemProgram,
} from "@solana/web3.js";
import { Orao, networkStateAccountAddress, randomnessAccountAddress } from "@orao-network/solana-vrf";
import { Cflip, IDL } from "../target/types/cflip";

describe("cflip", () => {
    const provider = anchor.AnchorProvider.env();
    anchor.setProvider(provider);

    const program = new Program<Cflip>(IDL, new PublicKey("2XeJv53N1UzbupYNDH9PDakRWQFS4VX4bJDPm8P5T64J"), provider);
    const vrf = new Orao(provider);

    // Initial force for russian-roulette
    let force = Keypair.generate().publicKey;

    // This helper will play a single round of russian-roulette.
    async function spinAndPullTheTrigger(force: Buffer) {
        const random = randomnessAccountAddress(force);
        const networkState = await vrf.getNetworkState()
        const treasury = networkState.config.treasury

    await program.methods
        .spinAndPullTheTrigger([...force])
        .accounts({
            player: provider.wallet.publicKey,
            vrf: vrf.programId,
            config: networkStateAccountAddress(),
            treasury,
            random,
            systemProgram: SystemProgram.programId,
        })
        .rpc();
}

it("spin and pull the trigger", async () => {
    await spinAndPullTheTrigger(force.toBuffer());

    // Await fulfilled randomness (default commitment is "finalized"):
    const randomness = await vrf.waitFulfilled(force.toBuffer());
    console.log("Your randomness is " + randomness.fulfilled());
    });
});

你的 Anchor.toml 是什么? 另外我认为您使用的是 SDK 而不是 CPI,如果您发布更多代码会更有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM