使用壓縮公鑰在python中進行簽名驗證
我正在嘗試學習比特幣區塊鏈程式。我正在嘗試使用 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")
給簽名是有效的