1. 引言
Solana 是一个高性能、无需许可的 Layer 1 区块链系统,支持使用多种语言(例如 Rust、C++ 和 C)开发程序(智能合约)。借助 Tower BFT(拜占庭容错),它每秒可以处理数千笔交易。Solana 的核心创新之一是历史证明(PoH),这是一个在网络中可全局访问、无需许可的时间源,它在共识之前运行。我们将在未来更详细地讨论 PoH。
2. 我们的使命
BlockSec 是一家区块链安全公司,以“保护 DApp 生态系统”为使命。因此,我们不仅提供合约审计服务,还为整个社区提供有关如何编写安全智能合约的建议。考虑到 Solana 的流行度、高性能和出色的设计,本系列将重点关注 Solana 上 Rust 智能合约的安全性。
3. Hello Solana
在从安全角度讨论 Solana 智能合约之前,我们首先需要了解如何编写、编译和部署 Solana 智能合约。这里我们以 Hello World 为例进行说明。
3.1 代码审查
在 Solana 中,智能合约称为程序。 Hello World 程序旨在维护一个计数器,该计数器记录客户端调用目标账户(由程序拥有)的次数,并将此数字作为输出打印。
现在让我们逐步分析整个合约。

第 1 行到第 9 行使用 use 关键字显式引入了许多库。

第 12 行,[derive(BorshSerialize, BorshDeserialize, Debug)] 用于序列化和反序列化传递给结构体 GreetingAccount 的参数,该结构体从第 13 行到第 16 行定义。该结构体包含一个名为 counter 的 u32 公有成员。之后,如注释所示,第 19 行导出了程序的入口点,即 process_instruction 函数。

process_instruction 函数接受三个参数(即 program_id、accounts 和 _instruction_data)。请注意,program_id 是已部署程序的公钥(即地址),而 accounts 包含要问候的账户。_instruction_data 为空且未使用。
要了解有关 Solana 中程序和账户的更多信息,请参阅 Solana 文档。
第 27 行,打印一条问候消息。从第 30 行到第 33 行,将遍历账户,并提取要问候的特定账户。之后,将检查账户的所有者,应将其与 program_id 进行比较,以确保程序可以写入该账户。最后,使用 try_from_slice 提取 greeting_account,并将计数器加 1。
3.2 编译程序
要编译程序,我们需要使用以下命令安装 Solana CLI。
sh -c "$(curl -sSfL https://release.solana.com/v1.9.9/install)"
要确认 Solana CLI 已成功安装,我们可以使用以下命令检查其版本。
solana --version
要编译程序,我们可以使用以下命令。
cargo build-bpf --manifest-path=./src/program-rust/Cargo.toml --bpf-out-dir=dist/program
编译后的程序将生成在 --bpf-out-dir 中指定的目录下的 helloworld.so 文件。
3.3 部署合约
要在 Solana 上部署编译后的程序,我们需要先选择一个集群。在 Solana 中,有四个不同的集群:主网、测试网、开发网和本地网。在本帖中,我们仅为演示在开发网上部署编译后的程序。
我们使用以下命令指定集群。
solana config set --url https://api.devnet.solana.com
之后,我们需要生成用于部署的钱包。 solana-keygen new --force 有助于生成钱包。有关更多信息,请查看 Solana 文档。
由于新创建的账户没有余额来支付交易费用,因此我们使用以下命令空投 1 SOL。
solana airdrop 1 <YourPublicKey> --url https://api.devnet.solana.com
现在,我们已准备好部署合约。
solana program deploy dist/program/helloworld.so
用户可以在 Devnet Explorer 中查看程序和交易。在此演示中,您可以通过 此链接 找到已部署的程序。
3.4 发送交易
建议编写脚本,即所谓的客户端,来发送 Solana 交易。幸运的是,Solana 提供了 示例代码。要运行客户端,我们首先需要安装客户端依赖项。
npm install
安装完成后,我们使用以下命令运行 main.ts 文件中的 main 函数。
npm run start

在 main 函数中,客户端首先使用 establishConnection() 函数建立与指定集群(即开发网)的连接,使用 establishPayer 函数检查支付交易费用的付款人,并使用 checkProgram 函数验证程序和要问候的账户是否存在。请注意,如果所需账户不存在,它将被创建,发送一个交易。在我们的演示中,该账户已在此 交易 中创建。

当一切就绪后,将调用 sayHello 函数向目标账户发送问候。交易包含一个指令。此指令接收包含一个账户的 keys。该账户的公钥(即 greetedPubkey)是要问候的目标账户。请注意,此账户用于存储,而不是签名者。在这种情况下,isSigner 属性为 false,而 isWritable 为 true。 programId 是 greetedPubkey 的所有者,也是已部署合约的地址。请注意,data 为空,并且在此交易中不会使用。在第 208 行,调用 sendAndConfirmTransaction 函数,交易将被发送到集群。
您可以在 这里 查看详细交易。
4. 结论
在本文中,我们简要介绍了 Solana 的背景,并回顾了示例项目(即 Hello World)。我们学习了如何在 Solana 上部署程序,以及如何使用客户端与链上程序进行交互。请继续关注我们的博客,我们将持续发布本系列的更多文章。



