作者:ZAN Team
本文僅作為技術分享,不構成任何投資建議。
BTC 上也要有自己的智能合約了?
最近比特幣生態上,Fractal BTC 在經歷了多次測試網之後,終於在9 月上線主網。 Fractal 的一大特點就是具備「智能合約」的能力,而且幾乎在推出主網的同時,上線了一個新的代幣協定CAT20。 CAT20 有什麼技術上的巧妙設計呢?我們又可以學到什麼?
Fractal Bitcoin
在了解CAT20 之前我們需要簡單了解一下Fractal Bitcoin,他們的關係就像ERC20 和ETH 一樣,CAT20 協定是部署在Fractal Bitcoin 上的。
Fractal Bitcoin 又稱作分形比特幣,是完全相容BTC 的「二層」網路。相較於BTC,它的區塊確認時間更快,僅需1 分鐘。它的基本原理簡單來說就如它的名字所言,就是將BTC 網路複製了幾份,每條鏈都會處理交易,可以處理交易的節點多了,速度也就自然快了。不過具體的細節例如不同鏈之間是如何通訊的目前還不是很清楚,官方也沒有對應的技術文件可以參考。
如果只是一個二層鏈交易更快,似乎沒有讓人興奮的點。但是,在Fractal 中啟用了BTC 很久之前就因為安全原因棄用的操作碼OP_CAT,讓Fractal Bitcoin 的能力上升了一個台階,有人說OP_CAT 能讓BTC 具有智能合約的能力,這樣的話可以遐想的空間就更多了。
現在,就有人在Fractal Bitcoin 上實作了類似ERC20 的協定。
關於OP_CAT 為什麼棄用以及為什麼又可以在Fractal Bitcoin 上使用,後續可以展開講講,這裡我們關注CAT20。
CAT Protocol
以下內容參考白皮書:Introduction | CAT Protocol (https://catprotocol.org/)
以及github 倉庫:
GitHub – CATProtocol/cat-token-box: A monorepo for packages implementing CAT protocol (https://github.com/CATProtocol/cat-token-box)
有了底層的OP_CAT 支持,很快就有了對應的協議,CAT Protocol。目前一個已經在實際運行的協議是CAT20 協議,在Unisat 上也新增了對應的面板:https://explorer.unisat.io/fractal-mainnet/cat20。
看到CAT20 的名字大家應該也能反應過來,它應該要跟ERC20 比較像。相較於成熟的ERC20 協議,大家部署一個Token 已經非常的方便,CAT20 是如何實現ERC20 類似的生命週期。
Deploy
在部署之前,用戶需要指定自己的錢包地址以及代幣的基本信息,代幣的基本信息和ERC20 的類似:
會有一些不同點CAT20 可以設定預挖和每次Mint 的數量限制。當然ERC20 可以透過合約的能力也可以實現這些能力。
在部署階段,會發起兩筆交易,可以認為是兩個階段:「 commit 」和「 reveal 」。引用官方上的圖,部署的階段如下:
在「 commit 」階段,交易的輸出腳本中會將代幣的基本資訊寫入,例如代幣的名稱、符號等。在「 commit 」階段發起的交易hashId 會作為該代幣的標誌,用來區分其他代幣。
可以看到這筆交易「 bc1pucq…ashx 」這個utxo 就是對應了commit。然後剩下的兩筆指向「 bc1pszp…rehc4 」的交易,第一筆是用來支付下面「 reveal 」階段的gas 費,另一筆則是找零。
在「 reveal 」階段,可以看到有兩筆utxo 輸入,對應了先前commit 階段的前兩個輸出。這筆交易首先會輸出一個OP_RETURN,在OP_RETURN 中會保存CAT20 的初始狀態的Hash。之後會再輸出一個Minter,它會在後續的Mint 過程中發揮重要作用,用來維護Mint 過程的狀態變化。
回過頭看整個Deploy 的過程,「 commit 」和「 reveal 」遵循了區塊鏈上常用的提交和揭示兩個步驟,是一種比較常見的部署項目的方式,項目的一些數據只在「 reveal 」階段才會揭露出來。
Mint
我們先來看看Mint Token 的時候,交易是這樣的。
在上圖中可以看到,Mint 的過程有以下幾個特徵。
-
mint 的輸入是一個minter,最開始是deploy 的時候產生的。
-
每一次mint 都有且只有一個minter 作為輸入,有任一minter 作為輸出(有點問題)
-
每次mint 都有且只有一個token(有點點問題)
-
輸出的順序是有要求的,minter 後面必須是token
知道了Mint 的過程,其實我們可以發現一些特殊情況,會讓整個Mint 的過程變得有趣。
例如,minter 作為mint 交易的輸出,他可以是1 個、多個甚至是0 個。如果每次Mint 的時候都設定為1 個,那麼整個網路中可以使用到的minter 數量就會保持不變(1 個),這會讓Mint 變得擁擠,大家都需要搶這個minter,為了避免這種情況,是需要將每次輸出的minter 數量設為大於1 ,這樣mint 之後,大家可以使用的minter 就會越來越多。
不過,每多輸出一個minter 意味這你需要多支付一筆utxo,出於經濟考慮,更多的人會樂意將minter 設置為0,就會不可避免的讓minter 變得通縮,這就需要一些人來進行奉獻了,自願支付多出來的minter。
在V2 版本,預設是產生兩個Minter,並且兩個Minter 的狀態會盡可能相近。
交易的建構
可能有小夥伴發現了一個問題,那就是為什麼可以使用minter 的utxo 進行交易的建構?想要了解這個問題就需要對「合約」的原始碼進行分析。
1、reveal utxo
首先我們對reveal 流程中的交易進行分析,我們發現他使用了前一個交易的輸出commit 作為輸入。為什麼可以拿一個不是我們地址的utxo 建構交易的輸入呢?
依照常理,一個私鑰對應一個公鑰,公鑰衍生出地址。當驗證一個輸入的utxo 是否有效的時候,一般是透過比較簽章用公鑰解密之後是否和原本的交易一致來決定。這部分的邏輯是寫在比特幣腳本裡的。所以我們可以巧妙的改寫腳本的邏輯,在腳本中寫的公私鑰對是我們自己地址的,這樣我們就可以控制兩個不同地址的utxo 了。
看源碼我們就能知道發生了什麼事:
這裡還會有一個問題,就是一個私鑰對應一個公鑰,那為什麼產生的commit 地址會跟我們地址不一樣呢?這裡從原始碼可以看到
也就是說,我們的私鑰會根據一個ISSUE_PUBKEY 來調整公鑰,這也是P2TR 位址的特性。
2、minter utxo
reveal 流程中,我們使用不同的utxo 的作為輸入,但其實加密的金鑰是同一把,也就是部署者的私鑰。但在minter 階段,所有的人都可以使用這些utxo 作為輸入,這又是怎麼做到的呢?
這部分我猜測是之前說的OP_CAT 的能力,也就是智能合約的能力,每個minter 就是一個智能合約。不過目前這部分的源碼沒有公開,暫時不知道具體的實作是怎麼樣的。
交易的狀態(V2)
在minter 中,也保留了狀態。這個狀態存在兩個地方:一個是交易輸出的OP_RETURN 中,另外就是儲存在智能合約中,也就是上述提到的Minter 以及Token。
在OP_RETURN 中儲存的是目前交易輸出狀態的Hash,在合約中會儲存Token 剩餘的Mint 次數。每次Mint 之後,新產生的Minter 的mint 數量會等於剩餘可以mint 的數量除以二。用圖表示:
最後打完的時候,所有Minter 的剩餘數量為0。
回到最開始的圖上,除了Minter 是一個智能合約之外,生成的Token 也是智能合約,也就是CAT20。 CAT20 有兩個基本的狀態:數量以及Token 的歸屬者地址。你可以看到不像之前的BRC20 或銘文,你的CAT20 並不是在你地址的UTXO 上。
Transfer
Transfer 的時候,建構交易的輸入和輸出的token 其裡面的數量需要保持一致。當然同一筆交易裡面可以有多個不同的token,只需要不同token 的其輸入輸出的數量保持一致就行。
Burn
想要燃燒掉Token 的話,只需要將Token 轉到一個普通地址上即可。
總結
可以看到,所有的操作都是由使用者自己去構建,靈活性非常大,所以在合約部分需要做很多的校驗邏輯。目前爆出的一些漏洞也是因為校驗邏輯出現了疏忽。
這樣的設計可以有一些好處:
-
如果想要找出所有的Token 的持有情況,只需要檢查token 的utxo 就行,不需要繼續往上查。
-
如果想查看mint 目前的狀況,可以搜尋OP_RETURN 中資料帶有cat 的交易就好。