Ecdsa
如何使用 pycoin 驗證樣本 litecoin tx 的 ECDSA?
我正在使用 litecoin testnet 網路進行測試。
053baee857adfdc16959e9dbc9618e0e935f0a1e51e226e651fcd67242f1f462
我有一個0的 UTXOvout
。我進行了原始交易,將硬幣發送給自己(在不同的地址):
$ ./litecoind createrawtransaction '[{"txid":"053baee857adfdc16959e9dbc9618e0e935f0a1e51e226e651fcd67242f1f462", "vout":0}]' '{"mroLyo22ptLfq5LKMijnCyD35mEsAWTkF9":0.039}' 010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b050000000000ffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000
然後我使用以下命令簽署原始交易
signrawtransaction
:$ ./litecoind signrawtransaction 010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b050000000000ffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000 signrawtransaction hash: eed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0 { "hex" : "010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b05000000006a4730440220258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e0220406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd012103ec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccfffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000", "complete" : true }
請注意,此處簽名的雜湊是
eed3...
. 我在程式碼中添加了一個列印行,以便我可以看到uint256
正在簽名的內容。具體來說,在進行script.cpp
簽名的signrawtransaction
地方,我添加了一個額外的布爾參數來記錄雜湊和下面的行。bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType, bool printHash) { ... // Leave out the signature from the hash, since a signature can't sign itself. // The checksig op will also drop the signatures from its hash. uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); if (printHash) { std::cout << "signrawtransaction hash: " << hash.ToString() << std::endl; }
接下來,我
decoderawtransaction
取棧上的第一個元素asm
,也就是簽名。30 44 02 20 258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e 02 20 406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd 01
在這裡,在 Pieter Wuille 的幫助下(Why the signature is always 65 (1+32+32) bytes long?),我們可以看到 R 和 S 元素。
接下來,我將它們放入我的 pycoin 測試文件中。Pycoin 是一個易於使用的比特幣 Python 庫((<https://github.com/richardkiss/pycoin>))。
從 pycoin.ecdsa 導入 * 導入字元串 def verify_sig(sig, prefix, xpub, signed_val): is_even = (前綴 % 2 == 0) pub_pair = public_pair_for_x(generator_secp256k1, xpub, is_even) print("sig: (" + hex(sig[0]) + ", " + hex(sig[1]) + ")") print("pub: (" + hex(pub_pair[0]) + ", " + hex(pub_pair[1]) + ")") print("hex:" + hex(signed_val)) print("有效:" + str(verify(generator_secp256k1, pub_pair, signed_val, sig))) sigR = 0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e sigS = 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd sig = (sigR, sigS) x = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf 雜湊 = 0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0 壓縮字元 = 0x03 verify_sig(sig,compressed_char,x,hash)
以及列印的結果:
信號:(0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e,0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07 酒館:(0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf,0x227874efbedb02b88568b55106634b9b4e3eca128ab46fa98abe7066aece0a51) 十六進制:0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0 有效:假
我真的不確定這裡可能出了什麼問題,因為這只是直接的 ECDSA 驗證。那裡是否有任何看起來很奇怪的值,或任何其他無法驗證的原因?
發現問題了!比特幣對於大/小端整數非常不一致。我反轉了雜湊並驗證了簽名!
最終程序:
從 pycoin.ecdsa 導入 * 導入字元串 導入數組 導入 binascii def verify_sig(sig, prefix, xpub, signed_val): is_even = (前綴 % 2 == 0) pub_pair = public_pair_for_x(generator_secp256k1, xpub, is_even) print("sig: (" + hex(sig[0]) + ", " + hex(sig[1]) + ")") print("pub: (" + hex(pub_pair[0]) + ", " + hex(pub_pair[1]) + ")") print("hex:" + hex(signed_val)) print("有效:" + str(verify(generator_secp256k1, pub_pair, signed_val, sig))) sigR = 0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e sigS = 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd sig = (sigR, sigS) x = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf 字元 = 0x03 revhash = "eed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0" 雜湊 = "" 對於範圍內的 i (0, len(revhash), 2): 雜湊 = revhash[i:i+2] + 雜湊 verify_sig(sig, char, x, int(hash, 16))
結果:
信號:(0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e,0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07 酒館:(0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf,0x227874efbedb02b88568b55106634b9b4e3eca128ab46fa98abe7066aece0a51) 十六進制:0xb035a57aa7c6cceec10e334b6f55c68e37de6f74af66af68b673a963fc00d3ee 有效:真