Cross Program Invocation(CPI)是指一个 program 在执行过程中调用另一个程序的 instruction。CPI 实现了可组合性:网络上的任何程序都可以调用其他程序的指令。
Cross-program invocation example
无 PDA 签名者的 CPI
使用 invoke 函数、Anchor CpiContext、原生 Rust 示例、SOL 转账 CPI 示例。
带 PDA 签名者的 CPI
使用带签名种子的 invoke_signed、Anchor CpiContext 与 PDA、原生 Rust PDA 签名示例。
CPI 执行与权限
11 步 CPI 执行流程、权限扩展规则、重入性、账户校验。
CPI 成本模型与数据同步
CPI 成本公式、序列化成本、账户数据同步、PDA 签名、返回数据。
关键要点
- 两个函数:
invoke(无 PDA 签名)和invoke_signed(带 PDA 签名)。 - 权限扩展:Account 权限(签名者、可写)会从调用方传递到被调用方。被调用方无法提升权限,只能使用调用方传递的权限。
- 共享计算预算:被调用方消耗的 CU 会减少调用方剩余的预算。
- 重入性:允许直接自递归(A->A->A),但不允许间接重入(A->B->A 返回
ReentrancyNotAllowed)。
限制
| 限制项 | 数值 | 来源 |
|---|---|---|
| 最大指令堆栈深度 | 5(SIMD-0268 可达 9) | MAX_INSTRUCTION_STACK_DEPTH, MAX_INSTRUCTION_STACK_DEPTH_SIMD_0268 |
| CPI 调用成本 | 1,000 CU(SIMD-0339 为 946) | DEFAULT_INVOCATION_COST, INVOKE_UNITS_COST_SIMD_0339 |
| 每次 CPI 最多 PDA 签名者 | 16 | MAX_SIGNERS |
| CPI 指令数据最大值 | 10 KiB(10,240 字节) | MAX_INSTRUCTION_DATA_LEN |
| 返回数据最大值 | 1,024 字节 | MAX_RETURN_DATA |
| CPI 账户信息最大数量 | 128(SIMD-0339 可达 255)* | MAX_CPI_ACCOUNT_INFOS, MAX_CPI_ACCOUNT_INFOS_SIMD_0339 |
| CPI 序列化成本 | 每 250 字节 1 CU | cpi_bytes_per_unit |
| 每次 CPI 账户数据最大重分配 | 10,240 字节(10 KiB) | MAX_PERMITTED_DATA_INCREASE |
invoke 与 invoke_signed
Solana 提供了两个用于进行 CPI 的函数:
| 函数 | 使用场景 | PDA 签名 |
|---|---|---|
invoke | 所有所需签名者已在原始交易中签名的 CPI | 否 |
invoke_signed | 调用程序需要代表其拥有的 PDA 进行签名的 CPI | 是,通过 signer seeds |
在底层,invoke 实际上只是调用了 invoke_signed,并传入一个空的 signer
seeds 数组。当你不需要 PDA 签名时,使用
invoke;当程序需要代表 PDA 授权操作时,使用 invoke_signed。
这两个函数最终都会触发相同的 syscall(sol_invoke_signed_rust),并经过相同的运行时路径(cpi_common)。唯一的区别在于是否提供了 signer
seeds。当提供 seeds 时,运行时会派生 PDA 公钥,并在权限检查前将其加入有效签名者集合。
Is this page helpful?