跳至主要內容
進階

Median Time Past

了解 MTP(中位時間過去)如何用於時間鎖驗證,防止礦工操縱時間戳。

8 分鐘

中位時間過去(Median Time Past, MTP)是 BIP-113 引入的時間計算方法, 使用過去 11 個區塊時間戳的中位數來判斷時間鎖,防止礦工通過操縱時間戳來繞過時間鎖。

為什麼需要 MTP?

區塊時間戳的問題:

礦工可以在一定範圍內設置時間戳:
- 必須 > 前一區塊的 MTP
- 必須 < 當前時間 + 2 小時

攻擊場景(無 MTP):
1. Alice 發送時間鎖交易: nLockTime = T
2. 礦工想提前確認這筆交易
3. 礦工設置區塊時間戳 = T(即使實際時間 < T)
4. 交易被提前確認,時間鎖失效

MTP 解決方案:
- 使用過去 11 個區塊的中位時間
- 礦工需要操縱 6+ 個區塊才能影響 MTP
- 實際上不可行

MTP 計算

計算公式:

MTP = median(block[n-1], block[n-2], ..., block[n-11])

範例:
假設區塊 100-110 的時間戳:
[1000, 1005, 1003, 1010, 1008, 1015, 1012, 1020, 1018, 1025, 1022]

排序後:
[1000, 1003, 1005, 1008, 1010, 1012, 1015, 1018, 1020, 1022, 1025]

MTP = 第 6 個值 = 1012

Python 實現:
def get_median_time_past(block_index):
    timestamps = []
    current = block_index
    for _ in range(11):
        if current is None:
            break
        timestamps.append(current.timestamp)
        current = current.prev

    timestamps.sort()
    return timestamps[len(timestamps) // 2]

時間鎖驗證

BIP-113 之前 vs 之後:

BIP-113 之前:
if tx.nLockTime >= 500000000:  # 時間模式
    if block.timestamp < tx.nLockTime:
        reject("Time lock not satisfied")

BIP-113 之後:
if tx.nLockTime >= 500000000:  # 時間模式
    if MTP < tx.nLockTime:
        reject("Time lock not satisfied")

區別:
- 之前: 使用區塊時間戳(可被操縱)
- 之後: 使用 MTP(更安全)

// BIP-113 在 BIP-68/112/113 軟分叉中一起啟用
// 高度 419328 (2016 年 7 月)

對時間鎖的影響

MTP 導致的時間偏移:

特點:
- MTP 通常比實際時間晚 1-2 小時
- 因為它是過去區塊的中位數

影響:
- 時間鎖會比預期晚生效
- 設置 nLockTime 時需要考慮這個偏移

範例:
現在時間: 2024-01-01 12:00:00
當前 MTP: 2024-01-01 10:30:00 (約晚 1.5 小時)

如果設置 nLockTime = 2024-01-01 12:00:00:
- 交易實際要到 ~13:30 才能確認
- 因為 MTP 需要時間追上

建議:
- 時間鎖減去 2 小時作為緩衝
- 或使用區塊高度模式(更可預測)

區塊時間戳規則

區塊時間戳的共識規則:

下限:
- 必須 > MTP
- 否則區塊無效

上限:
- 必須 < 當前網路時間 + 2 小時
- 網路時間 = 節點時間 + 對等節點偏移

有效範圍:
MTP < timestamp < now + 2h

範例:
MTP = 1704067200 (2024-01-01 00:00:00)
當前時間 = 1704070800 (2024-01-01 01:00:00)

有效範圍: 1704067201 - 1704078000
(00:00:01 - 03:00:00)

// 礦工通常使用當前時間
// 但可以在範圍內自由設置

查詢 MTP

# RPC 命令

# 查看區塊的 MTP
bitcoin-cli getblockheader <hash>
{
  ...
  "time": 1704067200,       # 區塊時間戳
  "mediantime": 1704063600, # MTP
  ...
}

# 查看當前鏈的 MTP
bitcoin-cli getblockchaininfo | jq '.mediantime'

# 計算時間差
bitcoin-cli getblockheader $(bitcoin-cli getbestblockhash) | \
  jq '.time - .mediantime'
# 通常是 3600-7200 秒 (1-2 小時)

# 查看最近 11 個區塊的時間戳
for i in {0..10}; do
  hash=$(bitcoin-cli getblockhash $(($(bitcoin-cli getblockcount) - i)))
  bitcoin-cli getblockheader $hash | jq '.time'
done

在腳本中的使用

OP_CHECKLOCKTIMEVERIFY (CLTV):

腳本:
<locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP ...

驗證過程:
1. 檢查 tx.nLockTime >= script_locktime
2. 如果是時間模式 (>= 500000000):
   - 檢查 MTP >= script_locktime
3. 交易被包含在區塊中

OP_CHECKSEQUENCEVERIFY (CSV):

相對時間鎖也使用 MTP:
- 計算輸入 UTXO 的確認時間
- 使用 MTP 差值而非區塊時間戳差值

def check_sequence_time(input_height, current_height):
    input_mtp = get_mtp(input_height)
    current_mtp = get_mtp(current_height)
    elapsed = current_mtp - input_mtp
    # elapsed 用於與 nSequence 比較

歷史背景

BIP-113 的歷史:

問題發現:
- 礦工可以操縱時間戳
- 時間鎖安全性受威脅
- 影響閃電網路等協議

解決方案:
- 使用中位時間而非單一時間戳
- 11 個區塊提供足夠的安全邊際
- 需要控制多數算力才能操縱

啟用:
- 與 BIP-68 (相對時間鎖) 一起啟用
- 與 BIP-112 (OP_CSV) 一起啟用
- 2016 年 7 月,高度 419328

// 11 這個數字的選擇:
// - 奇數,確保中位數唯一
// - 大約 2 小時的區塊
// - 足夠防止操縱,又不會太滯後

對應用的影響

  • 閃電網路:HTLC 超時使用 MTP,更安全
  • 時間鎖合約:需要考慮 MTP 延遲
  • 繼承方案:實際等待時間比設定稍長
  • 原子交換:超時計算需要考慮 MTP

安全考量

MTP 的安全邊際:

要操縱 MTP,攻擊者需要:
1. 控制 6 個以上的最近區塊
2. 設置較低的時間戳
3. 這需要約 50% 的算力

實際風險:
- 51% 攻擊者有更多有利可圖的攻擊
- 操縱 MTP 收益有限
- 會被網路檢測到

殘餘風險:
- 礦工可以輕微延遲 MTP
- 但無法大幅提前
- 對大多數應用影響有限

最佳實踐:
- 重要合約使用足夠的安全邊際
- 考慮 MTP 可能的延遲
- 高價值交易等待更多確認

相關概念

  • Timelock:時間鎖概述
  • OP_CLTV:絕對時間鎖操作碼
  • OP_CSV:相對時間鎖操作碼
  • nSequence:序列號和相對鎖
  • Block Header:區塊頭結構
已複製連結
已複製到剪貼簿