You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测Solana程序是否通过CPI方式被调用?

Solana程序检测当前调用是否为CPI发起的实现方法

核心逻辑:Solana运行时会维护交易的调用栈,用户直接发起的顶层调用栈高度为1,跨程序调用(CPI)的栈高度大于1,官方SDK已经封装了现成的判断接口,不需要自己解析sysvar或者栈数据。

原生Solana SDK实现

直接调用SDK内置的solana_program::program::is_cpi()函数即可,返回值为true时代表当前是CPI调用,返回false代表是用户直接发起的顶层调用。
如果需要进一步校验发起CPI的程序身份,可以在确认是CPI调用后,调用solana_program::program::get_caller()获取调用方的程序ID,和白名单做比对即可。

示例代码:

use solana_program::{
    account_info::AccountInfo,
    entrypoint,
    entrypoint::ProgramResult,
    program,
    pubkey::Pubkey,
    msg,
};

entrypoint!(process_instruction);
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    instruction_data: &[u8],
) -> ProgramResult {
    let is_cpi = program::is_cpi();

    if is_cpi {
        msg!("当前调用为CPI发起");
        // 可选:校验调用方程序身份
        let caller_program_id = program::get_caller()?;
        // 此处添加白名单校验逻辑,比如判断caller_program_id是否属于允许调用的程序
    } else {
        msg!("当前调用为用户直接发起的顶层调用");
    }

    Ok(())
}

Anchor框架实现

Anchor已经对原生判断逻辑做了封装,直接在指令上下文Context上调用is_cpi()方法即可得到结果,不需要额外引入原生SDK的调用。
如果需要获取CPI调用方的程序ID,同样可以调用原生SDK的get_caller()方法。

示例代码:

use anchor_lang::prelude::*;

declare_id!("你的程序公钥");

#[program]
pub mod demo_program {
    use super::*;

    pub fn test_instruction(ctx: Context<TestCtx>) -> Result<()> {
        if ctx.is_cpi() {
            msg!("当前为CPI调用");
            // 可选:获取调用方程序ID做权限校验
            // let caller = solana_program::program::get_caller()?;
        } else {
            msg!("当前为用户直接调用");
        }
        Ok(())
    }
}

#[derive(Accounts)]
pub struct TestCtx {}

注意事项

  • 不要通过「是否有签名者」判断调用类型:CPI调用可以通过PDA签名、传入签名者账户等方式获得签名权限,顶层用户调用也可能构造无有效签名的非法指令,这个判断逻辑不可靠
  • 不要自行硬编码栈高数值做判断:后续Solana运行时可能调整栈初始高度,直接使用官方提供的is_cpi()接口可以保证跨版本兼容
  • 涉及权限控制的场景,不要只判断是否为CPI就放通逻辑,必须校验调用方程序ID是否在白名单内,防止恶意程序构造CPI调用越权操作

内容的提问来源于stack exchange,提问作者noooooooob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 23:06:28