
Ordinal銘文協議的原理與技術細節討論
TechFlow Selected深潮精選

Ordinal銘文協議的原理與技術細節討論
UTXO裡面的sats到底是怎麼被追蹤的,銘刻的內容到底是放在腳本什麼地方,以及BRC20在轉賬的時候為何需要兩次操作?
作者:@hicaptainz
最近兩週我在研究BTC生態和各種銘文項目的時候,發現很少有文章能夠清晰地把原理和技術細節介紹的清楚:比如銘文在鑄造的時候,交易是如何發起的,UTXO裡面的sats到底是怎麼被追蹤的,銘刻的內容到底是放在腳本什麼地方,以及BRC20在轉賬的時候為何需要兩次操作?我發現不瞭解這些技術細節,就很難搞明白 BRC20,BRC420,atomicals, stamps, 符文Runes這些各種協議的區別,本文將深入到BTC區塊鏈的基礎知識,試著回答上述問題。
BTC的區塊結構
區塊鏈本質是一種多用戶記賬技術,用計算機科學術語來說,是一種分佈式數據庫,每一段時間內的記錄(賬目)組成一個區塊,然後根據時間先後順序進行賬本擴展。

我們用excel做了表格來說明區塊鏈的工作原理。一份excel文件代表了一個區塊鏈,其中每一個單獨表格表示一個個區塊,區塊按照時間順序從560331,560332.一直到最新的560336. 560336會在區塊內打包最近的交易。區塊內部主體部分就是我們在會計領域最常見的複式記賬法,一邊地址記做借出(debit)就是inputs from,另一邊地址記做貸入(credit)就是outputs to。Value對應相應地址的BTC數量。Inputs的幣的數量會大於Outputs幣的數量,差額就是用戶層面的轉賬費,也是礦工(記賬人)的取得的手續費。區塊頭部會獲取上一個區塊高度,上一個區塊的哈希值,本區塊的建立時間(時間戳),和隨機數。那麼做為去中心化的記賬技術,到底是誰來搶到下一個區塊的記賬權呢?靠的就是這個隨機數和與之對應的哈希值。擁有算力的礦工通過對當前區塊的隨機數進行哈希計算,最先得到符合條件哈希值的礦工擁有下一個區塊的記賬權並且贏得區塊獎勵和轉賬費。最後是腳本區域,可以用來做一些擴展應用,比如腳本op_return可以當做附言欄。需要注意的是,在實際的區塊中,腳本區是附著在input和output信息中的,而不是真的另外單獨一個區域。比如附著在input的腳本是解鎖腳本(ScriptSig),需要錢包地址進行私鑰簽名授權允許轉出,而附著在output的腳本是鎖定腳本(ScriptPubKey),用來設置收到該BTC的解鎖條件(一般情況條件就是“有相應私鑰的人才能消費”)。


上面兩張圖是原始的input和output的數據結構表,在執行層面,腳本表現為交易信息的附帶參數,其中解鎖腳本(ScriptSig)因為需要私鑰授權,也被稱為“見證數據”(witness data)。
隔離見證和Taproot
儘管比特幣網絡已經運行了超過10年,沒有發生過什麼顯著的事件,但曾多次出現交易成本飆升到不再可行的高點。因此,比特幣的開發人員一直在討論如何最好地擴展網絡,以處理未來不斷增長的交易量。
2017年,這場辯論達到高潮,比特幣開發社區分裂成兩派,一派是支持使用軟分叉實施名為SegWit的功能,另一派是支持直接區塊擴容的“大區塊”派。
我們在上文提到了解鎖腳本需要用到私鑰授權生成“見證數據”,那麼是不是可以把這個見證數據從區塊中分離,從而變相增加每個區塊可容納的交易數呢?隔離見證(Segregated Witness)在2017年8月激活正式激活。它的實現方式正是將所有的交易數據分為兩部分,一部分是交易的基本信息(Transaction Data),另一部分是交易的簽名信息(Witness Data),並把簽名信息保存在一個新的數據結構中,是被稱為“隔離見證(witness)”的新區塊中,並與原始交易分開傳輸。

在技術上,SegWit的實施意味著交易不再需要包括見證數據(不會佔用比特幣原本為區塊安排的 1MB 空間)。取而代之的是,在一個區塊的末尾,為見證數據創建了一個額外獨立的空間。它支持任意的數據轉賬,並有一個折扣的 "區塊重量(block weight)",巧妙地將大量的數據保持在比特幣的區塊大小限制內,以避免硬分叉的需要。這樣,比特幣交易的交易數據大小提高了上限,同時降低了簽名數據的交易費用。在SegWit升級之前,比特幣的容量上限是1MB,而SegWit之後,雖然單純交易的容量上限仍舊是1M,但隔離見證空間的大小達到了4MB。
Taproot 於2021年11月實施,由 3 項不同的比特幣改進提案 (BIP) 組成,其中包括:Taproot、Tapscript 及其名為「Schnorr 簽名」的全新數字簽名方案。Taproot 旨在為比特幣用戶帶來諸多好處,例如提升交易私密性和降低交易費用。還將讓比特幣執行更多複雜的交易,從而拓寬應用場景(新增加了一些操作碼opcodes)。
這些更新是 Ordinals NFT的關鍵推動因素,它將NFT數據存儲在 Taproot 腳本路徑的花費腳本(spent script)中(見證數據空間)。這次升級使得結構化和存儲任意的見證數據變得更加容易,為 "ord" 標準奠定了基礎。隨著數據要求的放寬,假設一個交易可以用其交易和見證數據填滿整個區塊 -- 達到4MB的區塊大小(見證數據空間)限制 -- 極大地擴展了可以放在鏈上的媒體類型。
也許有人會問,既然在腳本中放入一些字符串,那對這些字符串沒有限制條件嗎?萬一真的執行這些腳本呢?如果隨便放內容,那會不會出現錯誤代碼拒絕出塊呢?這就要提到 OP_FALSE指令。OP_FALSE(在比特幣腳本中也表示為“0”)確保腳本語言中的執行路徑永遠不會進入OP_IF分支,並保持未執行狀態。它充當腳本中的佔位符或空操作(No Operation),類似於高級語言中的“註釋”,來保證後續的代碼不被執行。

UTXO轉賬模型
以上都是從計算機數據結構方面來研究BTC的基本原理,我們再從金融模型方面來討論一下UTXO模型。
UTXO是 Unspent Transaction Outputs 的縮寫,中文翻譯是“沒有花掉的交易輸出”,實際可以理解為在一次轉賬時剩餘沒有轉出的資金。那比特幣為啥要使用這麼一個概念呢?這就要從記賬方法的賬戶交易模型和賬戶餘額模型說起了。
因為我們在中心化的體系待的太久,已經非常習慣賬戶餘額模型的記賬方式。當用戶A給用戶B轉100塊錢時,銀行會先檢查A的銀行賬戶上是否有100元,如果有就從A的賬戶里扣除100元再在B的賬戶上加上100元,這樣一筆轉賬就完成了。
然而,比特幣的記賬算法裡沒有餘額這個概念。在區塊鏈的分佈式賬本上記錄的只有一筆筆的交易,並不會直接記錄一個賬戶當前餘額是多少(記錄餘額一般需要專門的服務器節點來記錄,那就中心化了)。假設當前用戶A餘額是1000元,如果用戶A給用戶B轉100元,這筆轉賬會被記錄成:
交易1 用戶A給用戶B轉賬100元
交易2 用戶A給用戶A自己轉賬900元 (UTXO)

這裡的交易2雖然是一筆交易,但從功能上來說他擔當了賬戶餘額的作用,表示在完成這筆100元轉賬後A的賬戶上還剩餘900元。
那麼問題來了,為啥非要造一個這樣的UTXO呢?因為在BTC區塊鏈上只能記錄交易,沒法記錄賬戶餘額。如果沒有這個UTXO的話,要計算餘額需要把一個賬戶的所有交易的入賬和出賬全部累加一遍,這是個非常消耗時間和計算資源的事情。而UTXO的出現巧妙的避免了在計算餘額時要回溯所有交易的痛點問題。
UTXO 有個特點,就是跟硬幣一樣,不能掰開用,那麼交易過程中如何湊夠輸入金額,又如何找零的呢?我們可以用硬幣來做類比(實際上每次當你看到UTXO這個單詞的時候請自動翻譯成“硬幣”比較好)。
小明給小剛轉賬1比特幣。整個過程是這樣的,小明要收集足夠的input,比如小明的地址對應的以往交易中,找到了一個面值為0.9的 UTXO,不夠1比特幣,好在交易中是允許有多個輸入的,所以小明又找到了一個面值0.2的 UTXO,這樣在這次轉賬的交易中,就會有兩個輸入。同時輸出也會有兩個,一個是指向小剛地址,面值是1比特幣。另一個指向小明自己的地址,面值是0.1比特幣,這個輸出就是找零了(這個例子忽略了gas)。
換句話說,小明口袋裡面有兩個硬幣,一個面值0.9,另一個面值0.2,此時小明需要支付面值1的硬幣,就需要同時把這兩個硬幣遞給小剛,小剛收到後找零0.1給小明。所以這個記賬模型的本質就是通過“找零”的動作來避免了“計算餘額”。
Ordinal協議的排序系統
Ordinal協議可以說是本輪BTC生態爆發的源頭,是把同質化的BTC分解成最小單位sat,然後對每一個sat標記一個序號。那是怎麼做的呢?
我們知道,BTC的總量是2100萬枚,一枚BTC最小可以拆分到一億份(sat),所以BTC的最小單位就是sat,這些BTC也好,最小單位sat也好,都是典型的同質化代幣FT。我們現在試著給這些sats分配一個序號(ordinal)。
前面在談到區塊數據結構的時候,我們提到交易信息需要註明input的地址和數額以及output的地址和數額。而每個區塊是包含了兩部分交易:BTC出塊獎勵和轉賬的手續費。手續費交易必然有input和output,但出塊獎勵因為是憑空生成的BTC,無input地址,所以這個“input from”的字段是空白的,也叫做“coinbase交易”。BTC總量的2100萬枚都是來源於這個coinbase交易,也是所有區塊中交易列表排列在第一位的。
Ordinal協議規定如下:
-
編號:每一個sat以他們被開採出來的順序進行編號
-
轉移:按照先進先出規則,從交易的輸入轉移到輸出
第一條規則相對簡單,它決定了編號只能由挖礦獎勵中的coinbase交易生成。例如,若第一個區塊的挖礦獎勵為50個BTC,則第一個區塊會分配出[0;1;2;...;4,999,999,999]範圍的sats;第二個區塊獎勵也為 50 BTC 時,則第二個區塊會分配出[5,000,000,000;5,000,000,001;...;9,999,999,999]範圍的sats。

這裡比較難理解的部分在於,由於UTXO實際上包含很多個聰,那麼這個UTXO中的每一個聰看起來都一樣,怎麼給他們排序呢?這個實際上是第二條規則決定的,舉一個簡單的例子吧:
我先假設BTC的最小分割單位是1,總共出了10個區塊,每個區塊的出塊獎勵是10個BTC,即總量是100個。我們直接可以給這100個BTC賦予一個(0-99)的序號。如果沒有任何轉賬情況,那我們只知道第一個區塊的10個BTC編號是(0-9),第二個區塊的10個BTC編號是(10-19),一直到第十個區塊的10個BTC編號是(90-99)。這其中因為沒有任何花費,也就沒有任何output,我們就只能給每10個BTC賦予一個編號範圍。
假設在第二個區塊中加入兩個支出(output),一個是3BTC,一個是“找零”的7 BTC,對應於給別人轉賬了3個BTC,再給自己找零7個BTC。此時在區塊的交易列表中,假設給自己找零的7個BTC排名第一(對應的編號是10-16),給別人的3BTC排第二(對應的編號是17-19)。這就通過對output的的轉移確認了某個UTXO所包含sats的順序集合。
注意是每一個sat不是UTXO! 由於UTXO是不可再分的最小交易單元,因此sat只能存在於UTXO中,且UTXO包含了一定範圍的sats,且只能在花費某一UTXO後產生新的輸出中對sats編號進行拆分。
至於用什麼方式來表達這個“編號”,Ordinal支持多種形式,比如上面提到的“整數法”,其他還有十進制小數法,度數法,百分比法,純字母命名法。

sats有了統一的序號之後,就可以考慮銘刻了(inscription)了。我們在上文中提到,可以在見證數據區域4M大小的空間上傳任意數據類型的文件,不管是文本,還是圖片和視頻,上傳之後,文件會自動轉為16進制存放在的taproot腳本區。所以是,1個UTXO,對應1個Taproot腳本區,而這1個UTXO會同時包含很多sats(整體是一個sats序列集合,為了防止粉塵攻擊,限制單個 UTXO 中的比特幣數量不可少於 546 聰。)。Ordinal協議為了方便記錄,人為地規定“使用這個序列集合的第一個sat編號來代表綁定關係”(白皮書原話是第一個output的第一個聰的編號),比如包含(17-19)號sats的UTXO就直接用17號來代替這個集合和銘刻內容綁定。
Ordinal資產的鑄造和轉移
Ordinal NFT很顯然就是把各種文件上傳到隔離見證區的腳本中並與之綁定一個sats序列集合,從而實現了在BTC鏈上發行NFT資產。但是這裡還有一個問題,隔離見證區的腳本即包含input的解鎖腳本,又包含output的鎖定腳本,那麼內容是放在哪個腳本中呢?正確的答案是兩者都有。這裡不得不提到區塊鏈技術中的commit-reveal機制。
區塊鏈中的Commit-Reveal機制是一種用於確保信息公平和透明處理的協議。這個機制通常用在需要提交隱藏信息(如投票或競標),然後在以後的某個時間點揭示這些信息的場景中。Commit-Reveal機制分為兩個階段:提交(Commit)階段和揭示(Reveal)階段。
1. 提交(Commit)階段:在這個階段,用戶提交他們的信息(如投票選擇或競標價格),但這個信息是加密的。通常,用戶會生成這個信息的哈希值(即信息的加密摘要),然後將這個哈希值發送到區塊鏈上。由於哈希函數的特性,它們可以生成一個獨特的輸出(哈希值),這個輸出對於原始信息來說是不可逆的。這意味著無法從哈希值推斷出原始信息。這個過程確保了信息在提交時的保密性。
2. 揭示(Reveal)階段:在一個預定的以後時間,用戶必須揭示他們的原始信息,並證明它與之前提交的哈希值相匹配。這通常是通過提交原始信息以及用於生成哈希值的任何附加數據(如隨機數或“鹽”)來完成的。網絡然後驗證這個原始信息的哈希值是否與之前提交的哈希值相同。如果匹配,則原始信息被接受為有效。
我們前面講過,銘刻的內容是需要和UTXO包含的sats序列集合綁定一起,UTXO在區塊中是一個output,所以必須附著在output的鎖定腳本中。但是BTC的全節點需要在本地維護和傳輸全網絡所有的UTXO集合。想象一下,要是有1萬個4M的視頻文件直接上傳到1萬個UTXO的鎖定腳本,那所有的全節點需要有超高的存儲空間和超快的網速,可以說整個鏈直接就崩了。因此,唯一的解決方法是把內容放到input中的解鎖腳本,然後再讓這個內容“指向”到另一個output。
所以說Ordinal資產的鑄造是需要分為兩步(錢包是把這兩步進行合併處理了,在構造交易時,同時構造commit-reveal這個父子交易,用戶體驗上會感覺只有一個步驟並且節省了gas費)。
在鑄造階段,用戶首先需要上傳某個文件的哈希值到commit交易中(自己A地址給自己B地址轉賬)的UTXO中的鎖定腳本,因為是哈希值,所以不佔用過多全節點的UTXO數據庫空間。其次,用戶再構造一個新的交易(自己B地址給自己A地址轉賬),稱之為reveal交易,此時的input需要使用上一步commit交易中含有文件哈希值的那個UTXO,並且該input的解鎖腳本必須包含原始銘刻文件。用白皮書中的原話描述,就是“首先,在commit中,創建一個提交到包含銘文內容的腳本的taproot 輸出。 其次,在reveal交易中,使用commit交易產生的輸出,來顯示鏈上的銘文內容。”
在轉移階段,Ordinal NFT和 BRC20稍有不同,Ordinal NFT因為是整體轉移,只需要把綁定某個UTXO的NFT直接轉給接收者即可,類似於普通的BTC轉賬。但BRC20因為牽扯到自定義數額轉賬,同樣分為兩步,第一步叫銘刻“交易”(Inscribe "TRANSFER"),第二步叫轉賬“交易”(Transfer "TRANSFER"),第一步的銘刻交易實際類似於一個Ordinal NFT的鑄造過程,暗含了commit-reveral 父子交易對,第二步轉賬交易類似於一個普通的Ordinal NFT的轉賬,把綁定某個UTXO的BRC20資產直接轉給接收者。有的錢包會把這三個交易(父子孫三代交易)同時構建,從而節省時間和gas。

總結來說,commit交易用來把銘刻內容(原始內容的哈希值)和帶序號的sats(UTXO)綁定,reveal交易用來把內容顯示出來(原始內容)。這個父子交易對共同完成了對於NFT的鑄造。
P2TR與一個例子
上面關於鑄造的技術討論還沒完結,因為有人會好奇,reveal交易到底是如何驗證commit交易中的銘文信息呢?為啥構造交易的時候需要自己的AB兩個地址互相轉賬呢?打銘文的時候也沒看到需要準備兩個錢包啊。這裡就需要講到Taproot的重大升級之一P2TR了。
P2TR (Pay-to-Taproot)是由Taproot升級引入的一種新類型的比特幣交易。P2TR交易通過允許用戶使用單一公鑰或更復雜的腳本(如多重簽名錢包或智能合約)來花費比特幣,實現了更高的隱私和靈活性。這是通過使用Merkleized Abstract Syntax Trees(MAST)和Schnorr簽名來實現的,這些技術使得可以在單個交易中有效地編碼多種花費條件。
-
創建花費條件
要創建一個P2TR交易,用戶首先定義一個花費條件,例如單一公鑰或更復雜的腳本,指定了花費比特幣的要求(例如,多重簽名錢包或智能合約)。
-
生成Taproot輸出
然後,用戶生成一個Taproot輸出,其中包括一個單一公鑰(公鑰代表花費條件)。這個公鑰是從用戶的公鑰和腳本的哈希的組合中派生出來的,使用一種稱為“tweaking”的過程。這確保了輸出看起來像一個標準的公鑰,使其在區塊鏈上與其他交易難以區分。
-
花費比特幣
當用戶想要花費比特幣時,他們可以使用他們的單一公鑰(如果花費條件被滿足),或者透露原始腳本並提供必要的簽名或數據以滿足花費條件。這是通過使用Tapscript來完成的,它允許更高效和靈活地執行花費條件。
-
驗證交易
礦工和節點隨後通過檢查所提供的Schnorr簽名和數據與花費條件進行驗證交易。如果條件被滿足,交易被視為有效,比特幣可以被花費。
-
增強的隱私和靈活性
因為P2TR交易只在花費比特幣時透露必要的花費條件,所以它們保持了高水平的隱私。此外,使用MAST和Schnorr簽名使得能夠高效地編碼多個花費條件,允許更復雜和靈活的交易,而不會增加交易的總體大小。
以上就是commit-reveal機制在P2TR中的應用方式,我們以一個實際案例來做說明。
使用區塊鏈瀏覽器https://www.blockchain.com/我們來研究一個Ordinal 圖片NFT的鑄造過程,包括了之前的commit-reveal兩個階段。
首先,我們看到commit交易的Hash ID是(2ddf90ddf7c929c8038888fc2b7591fb999c3ba3c3c7b49d54d01f8db4af585c)。可以注意到,這筆交易的輸出不包含銘文數據(實際上放的是16機制圖片文件的哈希值),網頁中也沒有相關的銘文信息。這個輸出的(bc1p4mtc.....)地址其實是通過“tweaking”過程產生的臨時地址(代表了腳本解鎖條件的公鑰),和taproot主地址(bc1pg2mp...)共享一個私鑰。此交易中的第二個UTXO屬於返還的“找零”操作。如此就實現了銘文內容與第一個UTXO包含的sats的綁定。

接著,我們查看reveal交易的記錄,其Hash ID是(e7454db518ca3910d2f17f41c7b215d6cba00f29bd186ae77d4fcd7f0ba7c0e1)。在這裡,我們可以看到Ordinals inscription 的信息。這筆交易的input地址正是前一個交易產生的臨時輸出地址(bc1p4mtc.....),input的解鎖腳本則包含了原始圖片的16進制文件,而輸出的0.00000546BTC(546聰)則是將這個NFT發送到自己的taproot主地址(bc1pg2mp...)。基於First in First Out原則以及“綁定的是第一個output的第一個聰的編號”,雖然前後兩個UTXO包含的sats的數量有變化,但是綁定的sat序號不變。所以,我們可以在(sat 1893640468329373)中找到這個銘文所在的聰。
(https://ordinals.com/sat/1893640468329373)

這兩個交易(屬於父子交易)在鑄造時會同時由錢包提交給內存池,所以只需要花費一筆gas,也很大幾率是進入到同一個區塊中被礦工記錄並廣播(以上例子中的兩個交易正是同時存在於區塊790468中。)。礦工和節點隨後通過檢查reveal交易中的input所提供的Schnorr簽名以及16進製圖片的哈希值與commit交易中的output鎖定腳本中的16進製圖片哈希值進行驗證。如果兩者相同,交易被視為有效,這個比特幣的UTXO可以被花費,那麼這兩個交易自然就被永久記錄在BTC的區塊鏈數據庫中,NFT的圖片也自然被保存下來並顯示出來。如果兩個哈希值不同,兩個交易會被取消,銘刻失敗。
BRC20協議與索引器
對於Ordinal協議,我們銘刻一段文本,它就是文字NFT(對應以太坊上的Loot),銘刻一張圖片,它就是圖片NFT(對應於以太坊上的PFP),銘刻一段音樂,它就是音頻NFT。那如果我們銘刻一段代碼,並且這段代碼是一段“發行FT同質化代幣”的代碼呢?
BRC20正是通過利用 Ordinal 協議將inscriptions(銘文)設置為 JSON 數據格式來部署、鑄造和轉移 Token,JSON 包含一些代碼片段,描述 Token 的各種屬性,例如其供應量、最大鑄造單位和唯一代碼。我們在上一篇文章中已經講過,BRC20代幣的本質是半同質化代幣SFT,也就是說,在某些情況它可以當做NFT交易,某些情況可以當做FT交易,這種對“不同情況”的控制是如何辦到的呢?答案是索引器。
索引器其實是一個記賬人,用來把接收到的信息分門別類的記錄在數據庫裡。在Ordinal協議中,索引器通過對input和output的追蹤,來確定排序好的sats在不同地址中的變化。在BRC-20協議裡,索引器多了一個功能:記錄銘文中代幣餘額在不同地址的變化。
所以我們可以從記賬人的視角來看到不同的代幣存在形式:BRC20協議代幣其實存在於一個三重數據庫中。第一重Layer1,記賬人是BTC礦工,數據庫類型是“鏈式數據庫”,產生的BTC是FT資產。第二重layer2,記賬人是Ordinal索引器,數據庫類型是“關係型數據庫”,產生的帶序號的sats是NFT資產。第三重layer3,記賬人是BRC20索引器,數據庫類型是“關係型數據庫”,產生的BRC20資產是FT資產。當我們把BRC20按照“張”來算的時候,站的角度是ordinal索引器(由該索引器記錄),它自然是NFT;當我們把BRC20按照分拆好的“個”來思考的時候(尤其是充值到中心化交易所之後),站的角度是BRC20索引器(由該索引器記錄或者是中心化交易所的服務器記錄),它自然是FT。由此我們可以得到一個結論,半同質化代幣SFT的存在是因為有不同層級的記賬人導致的。
區塊鏈不就是一個分佈式數據庫嘛,所以才有了礦工這個記賬人群體來共同維護這個“鏈式數據庫”(因為只有鏈式數據庫才能做到真正的去中心化)。但兜兜轉轉,我們還是回到了中心化的“關係型數據庫”的老路。這也是為何前段時間Ordinal協議發起人,BRC20協議發起人,unisat錢包為了索引器是否要升級炒的不可開交的本質原因--記賬人意見不一致啦。
但是行業經過了十幾年的發展,還是積攢了不少“去中心化”的經驗,索引器可不可以用“鏈式數據庫”替代關係型數據庫?能不能採用欺詐證明或者ZKP來保證安全和去中心化?比特幣生態的DA需求會不會溢出到其他的DA從而促進多鏈生態繁榮和融合?我似乎看到了更多的可能性。
歡迎加入深潮 TechFlow 官方社群
Telegram 訂閱群:https://t.me/TechFlowDaily
Twitter 官方帳號:https://x.com/TechFlowPost
Twitter 英文帳號:https://x.com/BlockFlow_News














