Signieren einer Rohtransaktion mit Python ECDSA (oder OpenSSL)

Ich suche nach Einzelheiten zu Step15-17 von Redeeming a raw Tx Step By Step , was im Wesentlichen der Schritt ist, in dem die verkettete Raw-Tx-Struktur doppelt sha256-gehasht und dann mit einer ECDSA-Bibliothek signiert wird. Ich habe auch auf andere Rohtransaktionsleitfäden verwiesen ; Ich suche speziell, wie man eine rohe Hex-Tx-Struktur ohne GUI/Website/etc signiert

Ich habe ein Testnet- Tx mit Bitcoincore v0.10 (ein vollständiger Knoten) erstellt und dasselbe mit Python versucht und kann die rohe Tx-Struktur mit Python ecdsa nicht korrekt signieren .

Ich zeige zuerst die Bitcoincore-Befehle :

importprivkey 93FxXUeMJp93YQAtGeW5cE23gFN4sJbBr1RBmerLFVUDuqQqKL5
mu858WTEPiWWpAJRTMxC4ka6DJqiaCZiSB
createrawtransaction '[{"txid" : "72b764383b99fb3d112ac8b474a5d7c4242b75dbfee2d4e9cf9a6703d90f805a", "vout" : 1}]' '{"n1hjyVvYQPQtejJcANd5ZJM5rmxHCCgWL7" : 0.990}'
signrawtransaction 01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b7720000000000ffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac00000000

Final bitcoincore hex Tx (mit signrawtransaction):01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b772010000008a47304402204d78d2e6c0f801573e4960fb8e51ad939380d119d25f97d15efdedf815b05f02022066bd2ab0b401e32e7ce67ea45f8224097eeafbef2335d563776e5efe6632732d01410479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1ffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac00000000

Jetzt folgt der Python 2.7.9 -Code (FYI, mit iPython 2.0 , daher fehlt im folgenden Code ein Boilerplate):

# please excuse the hexlify/unhexlify, I work better in strings than bytes
import __future__
from pybitcointools import sha256
import hashlib, ecdsa
from ecdsa import SigningKey, SECP256k1
addr, pubkey = 'mu858WTEPiWWpAJRTMxC4ka6DJqiaCZiSB', '0479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1'
rawtx = '01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b7720100000000ffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac00000000' # TX structure with 00 as scriptSig, from bitcoincore createrawtransaction
# replacing the '00' scriptSig value
unsigned.append(rawtx[:82])  
unsigned.append('19'+'76a914953de657be4b305f606d9a9fbd35b070a682475788ac') # scriptSig = scriptPubKey input
unsigned.append(rawtx[84:]) 
unsigned.append('01000000')  # appending sighash_all
unsigned = ''.join(unsigned)

unsigned = '01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b772010000001976a914953de657be4b305f606d9a9fbd35b070a682475788acffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac0000000001000000'

# See https://github.com/warner/python-ecdsa/blob/master/README.md
sig1 = sk.sign(sha256(sha256(unsigned)), hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der) # note this references 2 different sha256 functions
sig2 = [hex(len(sig1 + '\x01'))[2:], hexlify(sig1), hexlify('\x01'), hex(len(unhexlify(pubkey)))[2:], pubkey]
sig2
>>> ['48',  '304502200e98d54ad642488121fa1fd4d055ff8f5b40773a21ecb42b5ef44ed9fd3b103c022100a2a56ffc3f27021a4be5d1d7a3016eff08fe7f550a4a894cca845ceab54dc53b', '01', '41', '0479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1']
sig2 = len(''.join(map(unhexlify, sig2)))
hex(len(''.join(map(unhexlify, sig2))))[2:] 
>>> '8b'  # push 139 bytes
sig3 
>>> 8b48304502200e98d54ad642488121fa1fd4d055ff8f5b40773a21ecb42b5ef44ed9fd3b103c022100a2a56ffc3f27021a4be5d1d7a3016eff08fe7f550a4a894cca845ceab54dc53b01410479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1 
signed = unsigned[:82] + sig3 + unsigned[84:]  # inserting the signature into the 00 the core software uses in place of ScriptSig

Endgültiger ( ungültiger ) Python-Tx:

01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b772010000008b48304502200e98d54ad642488121fa1fd4d055ff8f5b40773a21ecb42b5ef44ed9fd3b103c022100a2a56ffc3f27021a4be5d1d7a3016eff08fe7f550a4a894cca845ceab54dc53b01410479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1ffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac00000000

Das Senden des von Python generierten Tx funktioniert nicht. Wenn ich sendrawtransaction <PYTHON HEX>in Bitcoincore einen ungültigen Signaturfehler zurückgebe (insbesondere 16: Mandatory-Script-Verify-Flag-Failed (Skript ohne Fehler ausgewertet, aber mit einem falschen/leeren obersten Stack-Element beendet) (Code -26) ).

(TL;DR) FRAGE : Was mache ich falsch, wenn ich die rohe Transaktionsstruktur manuell signiere?

NB: Mir ist bewusst, dass pybitcointools und verwandte Bibliotheken Txs signieren können, aber ich suche nach Einzelheiten , da selbst diese beiden sehr gründlichen Bitcoin SE-Ressourcen ( LINK1 , LINK2 ) an den Details vorbeigehen. Ich würde eine Antwort für Python ECDSA oder PyCrypto bevorzugen , aber OpenSSL ist meiner Meinung nach die nächstbeste Antwort.

Ich habe dies in den letzten zwei Stunden oder so debuggt, konnte aber keine Fehler finden. Tut mir leid, ich kann keinen Fehler finden, und jedes Mal, wenn ich versuche, eine Transaktion einzureichen, wird der Fehlercode -25 angezeigt. Ich habe sogar überprüft, dass der zu signierende Hash derselbe ist, den der Daemon durch Druckzeilen in meinem kompilierten Daemon signiert. Sie können meine Debugging-Datei hier sehen , falls es Ihnen hilft.
Ich sehe jetzt, dass diese Transaktionsausgabe bereits ausgegeben wurde . Bedeutet dies, dass Sie Ihren Fehler erfolgreich gefunden haben? Wenn ja, solltest du es posten!
@StephenM347 Ja! Ich füge den Code hinzu. Das Problem war der Code für ECDSA
@ StephenM347 Siehe die Antwort unten, aber das Problem schien sich aus der Verwendung des öffentlichen Schlüssels beim Signieren und einer Ungleichheit zwischen String/Bytes/DER-Eingabe zu ergeben

Antworten (1)

OK, ich habe herausgefunden, wie man den rohen Tx mit Python ecdsa signiert. Ich gehe es durch:

Abrufen:

createrawtransaction '[{"txid" : "72b764383b99fb3d112ac8b474a5d7c4242b75dbfee2d4e9cf9a6703d90f805a", "vout" : 1}]' '{"n2kx7k6JuA5Wy27fawaeiPX7dq8mbRDPAv" : 0.99}'=01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b7720100000000ffffffff01c09ee605000000001976a914e900510876cb689f1db6fa982376c301362b740c88ac00000000

Und dann fügen Sie den scriptPubKey für scriptSig's hinzu 00:

unsigned = '01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b772010000001976a914953de657be4b305f606d9a9fbd35b070a682475788acffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac0000000001000000'

Hinweis unsignedhat das 01000000angehängt, weil das SIGNHASH ist 01(das in einer Minute auch an die DER-Signatur angehängt wird)

Also verdoppeln wir SHA256- Bytes und geben txhashals Bytes zurück :txhash = hashlib.sha256(hashlib.sha256(unsigned.decode('hex')).digest()).digest()

Wir brauchen auch den privkey für mu858WTEPiWWpAJRTMxC4ka6DJqiaCZiSB(in geheimer Exponentenform, dh hex): privkey = 'dc57c6d067376c36bbed632c9d00f03767867f337d5a86b5b0308a60004f08ee'.zfill(64)privkey = 'dc57c6d067376c36bbed632c9d00f03767867f337d5a86b5b0308a60004f08ee'

Jetzt signieren wir mit diesem korrigierten Code :

signingkey = ecdsa.SigningKey.from_string(privkey.decode('hex'), curve=ecdsa.SECP256k1)
SIG = signingkey.sign_digest(txhash, sigencode=ecdsa.util.sigencode_der) +'01'.decode('hex')

Dies ist Arbeitscode. Von hier an machen wir einfach die Unterschrift:

ScriptSig=<varint of total sig length> <SIG from code, including appended 01 SIGNHASH> <length of pubkey (0x21 or 0x41)> <pubkey>

wopubkey = '0479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1

Geben Sie uns ein gültiges signiertes Tx , ohne Bitcoincore zu verwenden :

01000000015a800fd903679acfe9d4e2fedb752b24c4d7a574b4c82a113dfb993b3864b772010000008a47304402204d78d2e6c0f801573e4960fb8e51ad939380d119d25f97d15efdedf815b05f02022066bd2ab0b401e32e7ce67ea45f8224097eeafbef2335d563776e5efe6632732d01410479b22a5127d176a49d506c86f791031f94a389227ef46a8ddb725a88c944c37e3f753de6ee0b441a0237801f140810e111a1fd8276a2a5d0ee07224a1b551cc1ffffffff01c09ee605000000001976a914dd6cce9f255a8cc17bda8ba0373df8e861cb866e88ac00000000

Vielen Dank!!! Es hat ein bisschen gedauert, aber deine Tipps haben sehr geholfen.
Dieser ecdsa-Signaturcode gibt häufig „Nicht-kanonische Signatur: Hoher S-Wert“ zurück, die jetzt nicht dem Standard entspricht. Irgendwelche Ideen, wie das gelöst werden kann?
@NathanParker SIGwird <r><s>miteinander verkettet. Wir müssen überprüfen, s < N/2wo Ndie Kurvenreihenfolge ist, 115792089237316195423570985008687907852837564279074904382605163141518161494337. Wenn s>N/2, danns = N-s
@NathanParker, um Python-ECDSA zu verwenden, ersetzen Sie sigencode=ecdsa.util.sigencode_derdurchsigencode=ecdsa.util.sigencode_der_canonize
Ich hätte nie gedacht, dass es so unglaublich einfach ist. Vielen Dank!
Gibt signingkey = ecdsa.SigningKey.from_string(privkey.decode('hex'), curve=ecdsa.SECP256k1)Ihnen einfach die r,s-Werte oder etwas anderes? Ich benutze das überhaupt nicht. Ich versuche nur, meinen eigenen Code zu debuggen, wo mein Roh-TX in Ordnung ist, und meine Methoden zur Berechnung von r,s-Werten bekannte Tests bestehen, aber ich kann anscheinend kein signiertes TX im Vergleich zu Bitcoin Core duplizieren, und dieses kleine bisschen könnte sein warum? Würdest du @WizardOfOzzie wissen?