我們採用「先簽好 spend transaction,到指定時間再廣播」的 time-lock 模型,並以 Rust BDK 實作。
- 資產在指定時間前無法被轉出
- spend transaction 可事先完成建構與簽章
- 就算提前廣播,也會因 time-lock 條件而被節點拒絕
- 到時間點後,由系統或使用者再進行 broadcast
只在交易層加 nLockTime 並不足以避免同一筆 UTXO 被其他交易提前花掉。為了讓「時間未到前任何交易都無法花掉這筆錢」,我們先把資產轉入一個帶 CLTV timelock 的 vault script:
- 先將資產轉入帶 CLTV timelock 的 vault UTXO
- vault UTXO 在時間未到前「任何交易都無法花」
- 預簽的 spend transaction 只能在 locktime 到期後才會被接受
wsh(and_v(v:pk(A), after(T)))
A:簽章公鑰T:locktime(block height 或 timestamp)
- 建立 vault wallet(descriptor wallet)
- 使用 timelock script descriptor
- 同步後取得 vault UTXO
- 建立 spend transaction
- 指定收款地址與金額
- 設定
nLockTime = T - 所有 inputs 的
sequence < 0xffffffff(例如0xffff_fffe),否則 locktime 無效 - 設定 fee(固定 fee 或啟用 RBF)
- 使用 BDK 簽章
- 產生並簽好 PSBT
- extract 成 raw transaction hex
- 將已簽名交易與 metadata 存起來
- 到時間點後 broadcast
- 使用 Electrum / RPC 將 raw tx 廣播
- locktime 前廣播會被拒絕
- locktime 到期後才會進 mempool / 上鏈
nLockTime與 miniscript 的after(T)一致(同為 height 或 timestamp)- 每個 input 的
sequence必須< 0xffffffff(常用0xffff_fffe) - 若使用 RBF:
sequence <= 0xfffffffd(並確保你的錢包/節點策略允許) - 明確指定要花的 vault UTXO(coin selection / coin control)
- 確認簽章輸出可重現:保存
psbt(含 inputs/witness)或保存raw_tx_hex
vault_outpoint (txid:vout)
locktime_T (height|timestamp)
to_address, amount_sat
fee_rate_or_fee_sat
raw_tx_hex (或 psbt_base64)
created_at, broadcast_after
- Hackathon / demo:建議用 block height(測試穩定)
- 正式產品:可考慮 timestamp(需注意 CLTV 與 MTP 等規則)
預簽交易可能在到期時 fee 不足。
可選策略(擇一或組合):
- 保守估 fee(提高成功率、但成本較高)
- RBF(讓到期時可用更高手續費替換)
- CPFP(由收款方或系統用子交易補 fee)
- 建議使用 coin control,明確指定 vault UTXO
- 避免 wallet 後續行為導致 double-spend / 交易無法上鏈
- 私鑰可放在離線 signer
- 線上系統只保存已簽名交易(raw tx / PSBT + metadata)
- 到時間僅負責 broadcast,不具備簽章能力
使用 vault timelock script + 預簽 locktime spend transaction,確保資產在時間到之前無法被任何交易花掉,並在時間到後以已簽名交易完成轉出。
- Electrum:
broadcast <raw_tx_hex> - Bitcoin Core RPC:
sendrawtransaction <raw_tx_hex> - 若提前廣播失敗,常見回應包含 locktime / non-final(屬預期行為)
*** Delete File: timelock-presigned-bdk.md