本文介绍了在 Solana 上从程序 A 调用程序 B 的 CPI (Cross-Program Invocation) 技术,讨论了在没有程序 B 源代码的情况下如何调用它,并比较了调用 Anchor 框架和 Native Rust 构建的程序的差异。
通常,为了在 Solana 上从程序 A 到程序 B 执行 CPI,我们需要程序 B 的源代码。 那么,有没有办法在不需要源代码的情况下调用程序 B?以及当 CPI 到达一个用 Anchor 和 Native Rust 创建的程序时,这二者有什么区别?
你可以在这里查看代码演示:
max: 已初始化的工作流的数量。
ping: 一个工作流的投票数量。
注意: 在运行 (1) 和 (2) 之前,你需要修改两个地方的密钥对:
1. 在 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. 在 terminal 运行 ```solana config get```
你将会看到:Keypair Path: /home/trung1701/.config/solana/id.json
请替换 id.json 中的 keypair
(3) 当客户端与 vote() 函数交互时,Workflow 程序将调用 Logic1 程序。 Logic1 是用 Anchor 框架编写的程序。 ping 字段将在 PDA workflow 中递增 1 (ping++)。
(4) 当客户端与 vote2() 函数交互时,Workflow 程序将调用 Logic2 程序。 Logic2 是用 Native Rust 编写的程序。 ping 字段也将在 PDA workflow 中递增 1 (ping++)。
Logic1 和 Logic2 程序与 Workflow 程序完全独立构建。 Workflow 程序没有 Logic1 和 Logic2 程序的 IDL 文件。 因此,我们唯一知道的是 Logic1 和 Logic2 程序在 Solana 网络上的 programID 以便与它们交互。
在 Solana 编程中,接口定义语言 (IDL) 定义了一个程序的公共接口。 它定义了 Solana 程序的账户结构、指令和错误。 IDL 是用于编写与 Solana 程序交互的客户端代码的 .json 文件。
那么,用 Anchor 框架和 Native Rust 编写的程序之间执行 CPI 有什么不同?执行 CPI 时需要注意什么?
你可能听说过 Anchor 中的 discriminator。 那么它到底是什么?
准确地说,discriminator 用于确定在用 Anchor 构建的程序中将调用哪个函数。 discriminator 从字符串 "global:<NameOfFunction>"
的 Sha256 哈希值的前 8 个字节中截取。
例如,在 logic1 程序中有一个 vote() 函数。 Workflow 程序如何在不发生错误的情况下调用 logic1 程序中的 vote 函数? 为了做到这一点,在指令的 data 字段中,我们必须初始化一个向量,其中前 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)
。 为了使 logic1 程序中的 vote 函数能够接收这个数据,你需要添加 4 个字节来声明想要发送的结构体的字节数组的长度信息,然后再发送结构体的字节数组。 你可以在这里了解更多关于如何分配空间以将其他数据格式发送到 Anchor 程序的信息:https://book.anchor-lang.com/anchor_references/space.html
这样,当执行 CPI 时,我们可以看到我们在 Solana Explorer 上发送的指令数据如下所示:
正如你所看到的,为了调用任何由 Anchor 构建的程序,我们必须遵循 Anchor 的数据结构声明规则。 否则,我们将立即收到错误。
对于 Native Rust,我们可以调用另一个程序而无需任何注意。 你可以查看 workflow 程序和 logic2 程序中的 vote2() 函数进行比较。 然而,为了能够处理和导航到正确的函数以实现指令的意图,我们可以将 variant 字段添加到从客户端发送的结构体中,然后程序 A 将把这个结构体发送到程序 B。 届时,程序 B 将需要处理 variant 以导航到指令期望的函数。
总而言之,当从程序 A 到程序 B 中用 Anchor 构建的函数执行 CPI 时,我们需要注意根据 Anchor 提供的空间规则声明 discriminator 和格式化数据。
- 原文链接: medium.com/@trungbaotran...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!