Bitcoin Script 實戰教學(五):時間鎖機制完全指南
系列導言
這是「Bitcoin Script 實戰教學」系列的第五篇。本篇將深入探討時間鎖機制——比特幣腳本中控制「何時」可以花費資金的強大工具。時間鎖是構建支付通道、閃電網路、原子交換等進階應用的基礎技術,理解它對於掌握比特幣智能合約至關重要。
系列文章:
- 基礎概念與操作碼
- 標準腳本類型詳解
- SegWit 與 Taproot
- 多重簽名完全指南
- 時間鎖機制完全指南(本篇)
- 進階應用與智能合約
一、理解時間鎖
1.1 什麼是時間鎖?
在日常生活中,我們經常遇到時間相關的限制:銀行定存有到期日、支票可以開立遠期支票、遺囑在當事人去世前無效。時間鎖(Timelock)在比特幣中實現了類似的概念:它允許我們設定條件,使得資金只能在特定時間之後才能被花費。
這個看似簡單的功能,實際上是構建複雜金融應用的基石。想像一下:如果沒有時間鎖,就無法實現「如果三天內交易沒有完成,資金自動退回」這樣的條件。而這恰恰是跨鏈原子交換和閃電網路的核心需求。
比特幣支援兩種基本類型的時間鎖:絕對時間鎖指定一個具體的時間點或區塊高度,資金在此之前無法花費;相對時間鎖則指定一個時間長度,從 UTXO 被創建(確認)後開始計算,經過這段時間後資金才能花費。
1.2 時間的表示方式
比特幣的時間鎖使用一個巧妙的機制來區分區塊高度和 Unix 時間戳:如果數值小於 5 億(500,000,000),它被解釋為區塊高度;如果大於或等於 5 億,它被解釋為 Unix 時間戳。
選擇 5 億作為分界點是經過深思熟慮的。比特幣大約每 10 分鐘產生一個區塊,按此速度計算,達到 5 億區塊需要大約 9,500 年。而 5 億秒作為 Unix 時間戳,對應的是 1985 年 11 月——這顯然已經過去了。因此,這個分界點在可預見的未來都不會造成歧義。
區塊高度作為時間度量有其獨特優勢:它不受時區影響,且與比特幣網路的實際進展直接相關。但區塊產生時間有波動(可能 5 分鐘也可能 20 分鐘),所以對於需要精確時間的場景,時間戳可能更合適。
值得注意的是,比特幣的時間戳驗證不使用當前時間,而是使用「中位時間過去」(Median Time Past,MTP)——過去 11 個區塊時間戳的中位數。這個設計防止了礦工通過操縱單個區塊的時間戳來影響時間鎖。
1.3 四種時間鎖機制
比特幣實現了四種不同層級的時間鎖機制,可以按照兩個維度進行分類。
按時間類型分:nLocktime 和 CLTV 是絕對時間鎖,指定具體的時間點或區塊高度;nSequence 和 CSV 是相對時間鎖,指定從 UTXO 確認後經過的時間。
按實現層級分:nLocktime 和 nSequence 是交易層級的機制,它們影響整個交易何時可以被打包進區塊;CLTV 和 CSV 是腳本層級的機制,它們作為腳本中的操作碼,在花費時驗證時間條件。
這四種機制各有其設計目的和適用場景,接下來我們將逐一深入探討。
二、nLocktime:交易層級絕對時間鎖
2.1 設計與歷史
nLocktime 是比特幣最早的時間鎖機制,從創世區塊就存在。它是交易結構的一部分,佔據交易末尾的 4 個位元組。這個欄位的設計初衷是實現一種稱為「高頻交易通道」的功能——允許雙方在鏈下快速更新交易,但這個機制從未完整實現。
nLocktime 的工作原理相當直接:如果設置了一個非零的 nLocktime 值,這筆交易在指定的時間或區塊高度之前,不會被礦工打包進區塊,也不會被節點接受進記憶池。
2.2 啟用條件
nLocktime 的啟用需要一個額外條件:交易中至少有一個輸入的 sequence 欄位不等於 0xFFFFFFFF(最大值)。這個設計源於 sequence 欄位的原始用途——允許透過遞增 sequence 來更新未確認交易。當所有輸入的 sequence 都是最大值時,表示交易是「最終的」,nLocktime 將被忽略。
這個機制在實踐中造成了一些混淆。許多錢包軟體在不需要時間鎖時會將 sequence 設為 0xFFFFFFFF,此時即使設置了 nLocktime 也不會生效。如果你要使用 nLocktime,確保至少一個輸入的 sequence 小於最大值——通常使用 0xFFFFFFFE。
2.3 實際應用與限制
nLocktime 最常見的應用是創建「延遲支付」——一筆在未來某個時間點才生效的交易。例如,你可以創建一筆 nLocktime 設為一週後的交易,作為對某人的承諾,但保留在此之前取消的能力。
然而,nLocktime 有一個根本性的限制:它只是「紳士協定」。持有私鑰的人可以隨時創建一筆新交易(沒有時間鎖),花費相同的輸入。nLocktime 交易要等到解鎖時間才能廣播,而在此之前,發送者完全可以反悔。
這個限制意味著 nLocktime 不適合需要強制執行的場景。接收者無法確信資金真的會在未來到達——發送者可能在到期前花掉這些幣。對於這些場景,我們需要腳本層級的時間鎖。
2.4 記憶池行為
另一個實用性問題是記憶池的處理方式。目前的比特幣節點不會接受 nLocktime 尚未解鎖的交易進入記憶池。這意味著你不能提前將交易發送到網路上「等待」——你需要自己保管這筆交易,直到時間到了再廣播。
這對用戶體驗有明顯影響:你必須確保在正確的時間上線並廣播交易,或者依賴第三方服務來代為執行。
三、OP_CHECKLOCKTIMEVERIFY(CLTV)
3.1 從交易層級到腳本層級
CLTV(Check Lock Time Verify)透過 BIP 65 在 2015 年底引入,填補了 nLocktime 留下的空白。與 nLocktime 不同,CLTV 是一個腳本操作碼,在腳本執行時強制檢查時間條件。
CLTV 的設計非常精巧:它讀取堆疊頂端的值作為時間鎖,然後驗證花費這筆輸出的交易的 nLocktime 是否滿足條件。注意,CLTV 本身不消耗堆疊上的值——這是為了兼容性考慮。因此,使用 CLTV 後通常需要 OP_DROP 來清理堆疊。
3.2 執行規則
CLTV 的驗證邏輯檢查幾個條件。首先,堆疊頂端的值必須是非負數。其次,堆疊頂端的值和交易的 nLocktime 必須使用相同的時間類型——兩者都小於 5 億(區塊高度)或都大於等於 5 億(時間戳)。混合使用會導致腳本失敗。
最關鍵的檢查是:交易的 nLocktime 必須大於或等於腳本中指定的值。這確保了交易至少要等到腳本規定的時間才能被確認。
此外,CLTV 還要求花費這個輸出的輸入的 sequence 不能是 0xFFFFFFFF,因為那會禁用 nLocktime 機制。
3.3 為什麼 CLTV 無法繞過
CLTV 解決了 nLocktime 的「可繞過」問題。一旦資金被發送到包含 CLTV 的腳本地址,就沒有任何方法可以在指定時間之前花費它——即使所有相關私鑰的持有者都同意也不行。
這是因為 CLTV 條件被編碼在腳本中,而腳本是輸出的一部分。花費這個輸出的任何交易都必須滿足腳本條件,沒有例外。唯一的方法是等待時間到期。
3.4 應用場景:遺產規劃
CLTV 的經典應用之一是遺產規劃。考慮這樣的場景:Alice 想要確保如果她發生意外,她的比特幣可以被她的女兒 Carol 繼承。但她不想現在就把控制權交給 Carol。
使用 CLTV,Alice 可以創建一個這樣的腳本:正常情況下 Alice 可以用自己的簽名花費;但如果腳本中的時間條件滿足(比如五年後),Carol 也可以用她的簽名花費。Alice 可以每隔幾年將資金轉移到新的腳本(更新時間鎖),只要她仍然健在。
這個方案不需要任何信任的第三方,不需要律師或託管服務。時間鎖由比特幣協議本身強制執行。
3.5 應用場景:分期解鎖
另一個常見應用是分期解鎖,用於員工股權激勵或投資者鎖倉。假設公司承諾給員工分四年發放比特幣獎勵,每年解鎖 25%。
公司可以創建四個獨立的 UTXO,每個包含總獎勵的 25%,並設置不同的 CLTV 時間鎖:第一個一年後解鎖,第二個兩年後,依此類推。這樣員工可以確信公司無法撤銷承諾(除非員工把私鑰給了公司),而公司也知道員工不能提前取走資金。
四、nSequence:相對時間鎖基礎
4.1 從失敗的設計到重獲新生
nSequence 欄位的歷史頗為曲折。在比特幣最初的設計中,中本聰構想了一種機制:未確認交易可以透過提高 sequence 數字來「更新」。較高 sequence 的交易會取代較低的版本,而礦工會優先打包 sequence 最大的交易。
這個設計從未正確實現,原因是它存在嚴重的激勵問題。礦工沒有義務遵守這個規則——他們可以打包任何有效交易,包括低 sequence 但高手續費的版本。這讓整個機制變得不可靠。
多年來,sequence 欄位基本上被忽略,大多數錢包將其設為最大值。直到 2016 年,BIP 68 重新定義了這個欄位的語義,賦予它相對時間鎖的新功能。
4.2 BIP 68 的語義定義
BIP 68 對 sequence 欄位進行了精確的位元定義。最高位(bit 31)是禁用標誌:如果設置,這個輸入不受相對時間鎖限制。第 22 位是類型標誌:如果設置,時間鎖以 512 秒為單位計算;否則以區塊數計算。最低 16 位(bits 0-15)是實際的鎖定值。
這個設計允許的最大相對時間鎖是 65,535 個區塊(約 455 天)或 65,535 × 512 秒(約 388 天)。對於大多數應用場景,這已經足夠了。
選擇 512 秒(約 8.5 分鐘)作為時間單位而不是秒,是為了匹配比特幣的區塊時間粒度。這也意味著時間鎖的精度大約是 8 分鐘,對於大多數場景來說足夠精確。
4.3 相對時間鎖的含義
與絕對時間鎖不同,相對時間鎖的起點是 UTXO 被確認的時間,而不是某個固定時間點。這有重要的實際意義:你不需要預先知道 UTXO 何時會被創建,只需要指定「創建後需要等待多久」。
考慮一個支付通道的關閉場景:當一方單方面廣播關閉交易時,另一方需要時間來檢測和回應。相對時間鎖確保無論通道何時關閉,都有固定的「挑戰期」。如果使用絕對時間鎖,你需要在通道開設時就預測可能的關閉時間,這是不切實際的。
4.4 與 nLocktime 的交互
nSequence 的相對時間鎖與 nLocktime 的絕對時間鎖可以同時使用。規則是:一個交易要被接受,必須同時滿足 nLocktime 條件和所有輸入的 nSequence 條件。
這創造了一些有趣的可能性。例如,一個交易可以被設計成「在 2025 年 1 月 1 日之後,且在輸入被創建一週後」才能被確認。兩個條件都必須滿足。
五、OP_CHECKSEQUENCEVERIFY(CSV)
5.1 將相對時間鎖帶入腳本
CSV(Check Sequence Verify)透過 BIP 112 引入,是相對時間鎖在腳本層級的實現。與 CLTV 類似,CSV 讀取堆疊頂端的值,並驗證花費交易的對應輸入的 sequence 是否滿足條件。
CSV 需要交易版本至少為 2。這是因為 BIP 68 的 sequence 語義只適用於版本 2 及以上的交易,舊版本交易的 sequence 欄位沒有時間鎖含義。
5.2 執行規則
CSV 的驗證邏輯首先檢查堆疊值的禁用位(bit 31)。如果設置了禁用位,CSV 成為無操作——直接成功,不做任何檢查。這允許腳本在某些條件下「跳過」時間鎖。
如果禁用位未設置,CSV 驗證:交易版本至少為 2;輸入的 sequence 禁用位未設置;堆疊值和 sequence 使用相同的時間類型(都是區塊或都是時間);sequence 的鎖定值大於或等於堆疊值的鎖定值。
5.3 支付通道中的核心作用
CSV 在支付通道和閃電網路中扮演著核心角色。讓我們看看為什麼。
在雙向支付通道中,雙方可以離線更新通道狀態(餘額分配)。但這創造了一個問題:任何一方都可以廣播舊的狀態,試圖獲取比應得更多的資金。這被稱為「通道關閉欺詐」。
解決方案是使用 CSV 創建「挑戰期」。當一方廣播關閉交易時,他們的資金不能立即提取,必須等待一段時間(例如一週)。在這段時間內,另一方可以提交證據證明這是舊狀態,並取走所有資金作為懲罰。
這個「撤銷」機制的關鍵在於相對時間鎖:無論何時關閉通道,都有固定的挑戰期。如果使用絕對時間鎖,長期運行的通道將面臨時間鎖過期的問題。
5.4 可撤銷輸出腳本
閃電網路的承諾交易使用一種稱為「可撤銷輸出」的模式。當通道更新時,雙方交換前一狀態的「撤銷密鑰」。如果任何一方廣播舊狀態,對方可以使用撤銷密鑰立即取走所有資金。
這個腳本通常這樣設計:有兩條花費路徑。第一條是撤銷路徑——對方可以用撤銷密鑰簽名,立即花費。第二條是延遲路徑——擁有者需要等待 CSV 延遲期,然後用自己的簽名花費。
如果廣播的是最新狀態,對方沒有撤銷密鑰,只能等待延遲期結束後資金被正當領取。如果廣播的是舊狀態,對方持有撤銷密鑰,可以在延遲期內取走所有資金。這創造了強大的不作弊激勵。
六、HTLC:時間鎖與哈希鎖的結合
6.1 核心概念
HTLC(Hash Time Locked Contract,哈希時間鎖定合約)是時間鎖最重要的應用之一,結合了哈希鎖和時間鎖,創造出一種條件支付機制:收款人必須在限定時間內提供一個秘密(原像),否則資金會退回給付款人。
HTLC 的結構包含兩條路徑。成功路徑要求收款人提供一個值 preimage,使得 HASH160(preimage) 等於預設的 payment_hash,同時提供有效簽名。退款路徑允許付款人在時間鎖過期後,用自己的簽名取回資金。
這個機制的精妙之處在於:付款人創建 HTLC 時並不知道原像,只有收款人知道。收款人透過揭示原像來「領取」支付,而這個揭示動作是公開的——任何看到原像的人都可以用它來解鎖其他使用相同 payment_hash 的 HTLC。
6.2 原子交換
HTLC 最早的應用之一是跨鏈原子交換。假設 Alice 有 BTC 想要換取 Bob 的 LTC。傳統方法需要信任:誰先發送誰就面臨對方不履約的風險。HTLC 消除了這種信任需求。
流程如下:Alice 首先生成一個隨機的 preimage 並計算 payment_hash。她在比特幣鏈上創建一個 HTLC,將 BTC 鎖定給 Bob,條件是 Bob 需要提供這個 preimage。Alice 設置較長的超時時間,比如 48 小時。
Bob 看到 Alice 的 HTLC 後,在萊特幣鏈上創建對應的 HTLC,將 LTC 鎖定給 Alice,使用相同的 payment_hash。Bob 設置較短的超時時間,比如 24 小時。
現在 Alice 可以安全地領取 LTC:她用 preimage 解鎖 Bob 在萊特幣鏈上的 HTLC。這個動作會將 preimage 公開在萊特幣鏈上。
Bob 從萊特幣鏈上看到 preimage,用它來解鎖 Alice 在比特幣鏈上的 HTLC,獲取 BTC。
如果 Alice 不領取 LTC(可能她改變主意了),兩個 HTLC 都會超時:Bob 取回 LTC,Alice 取回 BTC。沒有人損失資金。
超時時間的差異很重要:Alice 必須有足夠時間在 Bob 取回 LTC 之前領取它。如果兩個超時相同,Bob 可能在 Alice 領取 LTC 的同時取回它,造成 Alice 揭示了 preimage 卻沒有拿到 LTC,而 Bob 用這個 preimage 拿走 BTC。
6.3 閃電網路支付
閃電網路將 HTLC 的原理發揮到極致。在閃電網路中,支付可以透過多個節點中繼,每一跳都使用 HTLC 來保證原子性——要麼整條路徑上的所有 HTLC 都被解鎖,要麼都超時退回。
假設 Alice 要透過 Bob(路由節點)支付給 Carol。首先,Carol 生成 preimage 和 payment_hash,將 payment_hash 告訴 Alice。Alice 在她和 Bob 的通道中創建一個 HTLC(支付給 Bob,條件是 Bob 提供 preimage)。Bob 在他和 Carol 的通道中創建對應的 HTLC(支付給 Carol,同樣的 payment_hash)。Carol 用 preimage 領取 Bob 的 HTLC。Bob 從 Carol 那裡獲得 preimage,用它領取 Alice 的 HTLC。
整個過程中,只有最終收款人 Carol 知道 preimage。當她揭示它時,支付沿著路徑回溯結算。每個節點都有足夠的時間完成自己的部分,因為超時時間沿著路徑遞減。
6.4 超時差與安全性
HTLC 路由中的超時差(cltv_expiry_delta)是關鍵的安全參數。每個路由節點需要確保自己有足夠時間:在上游 HTLC 超時之前,有時間發現下游 HTLC 被領取(或超時),並相應地處理上游 HTLC。
如果超時差太小,可能發生這樣的情況:上游 HTLC 即將超時,付款人廣播退款交易。同時,下游收款人廣播領取交易。中間節點可能既無法領取上游(因為超時了)也無法阻止下游(因為已經成功),導致損失。
閃電網路的 BOLT 規範建議每跳至少 40 個區塊(約 6.7 小時)的超時差,以確保在各種網路延遲和區塊重組情況下的安全性。
七、時間鎖的安全考量
7.1 時間戳操縱
礦工對區塊時間戳有一定的調整空間。規則允許時間戳比前一區塊向前最多 2 小時,且必須大於 MTP。這意味著單個礦工可以將時間戳設得比真實時間略早或略晚。
對於使用時間戳的時間鎖來說,這創造了一些不確定性。一個設定在特定時間解鎖的 HTLC,實際解鎖時間可能比預期早或晚幾個小時。對於高價值或時間敏感的應用,使用區塊高度通常更可預測。
MTP 機制減輕了這個問題的嚴重性。操縱 MTP 需要控制連續多個區塊的時間戳,這對於沒有大量算力的攻擊者來說是不切實際的。
7.2 費用炒作攻擊
在時間鎖即將到期時,可能出現費用競爭的情況。考慮一個 HTLC:收款人想要領取,而付款人想要退款。如果兩個交易在接近超時時同時廣播,誰的交易被打包取決於手續費。
這可能導致「費用炒作」——雙方不斷提高手續費試圖讓自己的交易被優先打包。在極端情況下,手續費可能接近甚至超過 HTLC 的價值。
解決方案是設計合理的超時差,確保一方有明確的時間窗口來行動。此外,一些協議使用「預簽名」交易,在 HTLC 創建時就鎖定手續費率,避免後續競價。
7.3 區塊鏈重組風險
區塊鏈重組可能影響時間鎖的行為。如果一個 HTLC 被領取的交易所在區塊被重組掉,preimage 的揭示可能被「逆轉」。對於跨鏈原子交換,這創造了風險:一條鏈上的支付被確認(多次),而另一條鏈發生重組。
緩解措施包括:等待足夠多的確認(特別是對於高價值交易);設計超時時間時考慮可能的重組;在關鍵操作前檢查對手方鏈的確認深度。
八、Taproot 時代的時間鎖
8.1 隱私提升
Taproot 為時間鎖應用帶來了顯著的隱私改進。傳統的 HTLC 在花費時會揭示整個腳本結構,觀察者可以清楚看到這是一個條件支付。而在 Taproot 中,時間鎖條件可以隱藏在腳本樹中。
如果大多數情況下雙方合作(例如閃電網路通道正常關閉),他們可以使用 key path 花費——在鏈上看起來就像普通的單簽名交易。時間鎖路徑只有在需要時才會揭示,而且只揭示使用的那個分支,其他備用路徑保持完全隱藏。
這意味著大多數閃電網路交易在鏈上將無法與普通交易區分,大幅提升整個網路的隱私性。
8.2 效率優化
除了隱私,Taproot 還帶來效率提升。傳統 HTLC 需要在每次花費時揭示完整腳本,包括所有公鑰、哈希值、時間鎖等。Taproot 的 key path 只需要一個 64 位元組的簽名,是可能的最小見證大小。
即使使用 script path,也只需要揭示實際使用的腳本分支和 Merkle 證明,而不是整個腳本。對於有多個條件分支的複雜合約,這可以節省大量空間。
8.3 PTLCs:超越 HTLC
Taproot 還使得 PTLC(Point Time Locked Contract)成為可能,這是 HTLC 的進化版本。PTLC 使用「adaptor signatures」代替哈希鎖,提供了幾個重要優勢。
首先是隱私。HTLC 的 payment_hash 在整條支付路徑上是相同的,這創造了關聯性——知道入口和出口 payment_hash 相同的觀察者可以追蹤支付。PTLC 在每一跳使用不同的密碼學「謎題」,打斷了這種關聯。
其次是多路徑支付的原子性。將大額支付分成多條路徑時,HTLC 方案中的 preimage 一旦在任何路徑上揭示,就可能被用來領取其他路徑。PTLC 可以設計成真正原子的多路徑支付。
PTLC 的廣泛採用需要閃電網路生態的升級,這正在逐步進行中。
九、實作練習
練習 1:CLTV 遺產腳本
設計一個遺產規劃腳本:擁有者可以隨時花費,繼承人在指定日期後可以花費。計算正確的 CLTV 值(使用 Unix 時間戳),構建完整的 witness script,生成 P2WSH 地址。
練習 2:CSV 支付通道
設計一個簡化版的支付通道關閉腳本:雙方可以合作即時關閉(2-of-2 多簽),發起者需要等待 1008 區塊後才能單方面關閉。思考為什麼發起者需要延遲而接收者不需要。
練習 3:完整 HTLC 生命週期
模擬一個 HTLC 的完整生命週期:生成 preimage 和 payment_hash,構建 HTLC 腳本,模擬成功領取路徑的見證構造,模擬超時退款路徑的見證構造。比較兩種路徑的不同要求。
十、小結
本篇我們深入探討了比特幣的時間鎖機制:
nLocktime 是最早的時間鎖,在交易層級工作,但可以被持有私鑰者繞過。它主要用於「延遲」交易廣播,而非強制執行時間條件。
CLTV 將絕對時間鎖帶入腳本層級,透過共識規則強制執行,無法繞過。它適用於需要指定具體日期或區塊高度的場景,如遺產規劃、分期解鎖等。
nSequence 經過 BIP 68 重新定義,實現了交易層級的相對時間鎖。它指定從 UTXO 確認開始必須等待的時間,是支付通道的基礎。
CSV 是相對時間鎖的腳本版本,同樣透過共識強制執行。它是閃電網路中「挑戰期」機制的核心,確保欺詐行為有時間被發現和懲罰。
HTLC 結合了哈希鎖和時間鎖,創造了條件支付的能力。它是原子交換和閃電網路多跳支付的基礎,確保「要麼全部成功,要麼全部退款」的原子性。
時間鎖從單純的「延遲」功能,演進成構建複雜金融協議的核心原語。理解這些機制如何工作,是掌握比特幣智能合約的關鍵一步。
下一篇預告
在本系列的最後一篇文章中,我們將探討 Bitcoin Script 的進階應用,包括 Vault 設計模式、Discreet Log Contracts(DLC)的基礎概念、以及腳本優化技巧。我們還會展望比特幣腳本未來可能的發展方向,如 OP_CAT、OP_CTV 等提案。
參考資料
- BIP 65: OP_CHECKLOCKTIMEVERIFY - CLTV 的原始規格
- BIP 68: Relative Lock-time - nSequence 相對時間鎖語義
- BIP 112: OP_CHECKSEQUENCEVERIFY - CSV 的原始規格
- BIP 113: Median Time-past - MTP 時間計算規則
- BOLT #3: Transactions - 閃電網路交易格式
- BOLT #5: On-chain Handling - 閃電網路鏈上處理建議
- Bitcoin Optech: Timelocks - 時間鎖主題深入解析
Cypherpunks Taiwan
密碼學使自由和隱私再次偉大。Cryptography makes freedom and privacy great again.