本文深入探讨了Solana中跨程序调用(CPI)的实现方法,重点介绍了如何在没有目标程序源代码的情况下调用程序,并分析了调用Anchor程序和原生Rust程序的差异。文章通过示例代码,详细解释了Anchor程序的discriminator机制以及数据格式要求,并对比了原生Rust程序在CPI方面的灵活性。文章还提供了实际的代码示例和截图,帮助读者理解和实践。
通常,要从程序 A 对程序 B 执行 CPI,我们需要程序 B 的源代码。那么如何在没有源代码的情况下调用程序 B?调用用 Anchor 创建的程序和本地 Rust 程序之间有什么区别?
你可以在这里找到代码演示:
max: Number of workflows initiated.
ping: Number of votes in a workflow.
注意:在运行 (1) 和 (2) 之前,你需要编辑两个地方的密钥对:
1. in test/workflow.ts
const keypair = web3.Keypair.fromSecretKey(
Uint8Array.from([64,33,98,76,216,30,65,85,81,32,240,30,164,197,23,225,253,179,10,197,190,174,155,56,130,224,202,128,189,201,48,37,20,123,160,201,77,149,50,29,89,209,232,173,89,87,250,249,192,221,235,132,195,237,147,165,80,165,155,92,70,100,203,86])
)
2. in terminal run ```solana config get```
you will see: Keypair Path: /home/trung1701/.config/solana/id.json
please replace keypair in id.json
Logic1 程序和 Logic2 程序与 Workflow 程序完全分开构建。Workflow 程序没有 Logic1 程序和 Logic2 程序的 IDL
文件。因此,我们只知道 programID 已部署到 Solana 网络以与该程序交互。
在 Solana 编程中,接口定义语言 (IDL) 指定了程序的公共接口。它定义了 Solana 程序的账户结构、指令和错误代码。IDL 是 .json 文件,用于生成客户端代码,使用户可以轻松地与 Solana 程序交互。
那么 Anchor 和 Native Rust 的 CPI 有什么区别?此外,在执行 CPI 时,我们应该注意哪些数据传输事项?
你们可能都知道 Anchor 中的 discriminator。它实际上是什么?
discriminator 实际上用于确定应该在 Anchor 程序中调用哪个函数。discriminator 由 "global:<NameOfFunction>"
的 Sha256 哈希的前 8 个字节定义。
例如,在 logic1 程序中有一个 vote()
函数。如何在不发生错误的情况下使 Workflow 程序调用 Logic1 程序?为此,在指令的数据字段中,我们将必须初始化一个向量,其中前 8 个字节用于初始化 discriminator。
Step 1: using sha256 for string "global:vote" and you will get the result:
e36e9b17887eac197678a3a9928f2dfc8a1d553a698244524539ebb858a2b4d0
Step 2: Take the first 16 letters and convert hex to 8 bytes array
e36e9b17887eac19 => [227, 110, 155, 23, 136, 126, 172, 25]
很好!然而,这只是 discriminator,你需要根据你想从客户端发送的格式来更多地关注。在我的例子中,客户端想要发送一个 struct{number1:1, number2:7}
。然后我们将其转换为一个字节数组。这个字节数组的长度为 2。你可以在 Workflow 程序的 test 文件夹中的 workflow.ts
文件中找到此代码,查看注释 //test invoke from anchor program to anchor program(logic1)
。为了让 Anchor 程序中的 vote 函数识别此数据,你需要 4 个字节来包含此字节数组的长度信息,然后是包含你想要发送的结构的字节数组的信息。你可以在这里了解更多关于其他数据格式的信息:https://book.anchor-lang.com/anchor_references/space.html
因此,当执行 CPI 时,我们可以看到我们在 Solana explorer 上发送的指令数据具有以下格式:
正如你所见,要对任何 Anchor 程序执行调用,我们将必须遵循 Anchor 数据结构规则。否则,我们将立即收到错误。
对于 Native Rust,我们可以正常地从另一个程序进行调用,而无需过多麻烦。这完全取决于你。所以我不会展示任何东西,我只是简单地用 Logic2 程序给出一个演示,以便你可以看到和比较。
你需要在结构中添加一个 variant 字段,然后处理它们。
简而言之,当从程序 A 对用 Anchor 构建的程序 B 中的函数执行 CPI 时,我们需要注意 discriminator 声明和数据格式。对于 Native Rust,我们可以从程序 A 对程序 B 中的函数执行 CPI,而无需在指令中声明任何其他内容。但是,在 Native Rust 中,为了能够根据指令的目的处理并导航到正确的函数,我们可以向结构添加一个 variant 字段,并且程序 B 需要处理这些 variant 以导航到特定函数。
- 原文链接: medium.com/@trungbaotran...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!