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
~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly
Security Insights

~$15.9M Lost: Trusted Volumes & More | BlockSec Weekly

This BlockSec bi-weekly security report covers 11 notable attack incidents identified between April 27 and May 10, 2026, across Sui, Ethereum, BNB Chain, Base, Blast, and Berachain, with total estimated losses of approximately $15.9M. Three incidents are analyzed in detail: the highlighted $1.14M Aftermath Finance exploit on Sui, where a signed/unsigned semantic mismatch in the builder-fee validation allowed an attacker to inject a negative fee that was converted into positive collateral during settlement; the $5.87M Trusted Volumes RFQ authorization mismatch on Ethereum; and the $5.7M Wasabi Protocol infrastructure-to-contract-control compromise across multiple EVM chains.

Newsletter - April 2026
Security Insights

Newsletter - April 2026

In April 2026, the DeFi ecosystem experienced three major security incidents. KelpDAO lost ~$290M due to an insecure 1-of-1 DVN bridge configuration exploited via RPC infrastructure compromise, Drift Protocol suffered ~$285M from a multisig governance takeover leveraging Solana's durable nonce mechanism, and Rhea Finance incurred ~$18.4M following a business logic flaw in its margin-trading module that allowed circular swap path manipulatio

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly
Security Insights

~$7.04M Lost: GiddyDefi, Volo Vault & More | BlockSec Weekly

This BlockSec weekly security report covers eight attack incidents detected between April 20 and April 26, 2026, across Ethereum, Avalanche, Sui, Base, HyperLiquid, and MegaETH, with total estimated losses of approximately $7.04M. The highlighted incident is the $1.3M GiddyDefi exploit, where the attacker did not break any cryptography or use a flash loan but simply replayed an existing on-chain EIP-712 signature with the unsigned `aggregator` and `fromToken` fields swapped out for a malicious contract, demonstrating how partial signature coverage turns any historical signature into a generic permit. Other incidents include a $3.5M Volo Vault operator key compromise on Sui, a $1.5M Purrlend privileged-role takeover, a $413K SingularityFinance oracle misconfiguration, a $142.7K Scallop cross-pool index injection, a $72.35K Kipseli Router decimal mismatch, a $50.7K REVLoans (Juicebox) accounting pollution, and a $64K Custom Rebalancer arbitrary-call exploit.