1. 簡介
Solana 是一個高效能、無需許可的 Layer 1 區塊鏈系統,支援使用不同語言(如 Rust、C++ 和 C)開發程式(智能合約)。藉助 Tower BFT(拜占庭容錯)共識機制,它能夠每秒處理數千筆交易。Solana 的核心創新之一是歷史證明(Proof of History,簡稱 PoH),這是一種在網路中全域可用、無需許可的時間來源,其運作優先於共識機制。我們將在未來更詳細地討論 PoH。
2. 我們的使命
BlockSec 是一家區塊鏈安全公司,將「保護 DApp 生態系統」視為其使命。因此,我們不僅提供合約審計服務,還為整個社群提供關於如何編寫安全智能合約的建議。考慮到 Solana 的受歡迎程度、高效能以及優良的設計,本系列將專注於 Solana 上 Rust 智能合約的安全性。
3. Hello Solana
在我們從安全角度探討 Solana 智能合約之前,首先需要了解如何編寫、編譯和部署 Solana 智能合約。這裡我們以 Hello World 作為示範範例。
3.1 程式碼審查
在 Solana 中,智能合約被稱為程式 (program)。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 上部署編譯好的程式,我們首先需要選擇一個叢集 (cluster)。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 程式以及如何使用客戶端與鏈上程式互動。請持續關注我們的部落格,我們將會持續發佈該系列的更多文章。



