A friend of mine and I are trying to learn how to read the bitcoin blockchain. We've gotten pretty much everything except the ECDSA verification that the coins spent actually belong to the person who is spending them.

We're working off this answer from @amaclin from here: https://bitcoin.stackexchange.com/a/32308/235711

Here's our gist: https://gist.github.com/bnsh/922b5331a77f6b85d14e0bd682f6304d

And, here's the code directly.

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


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"
                                            "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(
    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__":

We're using the ecdsa python library (but we're OK with any other more appropriate library). How can we verify the signature using python?


1 Answer 1


OK, answering my own question..

There were three problems:

  1. VerifyingKey.verify's default value for hashfunc is hashlib.sha1, but we need hashlib.sha256. So, we need to add a hashfunc=hashlib.sha256 to the verify call. ECDSA docs

  2. What needs to get passed to the digest should be a single call to hashlib.sha256, not the double call that's usually done.

  3. The signature is actually a DER signature, which needs to be extracted. There is a library to do this, but we wanted to understand what was happening.

    a. The first byte in the signature is a 0x30 which signifies a "sequence" Intro to ASN.1.

    b. Next comes a 0x45, which indicates that the total following length of the sequence in bytes: 4 * 16 + 5 = 69.

    c. Next comes a 0x02, which indicates an integer.

    d. Next comes a 0x21. This is supposed to be the length of the first integer in the signature. But, we have to only extract the last 32 bytes of the integer.

    e. After the 33 bytes at byte 37 is another 0x02 (for another integer)

    f. Then comes a 0x20 (32 bytes) for the next integer

    So, the "real" signature is signature[5:(5+32)] + signature[39:(39+32)]

If we then do verkey.verify(signature, digest, hashfunc=hashlib.sha256) it runs without any exception.

The fixed code is here: https://gist.github.com/bnsh/808f2652818907e5153ee0dfde57a40f

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.