Wie verifiziere ich ECDSA eines Beispiel-Litecoin-TX mit Pycoin?

Ich verwende das Litecoin-Testnet-Netzwerk zum Testen. Ich habe ein UTXO bei 053baee857adfdc16959e9dbc9618e0e935f0a1e51e226e651fcd67242f1f462mit a voutvon 0.

Ich mache eine Rohtransaktion, indem ich die Coins an mich selbst (an eine andere Adresse) sende:

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

Dann signiere ich die Rohtransaktion mit signrawtransaction:

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

Beachten Sie, dass der hier signierte Hash eed3.... Ich habe dem Code eine Druckzeile hinzugefügt, damit ich sehen kann, was uint256signiert wird. Insbesondere in script.cpp, wo die Signierung für signrawtransactionstattfindet, habe ich einen zusätzlichen booleschen Parameter hinzugefügt, um den Hash und die Zeilen darunter zu protokollieren.

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

decoderawtransactionAls nächstes nehme ich das erste Element auf dem asmStapel, das die Signatur ist.

30 44 02 20 
258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e
02 20 
406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd
01

Hier können wir mit etwas Hilfe von Pieter Wuille ( Warum die Signatur immer 65 (1+32+32) Bytes lang ist? ) die R- und die S-Elemente sehen.

Als nächstes füge ich sie in meine Pycoin-Testdatei ein. Pycoin ist eine einfach zu verwendende Python-Bibliothek für Bitcoin (( https://github.com/richardkiss/pycoin )).

aus pycoin.ecdsa importieren *

Zeichenkette importieren

def verify_sig(Sig, Präfix, xpub, signed_val):
    is_even = (Präfix % 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("ist gültig: " + str(verify(generator_secp256k1, pub_pair, signed_val, sig)))

sigR = 0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e
sigS = 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd
sig = (sigR, sigS)
x = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf
Hash = 0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0
komprimiertes Zeichen = 0x03

verifiziere_sig(sig, komprimiertes_zeichen, x, hash)

Und das Ergebnis, das gedruckt wird:

sig: (0x258ad2725e66da0ae9825bb849a8e73f86e4767cf256c9fae44d18fbc1aacc2e, 0x406638a5b3369bb83d8a4f38a17b2f048c1495ebfd6fdff1a8c7b268e07f69cd)
pub: (0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf, 0x227874efbedb02b88568b55106634b9b4e3eca128ab46fa98abe7066aece0a51)
hex: 0xeed300fc63a973b668af66af746fde378ec6556f4b330ec1eeccc6a77aa535b0
gilt: Falsch

Ich bin mir wirklich nicht sicher, was hier schief laufen könnte, da dies nur eine direkte ECDSA-Verifizierung ist. Gibt es dort Werte, die seltsam erscheinen, oder gibt es andere Gründe, die dies nicht validieren würde?

Antworten (1)

Problem gefunden! Bitcoin ist sehr uneinheitlich in Bezug auf Big/Little-Endian-Ganzzahlen. Ich habe den Hash umgekehrt und die Signatur wurde verifiziert!

Abschlussprogramm:

aus pycoin.ecdsa importieren *

Zeichenkette importieren
Array importieren
binascii importieren

def verify_sig(Sig, Präfix, xpub, signed_val):
    is_even = (Präfix % 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("ist gültig: " + str(verify(generator_secp256k1, pub_pair, signed_val, sig)))

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

x = 0xec539eb3d882c07575f6143d891c18d580cd9d2726add0d71d262f2f6f2d4ccf
Zeichen = 0x03

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

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

Und das Ergebnis:

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