Script

如何計算 OP_CHECKMULTISIG 的下標?

  • October 16, 2019

我正在嘗試通過實現它來學習比特幣協議,並且我已經開始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]

然後你添加:

我知道腳本執行是從這個演變而來的

我不確定您的意思是“基於此方法建構”還是“已更改此方法”,但如果是前者,那麼這可能會解釋您得到的意外結果。希望我的回答可以澄清這一點。

確實,在(相對)較短的時期內,比特幣的腳本評估是通過您提到的方式完成的,將 aCODESEPARATOR放在兩者之間scriptSigscriptPubkey然後將整個事情連接起來形成一個可以執行的腳本,但是很早就改變了送出6ff5f718b6a67797b2b3bab8905d607ad216ee21日期為 2010 年 7 月 31 日。

經過上述更改,它引入了一個名為VerifyScriptin的函式script.cpp(今天的邏輯是 in interpreter.cpp),scriptSig並且scriptPubkey不再與CODESEPARATOR它們之間的 a 連接。相反,它們作為兩個單獨的腳本一個接一個地執行,堆棧的內容從第一個到第二個。

差異是微妙的,並沒有真正改變標準兌換的流程,但是當CHECKSIGscriptSig.

從資金交易開始

b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d

我們看到其中的第二個輸出是對scriptPubKeyindex 處的以下內容的支付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

  1. 0入堆棧
  2. 將簽名壓入堆棧
  3. 執行CODESEPARATOR——在腳本中將此點標記為開始scriptCode(覆蓋在開始時設置的先前值)
  4. 1入堆棧
  5. 將公鑰推入堆棧
  6. 1入堆棧
  7. 執行 1-of-1CHECKMULTISIG

此時,我們需要構造scriptCode要簽名的。規則是:

  1. 取最後執行的下標CODESEPARATOR直到腳本結束
  2. 移除所有CODESEPARATORS
  3. 刪除目前正在驗證的簽名的所有出現

由於簽名實際上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 本身中查看更多詳細資訊)

引用自:https://bitcoin.stackexchange.com/questions/79797