本文將探討 sCrypt 智能合約背後的概念,以及使用 sCrypt 程式設計的一些最佳實踐和安全檢查清單。

作者:Certik

封面: Photo by  Conny Schneider  on  Unsplash

sCrypt 是一種基於 TypeScript 的嵌入式領域特定語言(eDSL),專為在比特幣鏈上編寫智能合約而設計。 sCrypt 智能合約使用比特幣支援的操作碼,可以編譯成 Bitcoin Script。由此產生的類似彙編的腳本可用作交易中的鎖定腳本。

本文將探討 sCrypt 智能合約背後的概念,以及使用 sCrypt 程式設計的一些最佳實踐和安全檢查清單。

使用 sCrypt 編寫編寫智能合約:一個簡單範例

比特幣上的智慧合約使用 UTXO 模型,每筆比特幣交易由輸入和輸出組成,每筆比特幣交易由輸入和輸出組成:輸出包含:

  • 比特幣的數量
  • 字節碼(稱為 locking script,“鎖定腳本”)

輸入包含:

  • 對上一筆筆交易輸出的引用
  • 字節碼(unlocking script,“解鎖腳本”)

未花費交易輸出(UTXO)是指在任何交易中尚未被消耗的輸出。低階字節碼/操作碼,稱為 Bitcoin Script [1],由比特幣虛擬機器(BVM)[2]解釋執行。

比特幣支援的操作碼可以編譯為 Bitcoin Script。產生的類似彙編的腳本用作交易中的鎖定腳本。

如上圖所示的兩個交易,每個交易都有一個輸入(綠色)和一個輸出(紅色)。右側的交易使用了左側交易的輸出。鎖定腳本可以視為一個布林函數 f(x),它定義了花費 UTXO 中比特幣的條件,起到「鎖」的作用(因此稱為「鎖定」)。解鎖腳本則提供了使 f(x) 結果為 true 的函數參數,充當「鑰匙」(也稱為見證)來解鎖它。只有當輸入中的「鑰匙」與先前輸出的「鎖」相符時,才能花費該輸出中包含的比特幣。

在標準的比特幣支付中,鎖定腳本是「Pay To Pubkey Hash」(P2PKH)。它用於驗證付款人是否擁有與地址對應的正確私鑰,從而使他們能夠在解鎖腳本中產生有效的簽名。 sCrypt 語言使得鎖定腳本可以指定比簡單的 P2PKH 更複雜的花費條件,即 P2TR/P2SH 交易中的比特幣智能合約。

sCrypt 的智能合約在概念上類似於物件導向程式設計中的類別。每個套件都為特定類型的合約(例如 P2PKH 或多重簽名)提供了模板,可以實例化為具體的可執行合約物件。

部署並呼叫 sCrypt 智能合約

sCrypt 使用支付到見證腳本哈希(Pay-to-Witness-Script-Hash,P2WSH)來部署合約。部署過程包括將智慧合約程式碼編譯成腳本,對該腳本進行哈希處理,然後將雜湊值放入一個 P2WSH 交易 (Tx0),並將其廣播到網路。

當有人要呼叫已部署的合約時,他們會將完整的合約腳本和被呼叫方法的輸入作為見證資料嵌入到花費 Tx0 的後續事務 (Tx1) 中。

部署與交易呼叫:左側表示輸入,右側表示輸出

已知的 sCrypt 局限性

sCrypt 可以在任何支援 Bitcoin Script 的區塊鏈上運行,包括比特幣分叉鍊和基於比特幣的鏈,如 Litecoin 和 Dogecoin。

比特幣禁用了許多腳本操作碼,例如 OP_CAT 和 OP_MUL,這極大地限制了 sCrypt 能表述的智能合約類型。比特幣社群正在積極討論重新啟用這些操作碼並引入新的操作碼。如果變更的提案得到採納,sCrypt 在比特幣上的功能將比現在更強大。

同時,有些區塊鏈具備完整的腳本操作碼,例如比特幣 SV 和 MVC。如今,sCrypt 在這些鏈上已達到滿載運行。

回溯至創世問題(Back-to-Genesis)

使用 sCrypt 的智能合約實現同質化代幣(FT)和非同質化代幣(NFT)時,會引發回溯至創世(Back-to-Genesis,B2G)問題,這是一個極大的安全挑戰。該問題涉及在基於 UTXO 的區塊鏈中追蹤代幣的創建交易。在比特幣等區塊鏈上,透過 sCrypt 創建的代幣以 UTXO 形式存在,並可能被頻繁轉移。 B2G 問題的關鍵在於,當試圖追蹤或驗證代幣的完整歷史記錄時,需要找到其創世交易,以確認代幣的來源和真實性。

從各種角度上來說,這一點很重要。包括:

  • 來源和真實性:了解代幣的完整歷史可以讓用戶驗證其真實性和來源。用戶可能希望確保代幣有一個合法且可追溯的來源。
  • 智能合約邏輯:智能合約或去中心化應用可能會依賴代幣的歷史。了解代幣的來源對於某些智能合約功能至關重要。

下圖展示了偽造基於 sCrypt 的 FT 和 NFT 的兩種方法。每個框代表一個交易,左側是輸入,右側是輸出。箭頭指向表示從一個交易到另一個交易的流轉過程。具有相同輸出顏色的交易使用相同的合約代碼。

Back-to-Genesis 問題可能會引發代幣協議中的兩個問題:

  • 重播攻擊(Replay Attack):攻擊者可能會重新發行相同的代幣。
  • 中間人攻擊(Man-in-the-Middle Attack):攻擊者可以從一個不相關的 UTXO 開始,並複製一個代幣交易鏈。他們可以複製交易的輸出(如第 1 行所示),並將其逐字貼到另一個交易中(如第 3 行所示)。這種情況之所以可能發生,是因為鎖定腳本僅在解鎖時進行評估,而不是在部署時進行評估。從那時起,交易就可以用來創建一個並行的虛假代幣鏈。

解決方案

在這些場景中,交易因為滿足 Layer-1 的驗證而被礦工接受。被紅色圓圈標記的最後幾筆交易看起來完全相同。驗證代幣交易合法性的唯一方法是追溯到其發行交易(即創世交易)。

為了解決重播攻擊,我們建議實作一個全域唯一的 ID,“GenesisID”,它代表發行交易的交易 ID(txid)。當發行 UTXO 被消費時,該 ID 會被複製,並作為代幣 ID 保留在所有後續代幣傳輸 UTXO 中。

使用發行交易的 TXID 作為唯一的 TokenID

為了減少中間人攻擊,我們建議開發者在目前交易之前回溯兩步,驗證父交易及其前一交易。

以下是一個簡單的範例,用於說明回溯驗證以及提前兩步驟驗證的重要性:

提前兩步驟驗證

當偽造的 UTXO(UTXO1)被花費到另一個代幣 UTXO(UTXO2)時,由於代幣合約並未被啟動(UTXO 的鎖定腳本僅在解鎖時執行),所以它會通過礦工驗證。然而,在我們建議的實作中,嘗試將 UTXO2 存入 UTXO3 需要同時驗證 UTXO1 和 UTXO2。這樣,中間人攻擊就失敗了,因 UTXO1 不包含與 UTXO2 和 UTXO3 相同的解鎖合約,交易將因此被拒絕。

sCrypt 安全提示與檢查清單

以下是 CertiK 在完成對一個基於 sCrypt 的 FT/NFT 項目審計後總結的安全提示和檢查清單:

1. 驗證代幣的回溯是否準確,當前 UTXO 的鎖定腳本程式碼段是否與前一個 UTXO 的匹配

sCrypt 團隊提供了一個範例,可以幫助開發者設計回溯驗證流程:

2. 確保協定不會受到偽造創世 ID 的攻擊

這可以透過在回溯過程中驗證創世 ID 來實現,如下例所示:

檢查應確認目前的 genesisTxid 是否與創世交易的 ID 或前一個交易的 ID 相符。

3. 確認 UTXO 輸入證明真實合法,而非偽造

必須驗證以下參數:

  • 令牌發送頭
  • 上一個令牌輸入索引
  • tokenTx 輸入證明
  • prevTokenTx 證明

我們建議開發者在驗證 UTXO 輸入證明時遵循此流程圖。

4. 確保代幣解鎖過程已適當授權

代幣可以由代幣所有者直接解鎖,也可以透過代幣鎖定合約解鎖。代幣所有者必須透過有效的簽名驗證其所有權,以解鎖代幣。如果透過智能合約解鎖代幣,必須遵守合約中規定的任何條款或限制。請注意,禁止解鎖被燒毀的代幣。

5. 確保代幣轉移 UTXO 中的代幣數量保持一致,以防止雙花攻擊

已解鎖的代幣可以轉移到一個或多個接收錢包。重要的是確保作為輸入的代幣總量等於作為輸出的代幣總量。換句話說,正在轉移的代幣必須與可用的代幣相匹配,以保持平衡。透過強制執行這項要求,合約能夠防止雙花攻擊的可能性。

6. 驗證是否選擇了適當的 SigHash 類型,明確指定交易的哪些部分被簽名

SigHash [3]標誌決定了加密簽名涵蓋比特幣交易中哪些部分。預設情況下,使用 SIGHASH_ALL 標誌,確保簽名涵蓋所有輸入和輸出。然而,選擇不同的 SigHash 類型時需要謹慎。例如,SIGHASH_NONE 標誌不會簽署任何輸出,這可能會引入安全漏洞。應用簽名後可更改的輸出可能會導致詐欺或操縱。

7. 驗證合約完整性

在比特幣的 UTXO 模型中,智能合約通常是一次性且無狀態的。這是因為包含合約的 UTXO 一旦用完就會被銷毀,且不會在區塊鏈上留下痕跡。儘管這種設計簡單且高效,但也存在安全風險,因為合約可能會被篡改。為此,需要驗證合約腳本程式碼的雜湊值,這包括將腳本的雜湊值嵌入到腳本資料中。在交易過程中,將嵌入的雜湊值與腳本的實際雜湊值進行比較,即可確認腳本的完整性。

為了應對挑戰,一種方法是驗證合約腳本程式碼的雜湊值。這種方法需要將腳本的雜湊值作為資料的一部分保存在腳本內部。在執行涉及智慧合約的交易時,可以將腳本程式碼的雜湊值與儲存的值進行比對。如果雜湊值匹配,則確認合約腳本未發生變化,且未被篡改。

8. 驗證交易完整性

sCrypt 提供了一個強大的函式庫叫做 Tx,除了鎖定腳本和解鎖腳本外,還能檢查包含合約本身的整個交易。 sCrypt 透過這項全面的交易檢查功能,使合約能夠驗證輸入資料。

主要驗證步驟之一是將解鎖腳本的輸入與從交易預映像中提取的資料進行比對。此外,還需要驗證 txPreimage 是否為目前交易的預映像。

9. 驗證資料完整性

在輸出的鎖定腳本中,智慧合約被分成兩個部分:程式碼和狀態。合約的狀態儲存在鎖定腳本的資料部分。在協定本身管理資料部分的場景中,必須特別注意資料欄位的處理。至關重要的是,必須要驗證儲存在鎖定腳本資料部分的資料欄位是否在正確的位置索引上進行存取和儲存。

總結

綜上所述,sCrypt 作為一種比特幣智慧合約開發語言,為開發者提供了豐富的可能性。從回溯驗證到防偽造代幣和完整性檢查,本文總結的安全提示與最佳實踐建議,來自業界領先的審計機構與開發者的實際經驗。希望這些內容能為開發者提供有價值的參考,協助建置更安全、更有效率的 Web3.0 應用。

[1] 比特幣腳本:https://wiki.bitcoinsv.io/index.php/Scr​​ipt

[2] 比特幣虛擬機器(BVM): https://xiaohuiliu.medium.com/introduction-to-bitcoin-smart-contracts-9c0ea37dc757

[3] SigHash:https://wiki.bitcoinsv.io/index.php/SIGHASH_flags

免責聲明:作為區塊鏈資訊平台,本站所發布文章僅代表作者及來賓個人觀點,與 Web3Caff 立場無關。文章內的資訊僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。