Solana原生代码中CPI调用时,被调用程序如何获取并验证调用方Program ID?
在Solana中CPI调用时验证调用方程序ID的方案
核心逻辑
Solana的Runtime会自动维护CPI调用的调用栈上下文,调用方的程序ID会被Runtime自动记录,不需要手动传递。ProgramB可以直接通过Solana提供的API读取这个上下文里的调用方ID,完全不存在被篡改的可能,这和Solidity中msg.sender的可信性一致。
具体实现(以Rust为例)
在ProgramB的指令处理函数中,通过Solana SDK的invoke_context()API获取直接调用方的程序ID,然后和预设的ProgramA ID做对比即可:
use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, invoke_context, }; // 预先定义ProgramA的程序ID const EXPECTED_CALLER: Pubkey = Pubkey::new_from_array([/* 替换为ProgramA的公钥字节数组 */]); pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], _instruction_data: &[u8], ) -> ProgramResult { // 从Runtime上下文获取直接调用方的程序ID let caller_id = invoke_context::get_invoke_context()?.caller_program_id(); // 验证调用方是否为ProgramA if caller_id != &EXPECTED_CALLER { return Err(ProgramError::InvalidCaller); } // 执行后续业务逻辑 Ok(()) }
关于"手动传递账户"的问题
你提到的手动传递调用方账户的方案不可靠,因为发起CPI的ProgramA可以随意传入任意账户公钥,无法确保这个账户就是真实的调用程序。而通过Runtime调用栈获取的ID是由Solana节点维护的,任何用户程序都无法修改这个上下文信息,完全可信。
补充说明
如果是多层嵌套CPI(比如ProgramA → ProgramB → ProgramC),caller_program_id()获取的是直接上层的调用方(ProgramC拿到的是ProgramB的ID)。如果需要追溯最顶层的发起程序,可以通过invoke_context().call_stack()遍历整个调用栈,但绝大多数场景下验证直接调用方就足够满足需求。
内容的提问来源于stack exchange,提问作者Neo
相关产品推荐
相关产品推荐

