Python

在 Python 中驗證 Pizza 交易

  • June 8, 2022

我和我的一個朋友正在嘗試學習如何閱讀比特幣區塊鏈。我們已經得到了幾乎所有東西,除了ECDSA 驗證所花費的硬幣實際上屬於花費它們的人。

我們正在從@amaclin 那裡得到這個答案:https ://bitcoin.stackexchange.com/a/32308/235711

這是我們的要點:https ://gist.github.com/bnsh/922b5331a77f6b85d14e0bd682f6304d

而且,這是直接的程式碼。

#! /usr/bin/env python3
# vim: expandtab shiftwidth=4 tabstop=4

"""
Hi.

We're trying to implement this code at https://bitcoin.stackexchange.com/a/32308/235711 in python with the
ecdsa library. Unfortunately, we're not clear on what language this code below is written in.. C++? C#?

We'd like to verify using python, but it doesn't seem to work. Can someone tell us what we're doing wrong?

const QByteArray xx ( QByteArray::fromHex ( "01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d41"
                                           "5db55d07a1000000001976a91446af3fb481837fadbb421727f9959c2d32a368"
                                           "2988acffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c"
                                           "54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b180"
                                           "6be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce33"
                                           "88fa1abd0fa88b06c44ce81e2234aa70fe578d455dac0000000001000000" ) );
const MyKey32 digest ( xx.constData ( ), xx.size ( ) ); // construct object of sha256 (sha256 ( xx ) )
_trace ( digest.toString ( ) );                         // print result
const QByteArray pubkey ( QByteArray::fromHex ( "042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb" ) );
const QByteArray signature ( QByteArray::fromHex ( "30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e" ) );
_trace ( QString ( "verify=%1" ).arg ( digest.verify ( pubkey, signature ) ) );
"""

import hashlib
import ecdsa

def from_hex(hexval):
   return bytes(int(hexval[idx:(idx+2)], 16) for idx in range(0, len(hexval), 2))

def to_hex(raw):
   return "".join(reversed([f"{int(val):02x}" for val in raw]))

def mykey32(data):
   return hashlib.sha256(hashlib.sha256(data).digest()).digest()

def main():
   # We're preserving the same variable names as the sample code above
   # for clarity.
   xx = from_hex(
       "01000000018dd4f5fbd5e980fc02f35c6ce145935b11e284605bf599a13c6d41"
       "5db55d07a1000000001976a91446af3fb481837fadbb421727f9959c2d32a368"
       "2988acffffffff0200719a81860000001976a914df1bd49a6c9e34dfa8631f2c"
       "54cf39986027501b88ac009f0a5362000000434104cd5e9726e6afeae357b180"
       "6be25a4c3d3811775835d235417ea746b7db9eeab33cf01674b944c64561ce33"
       "88fa1abd0fa88b06c44ce81e2234aa70fe578d455dac0000000001000000"
   )
   digest = mykey32(xx)
   print(to_hex(digest)) # This clearly works as described in the post.

   pubkey = from_hex("042e930f39ba62c6534ee98ed20ca98959d34aa9e057cda01cfd422c6bab3667b76426529382c23f42b9b08d7832d4fee1d6b437a8526e59667ce9c4e9dcebcabb")
   signature = from_hex("30450221009908144ca6539e09512b9295c8a27050d478fbb96f8addbc3d075544dc41328702201aa528be2b907d316d2da068dd9eb1e23243d97e444d59290d2fddf25269ee0e")
   
   verkey = ecdsa.VerifyingKey.from_string(pubkey, curve=ecdsa.SECP256k1)

   # This fails with ecdsa.keys.BadSignatureError: MalformedSignature('Invalid length of signature[...]')
   # It seems like it wants a 64 byte signature.
   verkey.verify(signature, digest)

   # How would we verify the pizza transaction as was done in the post
   # in python?
   # Thanks!

if __name__ == "__main__":
   main()

我們正在使用 ecdsa python 庫(但我們可以使用任何其他更合適的庫)。我們如何使用python驗證簽名?

謝謝。

好的,回答我自己的問題..

出現了三個問題:

  1. VerifyingKey.verify的預設值為hashfunchashlib.sha1但我們需要hashlib.sha256. 所以,我們需要hashfunc=hashlib.sha256verify呼叫中添加一個。ECDSA 文件
  2. 需要傳遞給摘要的應該是對 的一次呼叫hashlib.sha256,而不是通常完成的兩次呼叫。
  3. signature實際上是一個DER簽名,需要提取。有一個圖書館可以做到這一點,但我們想了解發生了什麼。

一個。簽名中的第一個字節是 0x30,它表示 ASN.1 的“序列”介紹

灣。接下來是一個 0x45,它表示序列的以下總長度(以字節為單位):4 * 16 + 5 = 69。

C。接下來是一個 0x02,它表示一個整數。

d。接下來是 0x21。這應該是簽名中第一個整數的長度。但是,我們只需要提取整數的最後32 個字節。

e. 在字節 37 的 33 個字節之後是另一個 0x02(另一個整數)

F。然後是下一個整數的 0x20(32 字節)

所以,“真正的”簽名是signature[5:(5+32)] + signature[39:(39+32)]

如果我們這樣做verkey.verify(signature, digest, hashfunc=hashlib.sha256),它就會毫無例外地執行。

固定程式碼在這裡:https ://gist.github.com/bnsh/808f2652818907e5153ee0dfde57a40f

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