Transactions

python中的P2SH簽名驗證

  • May 26, 2018

我試圖通過編寫 python 程式碼來解析區塊鏈中的真實交易來理解和驗證 P2SH 腳本。我隨機選擇了下面的交易。TxID:7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45

我收到 BadSignatureError。

原始交易:

bitcoin-cli getrawtransaction 7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45
0100000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee40000000009000483045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51aefeffffff0120f40e00000000001976a9141d30342095961d951d306845ef98ac08474b36a088aca7270400

解碼以上 Raw transaction 並獲取鎖定腳本的 Transaction ID:

bitcoin-cli decoderawtransaction $(bitcoin-cli getrawtransaction 7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45)
{
 "txid": "7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45",
 "hash": "7edb32d4ffd7a385b763c7a8e56b6358bcd729e747290624e18acdbe6209fc45",
 "version": 1,
 "size": 229,
 "vsize": 229,
 "weight": 916,
 "locktime": 272295,
 "vin": [
   {
     "txid": "40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8",
     "vout": 0,
     "scriptSig": {
       "asm": "0 3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b790[ALL] 5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae",
       "hex": "00483045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae"
     },
     "sequence": 4294967294
   }
 ],
 "vout": [
   {
     "value": 0.00980000,
     "n": 0,
     "scriptPubKey": {
       "asm": "OP_DUP OP_HASH160 1d30342095961d951d306845ef98ac08474b36a0 OP_EQUALVERIFY OP_CHECKSIG",
       "hex": "76a9141d30342095961d951d306845ef98ac08474b36a088ac",
       "reqSigs": 1,
       "type": "pubkeyhash",
       "addresses": [
         "13fLLox43yXYvfoZadXpGbkTUXkW8bhqut"
       ]
     }
   }
 ]
}

獲取鎖定腳本的解碼事務:

bitcoin-cli decoderawtransaction $(bitcoin-cli getrawtransaction 40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8)
{
 "txid": "40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8",
 "hash": "40eee3ae1760e3a8532263678cdf64569e6ad06abc133af64f735e52562bccc8",
 "version": 1,
 "size": 189,
 "vsize": 189,
 "weight": 756,
 "locktime": 0,
 "vin": [
   {
     "txid": "42a3fdd7d7baea12221f259f38549930b47cec288b55e4a8facc3c899f4775da",
     "vout": 0,
     "scriptSig": {
       "asm": "3044022048d1468895910edafe53d4ec4209192cc3a8f0f21e7b9811f83b5e419bfb57e002203fef249b56682dbbb1528d4338969abb14583858488a3a766f609185efe68bca[ALL] 031a455dab5e1f614e574a2f4f12f22990717e93899695fb0d81e4ac2dcfd25d00",
       "hex": "473044022048d1468895910edafe53d4ec4209192cc3a8f0f21e7b9811f83b5e419bfb57e002203fef249b56682dbbb1528d4338969abb14583858488a3a766f609185efe68bca0121031a455dab5e1f614e574a2f4f12f22990717e93899695fb0d81e4ac2dcfd25d00"
     },
     "sequence": 4294967295
   }
 ],
 "vout": [
   {
     "value": 0.00990000,
     "n": 0,
     "scriptPubKey": {
       "asm": "OP_HASH160 e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a OP_EQUAL",
       "hex": "a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87",
       "reqSigs": 1,
       "type": "scripthash",
       "addresses": [
         "3P14159f73E4gFr7JterCCQh9QjiTjiZrG"
       ]
     }
   }
 ]
}

我們需要評估解鎖腳本+鎖定腳本。

解鎖腳本:

00483045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae

評估解鎖腳本:

step 1: stack -> 0x00 (OP_0)
step 2: stack -> 0x00, 0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001
step 3: stack -> 0x00, 
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001, 
0x5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae

將此堆棧複製為 stack_copy

鎖定腳本:

a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87

現在使用堆棧評估鎖定腳本:

step 4: 0xa9 (OP_HASH160)
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0xe9c3dd0c07aac76179ebc76a6c78d4d67c6c160a

step 5: 0x14 (pushdata 20 bytes) 0xe9c3dd0c07aac76179ebc76a6c78d4d67c6c160a
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0xe9c3dd0c07aac76179ebc76a6c78d4d67c6c160a,
0xe9c3dd0c07aac76179ebc76a6c78d4d67c6c160a

step 6: 87 OP_EQUAL
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0x01

由於 OP_EQUAL 返回 True 並且它是 P2SH,我們根據複製的堆棧評估兌換腳本

堆棧 = stack_copy

兌換腳本 = stack.pop():

0x5141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51ae

評估兌換腳本:

step 7: 0x51 (OP_1)
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0x01

step 8: 0x41 (Pushdata 65 bytes) 042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0x01,
0x042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf

step 9: 0x51 (OP_1)
stack -> 0x00,
0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001,
0x01,
0x042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf,
0x01

step 10: 0xae (OP_CHECKMULTISIG)
We have 1 sig and 1 pubkey. To check multisig we need to get transaction which was signed. We will come back to this step once we have transaction which was signed.

來自堆棧的簽名:

0x3045022100ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf88302200b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b79001

解析簽名:

0x30 DER
0x45 Length
0x02 Type Integer
0x21 Length of r
00 (ignore) ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf883 (r)
02 Type Integer
20 Length of s
0b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b790 (s)
01 SIGHASH_ALL

Sig-type 是 SIGHASH_ALL 所以我們只用鎖定腳本替換解鎖腳本。

r||s: ——————————————— —————-(1) ad0851c69dd756b45190b5a8e97cb4ac3c2b0fa2f2aae23aed6ca97ab33bf8830b248593abc1259512793e7dea61036c601775ebb23640a0120b0dba2c34b790

解析解鎖腳本的原始交易:

01 00 00 00 Version
01 input count
c8 cc 2b 56 52 5e 73 4f f6 3a 13 bc 6a d0 6a 9e 56 64 df 8c 67 63 22 53 a8 e3 60 17 ae e3 ee 40 Previous transaction
00 00 00 00 Previous transaction out index
--------- replace this [
90 
00 48 30 45 02 21 00 ad 08 51 c6 9d d7 56 b4 51 
90 b5 a8 e9 7c b4 ac 3c 2b 0f a2 f2 aa e2 3a ed 
6c a9 7a b3 3b f8 83 02 20 0b 24 85 93 ab c1 25
95 12 79 3e 7d ea 61 03 6c 60 17 75 eb b2 36 40 
a0 12 0b 0d ba 2c 34 b7 90 01 45 51 41 04 2f 90 
07 4d 7a 5b f3 0c 72 cf 3a 8d fd 13 81 bd bd 30 
40 70 10 e8 78 f3 a1 12 69 d5 f7 4a 58 78 85 05 
cd ca 22 ea 6e ab 7c fb 40 dc 0e 07 ab a2 00 42 
4a b0 d7 91 22 a6 53 ad 0c 7e c9 89 6b df 51 ae
------------- ]
fe ff ff ff sequence
01 out count
20 f4 0e 00 00 00 00 00 value 
19 script size
76 a9 14 1d 30 34 20 95 96 1d 95 1d 30 68 45 ef 98 ac 08 47 4b 36 a0 88 ac script pubkey
a7 27 04 00 lock time
---> Add SIGHASH_ALL here

用解鎖腳本替換鎖定腳本後:

01 00 00 00 Version
01 input count
c8 cc 2b 56 52 5e 73 4f f6 3a 13 bc 6a d0 6a 9e 56 64 df 8c 67 63 22 53 a8 e3 60 17 ae e3 ee 40 Previous transaction
00 00 00 00 Previous transaction out index
------- locking script [
17
a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87
------- ]
fe ff ff ff sequence
01 out count
20 f4 0e 00 00 00 00 00 value 
19 script size
76 a9 14 1d 30 34 20 95 96 1d 95 1d 30 68 45 ef 98 ac 08 47 4b 36 a0 88 ac 
a7 27 04 00 lock time
01 00 00 00 SIGHASH_ALL

現在執行 OP_CHECKMULTISIG:所以我們有已簽名的交易: ——————————(2)

0100000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee400000000017a914e9c3dd0c07aac76179ebc76a6c78d4d67c6c160a87feffffff0120f40e00000000001976a9141d30342095961d951d306845ef98ac08474b36a088aca727040001000000 

在堆棧上我們有: 0x00 -> 簽名 -> 0x01 -> Pubkey -> 0x01

公鑰:0x042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf

0x04 表示未壓縮。刪除我們擁有的: Pubkey : ——————————————- ——-(3)

0x2f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf

我使用 r||s 作為 (1) 的字節,將 raw_txn 作為 (2) 的字節,使用 pub_key 作為 (3) 的字節,作為以下 Python 程式碼的參數來執行 sigcheck:

def sigcheck(sig_b: bytes, pubkey_b: bytes, raw_txn_b: bytes):
       txn_sha256_b = hashlib.sha256(raw_txn_b).digest()

       prefix = pubkey_b[0:1]
       print('prefix = %s' % prefix)
       print('input pubkey = %s' % bytes.decode(binascii.hexlify(pubkey_b)))
       if prefix == b'\x02' or prefix == b'\x03':
               pubkey_b = getFullPubKeyFromCompressed(pubkey_b)[1:]
       elif prefix == b'\x04':
               pubkey_b = pubkey_b[1:]

       try:
               print("full public key = %s" % bytes.decode(binascii.hexlify(pubkey_b)))
               vk = ecdsa.VerifyingKey.from_string(pubkey_b, curve=ecdsa.SECP256k1)
               if vk.verify(sig_b, txn_sha256_b, hashlib.sha256) == True:
                       print('valid')
                       return 1
               else:
                       print('sigcheck: invalid')
                       return 0
       except ecdsa.BadSignatureError:
               print('sigcheck: Bad Signature')
               return 0

該程序非常適合在 P2PKH 場景中進行檢查,但在這裡失敗了,我不知道。執行這個程序我得到了 BadSignatureError。

我已經非常詳細地說明了我在做什麼。請幫助我理解並解決這個問題。

我正在使用先前交易的鎖定腳本(scriptPubKey)來替換目前交易的解鎖腳本(scriptSig)。相反,我需要使用redeemScript。解鎖腳本是 OP_0 。

所以為了得到(2)我們首先從解鎖腳本中得到redeemScript。

解鎖腳本:

90 Length of scriptSig
00 OP_0
48 Length of Signature
30 
45 
02 
21 
00 ad 08 51 c6 9d d7 56 b4 51 90 b5 a8 e9 7c b4 ac 3c 2b 0f a2 f2 aa e2 3a ed 6c a9 7a b3 3b f8 83 
02 
20 
0b 24 85 93 ab c1 25 95 12 79 3e 7d ea 61 03 6c 60 17 75 eb b2 36 40 a0 12 0b 0d ba 2c 34 b7 90 
01 SIGHASH_ALL
------ Redeem script ---->
45 51 41 04 2f 90 
07 4d 7a 5b f3 0c 72 cf 3a 8d fd 13 81 bd bd 30 
40 70 10 e8 78 f3 a1 12 69 d5 f7 4a 58 78 85 05 
cd ca 22 ea 6e ab 7c fb 40 dc 0e 07 ab a2 00 42 
4a b0 d7 91 22 a6 53 ad 0c 7e c9 89 6b df 51 ae
<-------------------------

我們得到了已簽名的原始交易:

01 00 00 00 Version
01 input count
c8 cc 2b 56 52 5e 73 4f f6 3a 13 bc 6a d0 6a 9e 56 64 df 8c 67 63 22 53 a8 e3 60 17 ae e3 ee 40 Previous transaction
00 00 00 00 Previous transaction out index
------ Redeem script ---->
45 51 41 04 2f 90 
07 4d 7a 5b f3 0c 72 cf 3a 8d fd 13 81 bd bd 30 
40 70 10 e8 78 f3 a1 12 69 d5 f7 4a 58 78 85 05 
cd ca 22 ea 6e ab 7c fb 40 dc 0e 07 ab a2 00 42 
4a b0 d7 91 22 a6 53 ad 0c 7e c9 89 6b df 51 ae
<-------------------------
fe ff ff ff sequence
01 out count
20 f4 0e 00 00 00 00 00 value 
19 script size
76 a9 14 1d 30 34 20 95 96 1d 95 1d 30 68 45 ef 98 ac 08 47 4b 36 a0 88 ac 
a7 27 04 00 lock time
01 00 00 00 SIGHASH_ALL

已簽名的原始交易:————————————(2)

0100000001c8cc2b56525e734ff63a13bc6ad06a9e5664df8c67632253a8e36017aee3ee4000000000455141042f90074d7a5bf30c72cf3a8dfd1381bdbd30407010e878f3a11269d5f74a58788505cdca22ea6eab7cfb40dc0e07aba200424ab0d79122a653ad0c7ec9896bdf51aefeffffff0120f40e00000000001976a9141d30342095961d951d306845ef98ac08474b36a088aca727040001000000

現在 (1)、(2) 和 (3) 給出簽名是有效的。

來自堆棧的腳本:

那是一個簽名,而不是 scriptSig。scriptSig 就是您所說的解鎖腳本。

用解鎖腳本替換鎖定腳本後:

因為您正在嘗試驗證 P2SH 交易,所以您實際上將 scriptSig 替換為redeemScript,而不是鎖定腳本(通常稱為 scriptPubKey)。

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