1

I'm using the litecoin testnet network for testing. I have a UTXO at 053baee857adfdc16959e9dbc9618e0e935f0a1e51e226e651fcd67242f1f462 with a vout of 0.

I make a raw transaction sending the coins to myself (at a different address):

$ ./litecoind createrawtransaction '[{"txid":"053baee857adfdc16959e9dbc9618e0e935f0a1e51e226e651fcd67242f1f462", "vout":0}]' '{"mroLyo22ptLfq5LKMijnCyD35mEsAWTkF9":0.039}'
 010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b050000000000ffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000

Then I sign the raw transaction using signrawtransaction:

$ ./litecoind signrawtransaction 010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b050000000000ffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000
signrawtransaction hash: eed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0
{
    "hex" : "010000000162f4f14272d6fc51e626e2511e0a5f930e8e61c9dbe95969c1fdad57e8ae3b05000000006a4730440220258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e0220406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd012103ec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccfffffffff0160823b00000000001976a9147bc2f9c3a30a18cc307d74fd014e4ddf7323398488ac00000000",
    "complete" : true
}

Note that the hash being signed here is eed3.... I added a print line t the code so that I could see the uint256 that is being signed. Specifically, in script.cpp, where the signing for signrawtransaction takes place, I added an extra boolean parameter to log the hash and the lines below.

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;
    }

Next, I decoderawtransaction and take the first element on the asm stack, which is the signature.

30 44 02 20 
258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e
02 20 
406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd
01

Here, with some help from Pieter Wuille (Why the signature is always 65 (1+32+32) bytes long?), we can see the R and the S elements.

Next I put them in to my pycoin test file. Pycoin is an easy to use python library for bitcoin ((https://github.com/richardkiss/pycoin)).

from pycoin.ecdsa import *

import string

def verify_sig(sig, prefix, xpub, signed_val):
    is_even = (prefix % 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("is valid: " + str(verify(generator_secp256k1, pub_pair, signed_val, sig)))

sigR = 0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e
sigS = 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd
sig  = (sigR, sigS)
x    = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf
hash = 0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0
compressed_char = 0x03

verify_sig(sig, compressed_char, x, hash)

And the result that is printed:

sig: (0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e, 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd)
pub: (0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf, 0x227874efbedb02b88568b55106634b9b4e3eca128ab46fa98abe7066aece0a51)
hex: 0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0
is valid: False

I am really not sure what could be going wrong here, since this is just straight ECDSA verification. Are there any values there that seem strange, or any other reason that this would not validate?

1 Answer 1

0

Found the problem! Bitcoin is very inconsistent about big/little endian integers. I reversed the hash and the signature was verified!

Final program:

from pycoin.ecdsa import *

import string
import array
import binascii

def verify_sig(sig, prefix, xpub, signed_val):
    is_even = (prefix % 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("is valid: " + str(verify(generator_secp256k1, pub_pair, signed_val, sig)))

sigR = 0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e
sigS = 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd
sig  = (sigR, sigS)

x    = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf
char = 0x03

revhash = "eed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0"
hash = ""
for i in range(0, len(revhash), 2):
    hash = revhash[i:i+2] + hash

verify_sig(sig, char, x, int(hash, 16))

And the result:

sig: (0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e, 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd)
pub: (0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf, 0x227874efbedb02b88568b55106634b9b4e3eca128ab46fa98abe7066aece0a51)
hex: 0xb035a57aa7c6cceec10e334b6f55c68e37de6f74af66af68b673a963fc00d3ee
is valid: True

Your Answer

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

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