Warum ist meine Rohtransaktion nicht kanonisch, erhält Fehler -25 und wird abgelehnt?

Ich versuche, die TX-Generierungslogik in eine andere (und unpopuläre) Sprache zu portieren. Ich habe mir viel zu lange den Kopf dagegen geschlagen und hätte weggehen sollen, aber, naja, du weißt schon, Schließung und Sturheit.

Hier ist mein Code bisher.

Blockchain.info, Electrum und Bitcoin Core entschlüsseln alle meinen Versuch eines TX in etwas vernünftig aussehendes, wie zum Beispiel:

{
"txid" : "49c210ae472c5b5e39447e1f6d9bc020cd0f0075cc03d919afb0857a964e1f41",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "b097384c42a3be2730db3e3720a1806c76172b6b62b2b5ee007c2c6fd295cadf",
"vout" : 1,
"scriptSig" : {
"asm" :     "30460221009f478737296e39bbcff2ef7c6f013acc25bea75941acd8573bcea83ca910018b022100e9bff518297fec344d6e7e42cabc5ec793c4be507af68210bdde803a8a2958ed0104d8f39341451e2e66a00ef010c815f1284bc5e3a187476aab319b2d127b7e219c9b9e68bff311c63474242a9baab34f7ddec05de2c45bd140a74a64621ccb42cb",
"hex" :   "4930460221009f478737296e39bbcff2ef7c6f013acc25bea75941acd8573bcea83ca910018b022100e9bff518297fec344d6e7e42cabc5ec793c4be507af68210bdde803a8a2958ed014104d8f39341451e2e66a00ef010c815f1284bc5e3a187476aab319b2d127b7e219c9b9e68bff311c63474242a9baab34f7ddec05de2c45bd140a74a64621ccb42cb"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 0.00439999,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 7847eb9e366653aeb8857d541236fe4fd90c57e7 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9147847eb9e366653aeb8857d541236fe4fd90c57e788ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"1BxzF8rgXtuiPuSN8azdMJgryzQSWt4Uoj"
]
}
}
]
}

Beim Versuch, eine Transaktion in Bitcoin Core zu senden, erhalte ich "Fehler -25". Beim Lesen anderer StackExchange-Posts und Googeln werden die möglichen Ursachen wie folgt aufgeführt:

"Beim Lesen des Quellcodes wird dieser Fehler zurückgegeben, wenn AcceptToMemoryPool fehlschlägt, aber nicht, wenn es fehlschlägt, weil die Transaktion ungültig ist. Gibt debug.log irgendetwas aus, wenn dies passiert?"

"Sie erhalten diesen obskuren RPC-Fehler, wenn Ihr TX Ausgaben verwendet, von denen Bitcoin noch nie gehört hat." Nichts erscheint in ~/.bitcoin/debug.log.

Beim Versuch, es mit dem Slash-Pushtx-Dienst von blockchain.info zu senden (oh, komm schon, gibt es ernsthaft keine weiße Liste für Websites, auf die ich verlinken kann?), erhalte ich „Skript führte zu einem nicht wahren Stack: []“, was impliziert, dass ich irgendwo einen Fehler in meiner Signaturlogik gemacht habe.

Ich bin durch diese gegangen:

Und andere, einschließlich verschiedener Ruby-, Python-, Java- und JavaScript-Implementierungen, aber ich brauche mehr als 10 Reputation, um mehr als 2 Links zu posten. Ich kann beim besten Willen nicht herausfinden, wo ich abweiche.

Ich weiß, dass Teile davon veraltet sind. Der private Schlüssel, mit dem ich beginne (von Electrum), ist nicht als komprimiert gekennzeichnet (33 Bytes nach DecodeBase58Check, wobei das letzte 0x01 ist), daher sollte der öffentliche Schlüssel in der Transaktion, aus der ich zu ziehen versuche, auch nicht komprimiert sein.

Der TX, den ich ausgeben möchte ( b097384c42a3be2730db3e3720a1806c76172b6b62b2b5ee007c2c6fd295cadf , zweite Ausgabe, auch bekannt als 1) mit diesem TX hat ein Ausgabeskript, das Pay to Public Key Hash (d9495c762aed3dba15eec648beb58a8a4) gemäß blockchain info ist. Der vollständige scriptPubKey des Prevouts lautet:

OP_DUP OP_HASH160 d9495c762aed3dba15eec648beb55a8a43b8d1bd OP_EQUALVERIFY OP_CHECKSIG

Mein privater Schlüssel, der von WIF dekodiert wurde und durch ecdsa::pub_from_priv(), ecdsa::pub_encode() läuft, stimmt mit diesem Wert überein.

Es scheint, als wäre es der schwierige Teil, das richtige Zeug zu hashen, bevor der Hash in die Signatur eingetragen wird. Ich hänge 0x01 an die Signatur an, nachdem sie aus ecdsa::Sign() kommt. Bevor der TX signiert wird, wird ihm 01000000 angehängt. scriptSig (das Skript in der Eingabe für den TX, den ich baue) ist "OP_DUP, OP_HASH160, PUSHDATA, Bitcoin-Adresse (Public Key Hash), OP_EQUALVERIFY, OP_CHECKSIG", bevor es signiert wird, dann wird es zu einem varstr der sig und des pubKey , und das dekodiert in vernünftig aussehenden Bytecode:

scriptSig:

0: OP_PUSHDATA 0x304602210094c538663c149f40929bb787d6174104a694181d063943a745e558b17d09e276022100b1812105ea6d7a8206c9019303a6459a9a2b2524b364debe69405b5d8b90c6c301
74: OP_PUSHDATA 0x04d8f39341451e2e66a00ef010c815f1284bc5e3a187476aab319b2d127b7e219c9b9e68bff311c63474242a9baab34f7ddec05de2c45bd140a74a64621ccb42cb

scriptPubKey:

0: OP_DUP
1: OP_HASH160
2: OP_PUSHDATA 0x7847eb9e366653aeb8857d541236fe4fd90c57e7
23: OP_EQUALVERIFY
24: OP_CHECKSIG

Die Verwendung des alten scriptPubKey als scriptSig wird angenähert und geht von einer einfachen Bezahlung bis zur Signatur aus, anstatt tatsächlich den genauen scriptPubKey aus dem TX zu ziehen, auf das gezogen wird, aber in diesem einfachen Fall scheinen sie zu stimmen.

Hier ist der rohe, (falsch?) signierte TX:

0100000001dfca95d26f2c7c00eeb5b2626b2b17766c80a120373edb3027bea3424c3897b0010000008b48304502202647239b48610693967a24c7c976f0df903891113c56f50b6e3368c3f73eefb0022100e372ab8bf76ab35cba1ce6c1890fb6b27bb4d6d54f080180b46a6ecc2ae09248014104d8f39341451e2e66a00ef010c815f1284bc5e3a187476aab319b2d127b7e219c9b9e68bff311c63474242a9baab34f7ddec05de2c45bd140a74a64621ccb42cbffffffff01c0b60600000000001976a9147847eb9e366653aeb8857d541236fe4fd90c57e788ac00000000

Hier ist der Code:

https://gist.github.com/scrottie/15f2fca963d164306dcb

PrivateKey auf Anfrage (da ist $1 drin).

Wenn jemand in der Lage ist, das wegzuwerfen und herauszufinden, wo ich falsch gelaufen bin, wäre ich sehr dankbar.

(1) Signatur ist ungültig (2) Signatur sollte im "Low-S-Value-Format" sein (3) BENUTZEN SIE TESTNET FÜR SOLCHE EXPERIMENTE!!!
Entschuldigung für den Mangel an Testnet-Nutzung. Wenn ich das zum Laufen bekomme, werde ich versuchen, einen guten Walk-Thru dafür zu erstellen, aber vorhandene sind für das Mainnet. Ich habe den privaten Schlüssel/von Adresse/an Adresse/BTC-Betrag/prev TX-Hash von bitcoin.stackexchange.com/questions/3374/… eingesteckt und bekomme genau den gleichen Sig-Hash: 9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e Byte für Byte vergleichen, bis dahin identisch . Dann hat das zufällige K das Vorzeichen geändert. Färben Sie mich miffed.
Ich wünschte wirklich, ich wüsste, was das K in diesem Beispiel war, damit ich es vollständig reproduzieren könnte ...
Ich kann in Google nichts zu "Low-S-Value-Format" finden. Beziehen Sie sich einfach auf Endianness? Hast du dafür eine Referenz? Danke für deine Antwort.
Suche nach „ECDSA Signaturformbarkeit“

Antworten (2)

%&@!

Behoben mit dieser Änderung:

$privateKey = $privateKey->as_hex();  $privateKey =~ s{^0x}{} or die;
warn sprintf "doing: python sig.py  '%s' '%s'\n", $privateKey, to_hex($s256);
open my $python, '-|', 'python', 'sig.py', $privateKey, to_hex($s256) or die $!;
my $sig = readline $python;
warn "python says for sig: $sig\n";
$sig =~ s{[^0-9a-fA-F]}{}g;
$sig = from_hex($sig);

Und diese Datei als sig.py hinzufügen:

import ecdsa
import ecdsa.der
import ecdsa.util

import sys

privateKey = sys.argv[1]
s256 = sys.argv[2]

# print("privateKey len: ");  print(len(privateKey.decode('hex')))
# print(privateKey)

# print("s256 len: ");  print(len(s256.decode('hex')))
# print(s256)

sk = ecdsa.SigningKey.from_string(privateKey.decode('hex'), curve=ecdsa.SECP256k1)
sig = sk.sign_digest(s256.decode('hex'), sigencode=ecdsa.util.sigencode_der) + '\01' # 01 is hashtype
print sig.encode('hex')

ich bin neu hier und täusche mich anscheinend selbst. Ich konnte die Module auf CPAN, die mit openssl zusammenarbeiten, nicht zum Laufen bringen, also hatte ich eine Option für die EC-Bibliothek, etwas, das in reinem Perl geschrieben war. Das hat anscheinend Probleme, obwohl es Unit-Tests besteht. Oder ich habe es falsch verwendet.

Das führte zu einer Ausgabe, die https://blockchain.info/pushtx dazu brachte , „Transaction Submitted“ zu sagen. Und dann bewegten sich die Münzen.

Vielen Dank an alle, die angehalten haben, um dies in Augenschein zu nehmen. Ich schulde dir ein Bier. Ich werde versuchen, es mit sehr, sehr guten Kommentaren und Dokumenten voranzutreiben.

Entschuldigung, ich verwende keine Perl-Sprache und habe keine Perl-Umgebung installiert, um Ihren Code zu debuggen

Sehen Sie sich an, wie Sie einen Basis-Tx einlösen können.

Ich sehe Schritt 13 nicht, in dem Sie 01000000 = SIGHASH_ALL an die Daten anhängen müssen, die Sie in Ihrer makeRawTransaction-Methode signieren

Ich habe mir angesehen, wie man einen grundlegenden Tx einlöst, die Protokollspezifikation, die Beschreibung von OP_CHECKSIG auf bitcoin.it (begrenzte URLs für mich, bis ich mehr StackExchange-Punkte bekomme) und en.bitcoin.it/w/images/en/7/ 70/Bitcoin_OpCheckSig_InDetail.png . pack("V") oder pack("L<") in Perl ist wie pack("<L") in Python. Ansonsten folgt es ziemlich genau. SIGHASH_ALL befindet sich in dieser Zeile: my $myTxn_forSig = makeRawTransaction( $outputTransactionHash, $sourceIndex, $scriptPubKey, $outputs ) . "01000000"; Danke, dass Sie sich das angesehen haben.