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

第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中,有四种不同的集群:mainnet、testnet、devnet和localnet。在本帖中,我们仅为演示目的在devnet上部署编译后的程序。
我们使用以下命令指定集群。
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() 函数建立与指定集群(即Devnet)的连接,使用 establishPayer 函数检查支付交易费用的付款人,并使用 checkProgram 函数验证程序和要问好的账户是否存在。请注意,如果所需账户不存在,它将被创建,方法是发送一笔交易。在我们的演示中,账户是在此交易中创建的。

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



