Back to Blog

保障 Solana 生态(二)—— 程序间调用

March 18, 2022
5 min read

0. 综述

1. 概述

在上篇博客中,我们介绍了如何部署和与程序进行交互。除了从客户端调用程序的指令外, Solana 还允许程序通过一种称为跨程序调用的机制互相调用。在这篇文章中,我们将说明跨程序调用的使用方法。测试代码在此处:https://github.com/blocksecteam/solana_demo

2. 跨程序调用

跨程序调用通常使用 invoke 函数实现。本节将通过在程序内部转账 lamports 来演示 invoke 的用法。

在 Solana 中,有一个名为 System Program原生程序,它负责创建新账户和转账 lamports (SOL)。 在这种情况下,要在程序内部(例如,程序 A)转账 lamports,程序 A 将调用 System Program 中的 transfer() 函数。让我们来逐步分析下面的具体示例。

2.1 代码审查

第 3 行到第 10 行导入了必要的库。请注意,invoke() 函数位于 solana_program::program 库中。

process_instruction 函数中,第 22 行到第 30 行提取了客户端传入的三个账户。请注意,lamports 将从 from_account 转账到 to_account。从第 33 行到第 44 行,调用了 invoke() 函数。它接收两个参数。第一个参数是目标调用的指令,第二个参数是一组账户。 在本例中,目标指令是用于转账 lamports 的 transfer()。账户包括被调用指令所需的所有账户。在本例中,就是 from_accountto_account

已部署的程序可以在以下链接中找到。

https://explorer.solana.com/address/EPaLuYQ4c11BJAe9ucLbta3xGFb17Zy3cZh3UDPbXRG9?cluster=devnet

2.2 转账 Lamports

如前所述,我们将交易发送到已部署的程序。已部署的程序将进一步调用 System Program 将 lamports 转账到目标地址。 上面的代码显示了客户端如何构建交易。第 93 行到第 101 行提取已部署程序的 programId。第 103 行到第 109 行生成发送方、接收方和 System Program 的地址。第 111 行到第 117 行构造交易。第 118 行,交易被发送到 Solana 集群。请注意,此交易中唯一的签名者是发送方,在第 112 行设置。发送方必须授权(即签名)从其账户扣款的交易,而接收方则不需要(第 113 行)。

您可以通过以下链接找到该交易。

https://explorer.solana.com/tx/4cxqnff8SakVcE9y5phmh6utcbBUGaLDPvqMRgSy9aPGdNVH6DCsQyJXCCzRGvW5CpygUi5pqhgBQxczXnWCoqPJ?cluster=devnet

3. Invoke 或 Invoke_signed

在 solana 中,程序可以在运行时生成称为程序派生地址 (PDA) 的账户。如果程序调用的目标指令包含带有 PDA 的签名账户,则应使用 invoke_signed() 而不是 invoke()。我们将使用另一个示例来演示 invoke_signed() 的用法。现在,让我们先回顾一下合约代码。

3.1 代码审查

在本例中,我们在程序内部(即程序 B)创建了一个 PDA。要创建 PDA,应在程序 B 中调用 System Program

第 3 行到第 10 行将导入所需的库。请注意,这次导入了 invoke_signed()(第 6 行)。

与第 2 节中的示例类似,我们提取了所需的账户(第 22-27 行),即 System Program 和 PDA。然后,我们使用 find_program_address() 函数生成 PDA 的地址以及用于将此 PDA 推离 ed25519 曲线的种子(第 29-30 行)。这是为了保证该地址没有关联的私钥。在本例中,客户端和程序使用相同的种子(即“You pass butter”)来生成 PDA。因此,公钥应该是相同的,这一点从第 31 行到第 34 行进行检查。之后,我们从第 37 行到第 47 行调用 invoke_signer 来发出 allocate() 指令。与 invoke() 函数不同,它还接受一个额外的参数,该参数是用于创建 PDA 的种子以及 find_program_address() 函数使用的 bump seed。

要创建/分配账户,账户本身应提供签名,在本例中 PDA 是签名者。要签名 PDA,则使用 invoke_signed。具体来说,solana 将使用种子和调用方(即程序 B)的 programId重新创建 PDA,并将其与提供的账户(第二个参数)进行匹配。如果它们相等,则 PDA 将被签名。

可以在下方查看已部署的程序。

https://explorer.solana.com/address/4h3RXGsouTRvUWNG1Dqq2tuuASTXFebHC3wFzv3tSFCK?cluster=devnet

3.2 创建 PDA

让我们来看第二个示例的客户端脚本。

在第 104 行,我们通过 findProgramAddress() 函数提取 PDA 和 bump seed。 从第 112 行到第 120 行,将创建交易并发送出去。请注意,PDA 的属性 (isSigner) 在这里是“false”(第 113 行),因为它不能由客户端签名。在运行时,程序 B 将签名 PDA 并调用 System Program 中的 allocate 指令。

您可以在以下交易中找到已分配的 PDA。

https://explorer.solana.com/tx/5fzdfzbD4281pY1HJm37f1fxSEMGmWtQmbmFPzEiP9v6hWF8GRRZmfcMDvPJugTrs3npnCsaWUGvw15URyBhx3LS?cluster=devnet

3.3 我们可以使用 Invoke() 吗?

为了演示 invoke 在这里不能被使用,我们使用另一个程序(即程序 C)来调用 allocation 指令。

我们所做的唯一更改是将 invoke_signed() 函数更改为 invoke() 函数。我们注意到账户无法创建。

错误显示跨程序调用需要签名者才能成功执行。也就是说,需要 invoke_signed() 函数。

4. 结论

在本文中,我们介绍了如何通过 invoke() 函数实现跨程序调用。我们还使用不同的示例来说明 invoke()invoke_signed() 之间的区别。敬请期待本系列的更多文章。

阅读本系列的其它文章:

Sign up for the latest updates
The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis
Security Insights

The Decentralization Dilemma: Cascading Risk and Emergency Power in the KelpDAO Crisis

This BlockSec deep-dive analyzes the KelpDAO $290M rsETH cross-chain bridge exploit (April 18, 2026), attributed to the Lazarus Group, tracing a causal chain across three layers: how a single-point DVN dependency enabled the attack, how DeFi composability cascaded the damage through Aave V3 lending markets to freeze WETH liquidity exceeding $6.7B across Ethereum, Arbitrum, Base, Mantle, and Linea, and how the crisis forced decentralized governance to exercise centralized emergency powers. The article examines three parameters that shaped the cascade's severity (LTV, pool depth, and cross-chain deployment count) and provides an exclusive technical breakdown of Arbitrum Security Council's forced state transition, an atomic contract upgrade that moved 30,766 ETH without the holder's signature.

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Apr 13 – Apr 19, 2026

This BlockSec weekly security report covers four attack incidents detected between April 13 and April 19, 2026, across multiple chains such as Ethereum, Unichain, Arbitrum, and NEAR, with total estimated losses of approximately $310M. The highlighted incident is the $290M KelpDAO rsETH bridge exploit, where an attacker poisoned the RPC infrastructure of the sole LayerZero DVN to fabricate a cross-chain message, triggering a cascading WETH freeze across five chains and an Arbitrum Security Council forced state transition that raises questions about the actual trust boundaries of decentralized systems. Other incidents include a $242K MMR proof forgery on Hyperbridge, a $1.5M signed integer abuse on Dango, and an $18.4M circular swap path exploit on Rhea Finance's Burrowland protocol.

Weekly Web3 Security Incident Roundup | Apr 6 – Apr 12, 2026
Security Insights

Weekly Web3 Security Incident Roundup | Apr 6 – Apr 12, 2026

This BlockSec weekly security report covers four DeFi attack incidents detected between April 6 and April 12, 2026, across Linea, BNB Chain, Arbitrum, Optimism, Avalanche, and Base, with total estimated losses of approximately $928.6K. Notable incidents include a $517K approval-related exploit where a user mistakenly approved a permissionless SquidMulticall contract enabling arbitrary external calls, a $193K business logic flaw in the HB token's reward-settlement logic that allowed direct AMM reserve manipulation, a $165.6K exploit in Denaria's perpetual DEX caused by a rounding asymmetry compounded with an unsafe cast, and a $53K access control issue in XBITVault caused by an initialization-dependent check that failed open. The report provides detailed vulnerability analysis and attack transaction breakdowns for each incident.