Transactions

使用壓縮公鑰在python中進行簽名驗證

  • March 22, 2018

我正在嘗試學習比特幣區塊鏈程式。我正在嘗試使用 txid 驗證交易中的簽名:4269fdc239d027922dcec96f1ae283dbaff10e2d1bd49605661d091e79714956

我收到 BadSignatureError

原始交易

./bitcoin-cli getrawtransaction 4269fdc239d027922dcec96f1ae283dbaff10e2d1bd49605661d091e79714956
01000000017f950ab790838e0c05e79856d25d586823fe139e1807405a3f207ff33f9b7663010000006b483045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990012102b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90ffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac00000000

解碼原始交易

./core/bitcoin-0.16.0/bin/bitcoin-cli decoderawtransaction 01000000017f950ab790838e0c05e79856d25d586823fe139e1807405a3f207ff33f9b7663010000006b483045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990012102b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90ffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac00000000
{
 "txid": "4269fdc239d027922dcec96f1ae283dbaff10e2d1bd49605661d091e79714956",
 "hash": "4269fdc239d027922dcec96f1ae283dbaff10e2d1bd49605661d091e79714956",
 "version": 1,
 "size": 224,
 "vsize": 224,
 "locktime": 0,
 "vin": [
   {
     "txid": "63769b3ff37f203f5a4007189e13fe2368585dd25698e7050c8e8390b70a957f",
     "vout": 1,
     "scriptSig": {
       "asm": "3045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990[ALL] 02b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90",
       "hex": "483045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990012102b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90"
     },
     "sequence": 4294967295
   }
 ],
 "vout": [
   {
     "value": 0.03993627,
     "n": 0,
     "scriptPubKey": {
       "asm": "OP_HASH160 69f3757380a56820abc7052867216599e575cddd OP_EQUAL",
       "hex": "a91469f3757380a56820abc7052867216599e575cddd87",
       "reqSigs": 1,
       "type": "scripthash",
       "addresses": [
         "3BMEXVvXXRFh2eJ9Eji115xfqJjWmLTCf8"
       ]
     }
   },
   {
     "value": 4.83049847,
     "n": 1,
     "scriptPubKey": {
       "asm": "OP_DUP OP_HASH160 d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac6 OP_EQUALVERIFY OP_CHECKSIG",
       "hex": "76a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac",
       "reqSigs": 1,
       "type": "pubkeyhash",
       "addresses": [
         "1LWPbaYN2jqhv9oZvYHxYKXuaiR1qJn52i"
       ]
     }
   }
 ]
}

這裡的“asm”包含簽名和公鑰

 "vin": [
   {
     "txid": "63769b3ff37f203f5a4007189e13fe2368585dd25698e7050c8e8390b70a957f",
     "vout": 1,
     "scriptSig": {
       "asm": "3045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990[ALL] 02b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90",
       "hex": "483045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990012102b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90"
     },
     "sequence": 4294967295
   }
 ],

獲取簽名

3045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990
=>
0x30 DER
0x45 Length
0x02 Type Integer
0x21 Length of R
0x00d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2 (R)
0x02 Type Integer
0x20 Length of S
0x78787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990 (S)

Removing 00 from R
Signature = R + S = d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f278787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990

壓縮公鑰 02b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90

解壓縮公鑰程式碼

def getFullPubKeyFromCompressed(x_str: str):
       prefix = x_str[0:2]
       print("prefix = %s" % (prefix))
       x_str = x_str[2:]
       x = int(x_str, 16)
       print("x = %x" % (x))
       p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
       y_squared = (x**3 + 7) % p
       y = modular_sqrt(y_squared, p)
       y_str = "%x" % y
       print("y_str = %s" % (y_str))
       y_is_even = (int(y_str[-1], 16) % 2 == 0)
       if prefix == "02" and y_is_even == False or prefix == "03" and y_is_even == True:
               y = p - y
               y_str = "%x" % y
       print("y = %s" % (y_str))
       return "04" + x_str + y_str

prefix = pubkey[0:2]
if prefix == "02" or prefix == "03":
       pubkey = getFullPubKeyFromCompressed(pubkey)[2:]
elif prefix == "04":
               pubkey = pubkey[2:]
print("full public key = %s" % pubkey)

輸出:

x = b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90
y_str = cacc567dc3036f2fceb1b6f16676285b153821eb79825dae124b17b9e29479cb
y = 3533a9823cfc90d0314e490e9989d7a4eac7de14867da251edb4e8451d6b8264
full public key = b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d903533a9823cfc90d0314e490e9989d7a4eac7de14867da251edb4e8451d6b8264

交易單次 SHA256 雜湊,因為這將再次完成簽名驗證程式碼

hashval = binascii.hexlify(hashlib.sha256(bytes.fromhex(raw_txn)).digest())
txn_sha256 = bytes.decode(hashval)
print("txn_sha256 = %s" % (txn_sha256))

輸出

txn_sha256 = fb0c792bfba8139654cffd2818bdd56f3541d11fb43ea46f646c3477de3d2e40

我們現在擁有所有三個資訊來驗證簽名

txn_sha256 = fb0c792bfba8139654cffd2818bdd56f3541d11fb43ea46f646c3477de3d2e40
pubkey = b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d903533a9823cfc90d0314e490e9989d7a4eac7de14867da251edb4e8451d6b8264
sig = d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f278787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990

我執行檢查簽名程序

sig_b = bytes.fromhex(sig)
txn_sha256_b = bytes.fromhex(txn_sha256)
vk = ecdsa.VerifyingKey.from_string(bytes.fromhex(pubkey),curve=ecdsa.SECP256k1)
if vk.verify(sig_b, txn_sha256_b, hashlib.sha256) == True: # True
       print("Signature is Valid")
else:
       print("Signature is not Valid")

但我得到 BadSignatureError

Traceback (most recent call last):
 File "bitcoin_localapis.py", line 294, in <module>
   sigcheck(sig, pubkey, raw_txn)
 File "bitcoin_localapis.py", line 202, in sigcheck
   if vk.verify(sig_b, txn_sha256_b, hashlib.sha256) == True: # True
 File "/home/vizeet/anaconda3/lib/python3.6/site-packages/ecdsa/keys.py", line 101, in verify
   return self.verify_digest(signature, digest, sigdecode)
 File "/home/vizeet/anaconda3/lib/python3.6/site-packages/ecdsa/keys.py", line 113, in verify_digest
   raise BadSignatureError
ecdsa.keys.BadSignatureError

請幫我解決這個問題。

問題在於我用於簽名驗證的原始交易。

原始原始交易

./bitcoin-cli getrawtransaction 4269fdc239d027922dcec96f1ae283dbaff10e2d1bd49605661d091e79714956
01000000017f950ab790838e0c05e79856d25d586823fe139e1807405a3f207ff33f9b7663010000006b483045022100d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f2022078787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990012102b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90ffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac00000000

手動解析這個

01 00 00 00 Version 
01 Input Count 
7f 95 0a b7 90 83 8e 0c 05 e7 98 56 d2 5d 58 68 23 fe 13 9e 18 07 40 5a 3f 20 7f f3 3f 9b 76 63 (32 Bytes of Previous Transaction ID)
01 00 00 00 Output Index (vout index) 
6b Script Length (107 bytes) (Replace this)
48 30 45 02 21 00 d8 62 94 03 cd 3b 49 95 0d a9 29 36 53 c6 27 91 49 c0 29 e6 b7 b1 53 71 34 2d 0d 2c e2 86 c8 f2 02 20 78 78 79 85 a6 44 e9 4f d9 24 6f 6c 25 73 33 36 c9 4a f5 f0 0d 9d 34 a0 7d c2 f9 e0 98 7e f9 90 01 21 02 b7 26 d7 ea e1 1a 6d 5c f3 b2 36 2e 77 3e 11 6a 61 40 34 7d ce e1 b2 94 3f 4a 28 97 35 1e 5d 90 Script (and Replace this)
ffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac00000000 Remaing bytes 
01 00 00 00 Append SIGHASH_ALL

用 scriptPubKey 替換上面標記的交易部分。我們似乎沒有理由這樣做。參考這個討論:

<https://bitcointalk.org/index.php?topic=102487.msg1123257#msg1123257>

壓縮公鑰 =02b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d90

來自公鑰的地址:程式碼

def sha256d(bstr):
   return hashlib.sha256(hashlib.sha256(bstr).digest()).digest()

def convertPKHToAddress(prefix, addr):
   data = prefix + addr
   return base58.b58encode(data + sha256d(data)[:4])

def pubkeyToAddress(pubkey_hex):
       pubkey = bytearray.fromhex(pubkey_hex)
       round1 = hashlib.sha256(pubkey).digest()
       h = hashlib.new('ripemd160')
       h.update(round1)
       pubkey_hash = h.digest()
       return convertPKHToAddress(b'\x00', pubkey_hash)

輸出地址

1LWPbaYN2jqhv9oZvYHxYKXuaiR1qJn52i

地址的 Hash160(來自<https://blockchain.info/address/1LWPbaYN2jqhv9oZvYHxYKXuaiR1qJn52i>)

d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6

將大小添加到散列地址

14 d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6

輸入交易雜湊 =63769b3ff37f203f5a4007189e13fe2368585dd25698e7050c8e8390b70a957f

該交易有後續的scriptPubKey。其中一個花費在我們將發現的交易中。

out index 0: 76 a9 14 89 df 43 9f 5e 48 c0 e7 ec d6 91 af fa 1f b8 39 15 7e 0a 71 88 ac
out index 1: 76 a9 14 d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6 88 ac

解碼腳本PubKey

OP_DUP = 0x76 (first byte of both)
OP_HASH160 = 0xa9 (second byte of both)
OP_EQUALVERIFY = 0x88 (second last byte of both)
OP_CHECKSIG = 0xac (last byte of both)

因此,我們需要在我們正在調查的交易的 scriptSig 中使用公鑰的 Hash160。這個我們已經計算過了。從我們得到的公鑰向 hash160 添加腳本

76 a9 14 d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6 88 ac

新腳本的大小 = 25 = 0x19

新腳本

19 76 a9 14 d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6 88 ac

所以我們發現這等於第二個輸入事務的scriptPubKey。此輸出標記為已用。

現在將此腳本插入原始事務之後。

我們得到新的原始交易

01 00 00 00 Version 
01 Input Count 
7f 95 0a b7 90 83 8e 0c 05 e7 98 56 d2 5d 58 68 23 fe 13 9e 18 07 40 5a 3f 20 7f f3 3f 9b 76 63 (32 Bytes of Previous Transaction ID)
01 00 00 00 Output Index (vout index)
19 76 a9 14 d5 f9 50 ab e0 b5 59 b2 b7 a7 ab 3d 18 a5 07 ea 1c 3e 4a c6 88 ac (New Script)
ffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac00000000 (Remaining)
01 00 00 00 Append SIGHASH_ALL

成為新的原始交易

01000000017f950ab790838e0c05e79856d25d586823fe139e1807405a3f207ff33f9b7663010000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688acffffffff021bf03c000000000017a91469f3757380a56820abc7052867216599e575cddd8777c1ca1c000000001976a914d5f950abe0b559b2b7a7ab3d18a507ea1c3e4ac688ac0000000001000000

我們只需要應用一次 SHA256,因為簽名驗證方法也應用 SHA256。程式碼

hashval = binascii.hexlify(hashlib.sha256(bytes.fromhex(raw_txn)).digest())
txn_sha256 = bytes.decode(hashval)
print("txn_sha256 = %s" % (txn_sha256))

輸出 :

txn_sha256 = 3d3b8997cc9c0e2275bd0f694b862f7d0bee4f7aee2456891e038322884070ad

現在我們有了簽名驗證所需的所有三個變數

pubkey = b726d7eae11a6d5cf3b2362e773e116a6140347dcee1b2943f4a2897351e5d903533a9823cfc90d0314e490e9989d7a4eac7de14867da251edb4e8451d6b8264
sig = d8629403cd3b49950da9293653c6279149c029e6b7b15371342d0d2ce286c8f278787985a644e94fd9246f6c25733336c94af5f00d9d34a07dc2f9e0987ef990
txn_sha256 = 3d3b8997cc9c0e2275bd0f694b862f7d0bee4f7aee2456891e038322884070ad

現在執行簽名驗證碼程式碼

sig_b = bytes.fromhex(sig)
txn_sha256_b = bytes.fromhex(txn_sha256)
vk = ecdsa.VerifyingKey.from_string(bytes.fromhex(pubkey),curve=ecdsa.SECP256k1)
if vk.verify(sig_b, txn_sha256_b, hashlib.sha256) == True: # True
       print("Signature is Valid")
else:
       print("Signature is not Valid")

給簽名是有效的

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