如何計算 OP_CHECKMULTISIG 的下標?
我正在嘗試通過實現它來學習比特幣協議,並且我已經開始
OP_CHECKSIG
工作了(按照<https://en.bitcoin.it/wiki/OP_CHECKSIG>中的說明),但我無法獲得OP_CHECKMULTISIG
用於傳遞的交易輸入簽名驗證。具體來說,我堅持交易eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb的第二個 tx_input ,我懷疑罪魁禍首是下標(在目前交易中替換簽名腳本的那個)。
這是在我的實現中計算下標的方式。
首先,通過連接生成腳本
[sigScript][OP_CODESEPARATOR][pubkeyScript]
(我知道腳本執行是從這裡演變而來的)。所以輸入的(解析的)腳本將是這樣的:
Frame 00: 0(00) Frame 01: 0000 - 30 44 02 20 27 6d 6d ad 3d ef a3 7b 5f 81 ad d3 0010 - 99 2d 51 0d 2f 44 a3 17 fd 85 e0 4f 93 a1 e2 da 0020 - ea 64 66 02 02 20 0f 86 2a 0d a6 84 24 93 22 ce 0030 - b8 ed 84 2f b8 c8 59 c0 cb 94 c8 1e 1c 53 08 b4 0040 - 86 81 57 a4 28 ee 01 END Frame 02: OP_CODESEPARATOR(0xab) Frame 03: 1(0x51) Frame 04: 0000 - 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c b3 3d 0010 - 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 14 d5 0020 - 5a END Frame 05: 1(0x51) Frame 06: OP_CHECKMULTISIG(0xae) Frame 07: OP_CODESEPARATOR(0xab) Frame 08: 0000 - 2a 9b c5 44 7d 66 4c 1d 01 41 39 2a 84 2d 23 db 0010 - a4 5c 4f 13 END Frame 09: OP_NOP2/OP_CHECKLOCKTIMEVERIFY(0xb1) Frame 10: OP_DROP(0x75)
OP_CODESEPARATOR
然後,我從我正在執行的最接近的下標創建OP_CHECKMULTISIG
直到腳本結束。因此,我將第 03 幀到第 10 幀包括在內。然後,我刪除
OP_CODESEPARATOR
腳本中的其他 s,即 Frame 07。這意味著這些框架應該組成下標:
Frame 03: 1(0x51) Frame 04: 0000 - 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c b3 3d 0010 - 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 14 d5 0020 - 5a END Frame 05: 1(0x51) Frame 06: OP_CHECKMULTISIG(0xae) Frame 08: 0000 - 2a 9b c5 44 7d 66 4c 1d 01 41 39 2a 84 2d 23 db 0010 - a4 5c 4f 13 END Frame 09: OP_NOP2/OP_CHECKLOCKTIMEVERIFY(0xb1) Frame 10: OP_DROP(0x75)
序列化後,最終的下標是這樣的:
0000 - 51 21 02 32 ab dc 89 3e 7f 06 31 36 4d 7f d0 1c 0010 - b3 3d 24 da 45 32 9a 00 35 7b 3a 78 86 21 1a b4 0020 - 14 d5 5a 51 ae 14 2a 9b c5 44 7d 66 4c 1d 01 41 0030 - 39 2a 84 2d 23 db a4 5c 4f 13 b1 75
但是,當這個下標用於替換簽名腳本,並最終生成雜湊時,ecdsa_verify 函式會報告簽名驗證失敗(但輸入是合法的)。
由於相同的過程適用於
OP_CHECKSIG
,我可能錯過了一些明顯的東西,但我無法通過閱讀原始的 Satoshi 程式碼、目前的實現或比特幣 Wiki 來弄清楚這一點。如果有幫助,我的 OP_CHECKMULTISIG 程式碼在這裡: https ://github.com/blaesus/tinybtc/blob/checkmultisig/src/script.c#L687
更簡單的版本(使用 prevTx.pubkeyScript 作為下標)也不會產生有效的結果:https ://github.com/blaesus/tinybtc/blob/validate/src/script.c#L344
這裡的主要困惑來自於此:
首先,通過連接生成腳本
[sigScript][OP_CODESEPARATOR][pubkeyScript]
然後你添加:
我知道腳本執行是從這個演變而來的
我不確定您的意思是“基於此方法建構”還是“已更改此方法”,但如果是前者,那麼這可能會解釋您得到的意外結果。希望我的回答可以澄清這一點。
確實,在(相對)較短的時期內,比特幣的腳本評估是通過您提到的方式完成的,將 a
CODESEPARATOR
放在兩者之間scriptSig
,scriptPubkey
然後將整個事情連接起來形成一個可以執行的腳本,但是很早就改變了送出6ff5f718b6a67797b2b3bab8905d607ad216ee21
日期為 2010 年 7 月 31 日。經過上述更改,它引入了一個名為
VerifyScript
in的函式script.cpp
(今天的邏輯是 ininterpreter.cpp
),scriptSig
並且scriptPubkey
不再與CODESEPARATOR
它們之間的 a 連接。相反,它們作為兩個單獨的腳本一個接一個地執行,堆棧的內容從第一個到第二個。差異是微妙的,並沒有真正改變標準兌換的流程,但是當
CHECKSIG
在scriptSig
.從資金交易開始
b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d
我們看到其中的第二個輸出是對
scriptPubKey
index 處的以下內容的支付1
:14 2a9bc5447d664c1d0141392a842d23dba45c4f13 NOP2 DROP
OP_CLTV
尚未啟動,因此操作碼0xb1
仍為NOP2
.然後由
eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
通過索引處的輸入
1
:0 47 30 44 02 20 276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea646602 02 20 0f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee 01 CODESEPARATOR 1 21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG
在 commit 之前的短暫時間內
6ff5f718
,兩個腳本將CODESEPARATOR
連接起來,並在它們之間放置一個完整的腳本,然後開始執行。我們從一個空堆棧開始,以及一個指向腳本開頭的指針,該腳本標誌著簽名的開始scriptCode
:
- 壓
0
入堆棧- 將簽名壓入堆棧
- 執行
CODESEPARATOR
——在腳本中將此點標記為開始scriptCode
(覆蓋在開始時設置的先前值)- 壓
1
入堆棧- 將公鑰推入堆棧
- 壓
1
入堆棧- 執行 1-of-1
CHECKMULTISIG
此時,我們需要構造
scriptCode
要簽名的。規則是:
- 取最後執行的下標
CODESEPARATOR
直到腳本結束- 移除所有
CODESEPARATORS
- 刪除目前正在驗證的簽名的所有出現
由於簽名實際上在last之前
CODESEPARATOR
,因此規則 #3 在這裡不會生效,但規則 #1 和 #2 會。這給我們留下了以下內容scriptCode
:1 21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG 14 2a9bc5447d664c1d0141392a842d23dba45c4f13 NOP2 DROP
這是你最初想出的,但現在讓我們看看改變如何產生影響
6ff5f718
。回想一下,我們現在正在處理兩個單獨的腳本,但其他規則都沒有改變。唯一的區別是這兩個腳本是分開執行的,一個接一個,並且從執行 的剩餘的堆棧
scriptSig
傳遞到執行scriptPubkey
.我們首先像以前一樣執行
scriptSig
並執行步驟#1 - #7,到達CHECKMULTISIG
我們構造scriptCode
. 由於實際執行的腳本現在只包含 中的內容scriptSig
,因此scriptCode
變為:1 21 0232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a 1 CHECKMULTISIG
如果此簽名檢查通過(並且確實通過了),則將 的值
1
壓入堆棧,並scriptSig
成功結束執行。這個堆棧現在傳遞給執行scriptPubkey
(非常簡單)並最終作為堆棧中的最終值保留,這使得scriptPubkey
解析為True
.這個交易非常有趣,真正展示了
6ff5f718
實際送出的重要性。這是一個很好的例子。另一個需要注意的小事是,這
scriptPubKey
本身就是一個任何人都可以花費的腳本。該腳本的條件只需傳遞一個評估為 True(例如1
-0x51
)的元素的單次推送即可滿足,並且簽名簽入scriptSig
實際上沒有任何意義,並且根本不保證資金安全。編輯 :
實際上,再仔細研究一下,似乎BIP-17中特別提到了此交易(此後已被放棄)。中的 20 個字節
scriptPubkey
實際上是 1-of-1 多重簽名腳本本身的 hash160:51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51ae
這實際上是 bip 的作者放置在鏈上的範例 bip-17 交易之一。如果這個 bip 被接受,這樣的交易就不會是任何人都可以消費的。(您可以在 bip 本身中查看更多詳細資訊)